diff --git a/models/doctype/Contact/Contact.js b/models/doctype/Contact/Contact.js index c9853f38..d15bc658 100644 --- a/models/doctype/Contact/Contact.js +++ b/models/doctype/Contact/Contact.js @@ -64,7 +64,6 @@ export default { return ['fullName']; }, getRowHTML(list, data) { - console.log(list, data); return `
${list.getNameHTML(data)}
`; } }, diff --git a/models/doctype/Tax/RegionalChanges.js b/models/doctype/Tax/RegionalChanges.js index 48476533..b9104274 100644 --- a/models/doctype/Tax/RegionalChanges.js +++ b/models/doctype/Tax/RegionalChanges.js @@ -1,41 +1,49 @@ +import frappe from 'frappejs'; + export default async function generateTaxes(country) { if (country === 'India') { const GSTs = { GST: [28, 18, 12, 6, 5, 3, 0.25, 0], IGST: [28, 18, 12, 6, 5, 3, 0.25, 0], 'Exempt-GST': [0], - 'Exempt-IGST': [0] + 'Exempt-IGST': [0], }; let newTax = await frappe.getNewDoc('Tax'); + for (const type of Object.keys(GSTs)) { for (const percent of GSTs[type]) { - if (type === 'GST') { - await newTax.set({ - name: `${type}-${percent}`, - details: [ - { - account: 'CGST', - rate: percent / 2 - }, - { - account: 'SGST', - rate: percent / 2 - } - ] - }); - } else { - await newTax.set({ - name: `${type}-${percent}`, - details: [ - { - account: type.toString().split('-')[0], - rate: percent - } - ] - }); - } + const name = `${type}-${percent}`; + + // Not cross checking cause hardcoded values. + await frappe.db.knex('Tax').where({ name }).del(); + await frappe.db.knex('TaxDetail').where({ parent: name }).del(); + + const details = getTaxDetails(type, percent); + await newTax.set({ name, details }); await newTax.insert(); } } } -}; +} + +function getTaxDetails(type, percent) { + if (type === 'GST') { + return [ + { + account: 'CGST', + rate: percent / 2, + }, + { + account: 'SGST', + rate: percent / 2, + }, + ]; + } else { + return [ + { + account: type.toString().split('-')[0], + rate: percent, + }, + ]; + } +} diff --git a/reports/AccountsReceivablePayable/AccountsReceivablePayable.js b/reports/AccountsReceivablePayable/AccountsReceivablePayable.js index ada10d0c..3626909b 100644 --- a/reports/AccountsReceivablePayable/AccountsReceivablePayable.js +++ b/reports/AccountsReceivablePayable/AccountsReceivablePayable.js @@ -31,8 +31,6 @@ async function getReceivablePayable({ reportType = 'Receivable', date }) { for (let entry of validEntries) { const { outStandingAmount, creditNoteAmount } = getOutstandingAmount(entry); - console.log(outStandingAmount); - if (outStandingAmount > 0.1 / 10) { const row = { date: entry.date, diff --git a/src/migrate.js b/src/migrate.js index 609ef74f..19c24a11 100644 --- a/src/migrate.js +++ b/src/migrate.js @@ -1,11 +1,28 @@ import frappe from 'frappejs'; -import migrate from 'frappejs/model/migrate'; +import runPatches from 'frappejs/model/runPatches'; import patchesTxt from '../patches/patches.txt'; const requirePatch = require.context('../patches', true, /\w+\.(js)$/); export default async function runMigrate() { - let patchOrder = patchesTxt.split('\n'); - let allPatches = {}; + if (await canRunPatches()) { + const patchOrder = patchesTxt.split('\n'); + const allPatches = getAllPatches(); + await runPatches(allPatches, patchOrder); + } + await frappe.db.migrate(); +} + +async function canRunPatches() { + return ( + (await frappe.db + .knex('sqlite_master') + .where({ type: 'table', name: 'PatchRun' }) + .select('name').length) > 0 + ); +} + +async function getAllPatches() { + const allPatches = {}; requirePatch.keys().forEach((fileName) => { if (fileName === './index.js') return; let method; @@ -20,6 +37,5 @@ export default async function runMigrate() { allPatches[fileName] = method; } }); - await migrate(allPatches, patchOrder); - await frappe.db.migrate(); -}; + return allPatches; +} diff --git a/src/pages/DatabaseSelector.vue b/src/pages/DatabaseSelector.vue index ac4b41e5..be70ad9b 100644 --- a/src/pages/DatabaseSelector.vue +++ b/src/pages/DatabaseSelector.vue @@ -139,7 +139,7 @@ export default { }; }, mounted() { - this.files = config.get('files', []).filter(({filepath}) => fs.existsSync(filepath)); + this.files = config.get('files', []).filter(({filePath}) => fs.existsSync(filePath)); this.showFiles = this.files.length > 0; }, methods: { @@ -158,6 +158,10 @@ export default { await this.connectToDatabase(file.filePath); }, async connectToDatabase(filePath) { + if (!filePath) { + return; + } + this.loadingDatabase = true; const connectionSuccess = await connectToLocalDatabase(filePath); this.loadingDatabase = false; diff --git a/src/pages/SetupWizard/SetupWizard.vue b/src/pages/SetupWizard/SetupWizard.vue index 81960658..8936dbab 100644 --- a/src/pages/SetupWizard/SetupWizard.vue +++ b/src/pages/SetupWizard/SetupWizard.vue @@ -65,6 +65,10 @@ import FormControl from '@/components/Controls/FormControl'; import Button from '@/components/Button'; import setupCompany from './setupCompany'; import Popover from '@/components/Popover'; +import config from '@/config'; +import path from 'path'; +import fs from 'fs'; +import { connectToLocalDatabase } from '@/utils'; import { getErrorMessage, @@ -121,24 +125,31 @@ export default { showMessageDialog({ message: this._('Please fill all values') }); return; } + try { this.loading = true; await setupCompany(this.doc); this.$emit('setup-complete'); } catch (e) { this.loading = false; - console.log(e, this.doc); if (e.type === frappe.errors.DuplicateEntryError) { - // TODO: Add option to overwrite file from here. - const message = this._( - 'Records already exist in the db. Please create a new database file and try again.' - ); - showMessageDialog({ message }); + await this.renameDbFileAndRerunSetup(); } else { handleErrorWithDialog(e, this.doc); } } }, + async renameDbFileAndRerunSetup() { + const filePath = config.get('lastSelectedFilePath'); + renameDbFile(filePath); + frappe.removeFromCache('AccountingSettings', 'AccountingSettings'); + delete frappe.AccountingSettings; + const connectionSuccess = await connectToLocalDatabase(filePath); + if (connectionSuccess) { + await setupCompany(this.doc); + this.$emit('setup-complete'); + } + }, }, computed: { meta() { @@ -152,4 +163,14 @@ export default { }, }, }; + +function renameDbFile(filePath) { + const dirname = path.dirname(filePath); + const basename = path.basename(filePath); + const backupPath = path.join(dirname, `_${basename}`); + if (fs.existsSync(backupPath)) { + fs.unlinkSync(backupPath); + } + fs.renameSync(filePath, backupPath); +} diff --git a/src/pages/SetupWizard/setupCompany.js b/src/pages/SetupWizard/setupCompany.js index 7742733e..82dae91a 100644 --- a/src/pages/SetupWizard/setupCompany.js +++ b/src/pages/SetupWizard/setupCompany.js @@ -12,7 +12,7 @@ export default async function setupCompany(setupWizardValues) { email, bankName, fiscalYearStart, - fiscalYearEnd + fiscalYearEnd, } = setupWizardValues; const accountingSettings = frappe.AccountingSettings; @@ -24,7 +24,7 @@ export default async function setupCompany(setupWizardValues) { bankName, fiscalYearStart, fiscalYearEnd, - currency: countryList[country]['currency'] + currency: countryList[country]['currency'], }); const printSettings = await frappe.getSingle('PrintSettings'); @@ -32,7 +32,7 @@ export default async function setupCompany(setupWizardValues) { logo: companyLogo, companyName, email, - displayLogo: companyLogo ? 1 : 0 + displayLogo: companyLogo ? 1 : 0, }); await setupGlobalCurrencies(countryList); @@ -55,24 +55,28 @@ async function setupGlobalCurrencies(countries) { currency_fraction_units: fractionUnits, smallest_currency_fraction_value: smallestValue, currency_symbol: symbol, - number_format: numberFormat + number_format: numberFormat, } = country; - if (currency) { - const exists = queue.includes(currency); - if (!exists) { - const doc = await frappe.newDoc({ - doctype: 'Currency', - name: currency, - fraction, - fractionUnits, - smallestValue, - symbol, - numberFormat: numberFormat || '#,###.##' - }); - promises.push(doc.insert()); - queue.push(currency); - } + if (!currency || queue.includes(currency)) { + continue; + } + + const docObject = { + doctype: 'Currency', + name: currency, + fraction, + fractionUnits, + smallestValue, + symbol, + numberFormat: numberFormat || '#,###.##', + }; + + const canCreate = await checkIfExactRecordAbsent(docObject); + if (canCreate) { + const doc = await frappe.newDoc(docObject); + promises.push(doc.insert()); + queue.push(currency); } } return Promise.all(promises); @@ -80,26 +84,30 @@ async function setupGlobalCurrencies(countries) { async function setupChartOfAccounts(bankName) { await frappe.call({ - method: 'import-coa' + method: 'import-coa', }); - const accountDoc = await frappe.newDoc({ - doctype: 'Account' - }); - Object.assign(accountDoc, { + const docObject = { + doctype: 'Account', name: bankName, rootType: 'Asset', parentAccount: 'Bank Accounts', accountType: 'Bank', - isGroup: 0 - }); - accountDoc.insert(); + isGroup: 0, + }; + + if (await checkIfExactRecordAbsent(docObject)) { + const accountDoc = await frappe.newDoc(docObject); + accountDoc.insert(); + } } async function setupRegionalChanges(country) { await generateTaxes(country); if (country === 'India') { - frappe.models.Party = await import('../../../models/doctype/Party/RegionalChanges'); + frappe.models.Party = await import( + '../../../models/doctype/Party/RegionalChanges' + ); await frappe.db.migrate(); } } @@ -107,10 +115,35 @@ async function setupRegionalChanges(country) { function updateCompanyNameInConfig() { let filePath = frappe.db.dbPath; let files = config.get('files', []); - files.forEach(file => { + files.forEach((file) => { if (file.filePath === filePath) { file.companyName = frappe.AccountingSettings.companyName; } }); config.set('files', files); } + +export async function checkIfExactRecordAbsent(docObject) { + const { doctype, name } = docObject; + const newDocObject = Object.assign({}, docObject); + delete newDocObject.doctype; + const rows = await frappe.db.knex(doctype).where({ name }); + + if (rows.length === 0) { + return true; + } + + const storedDocObject = rows[0]; + const matchList = Object.keys(newDocObject).map((key) => { + const newValue = newDocObject[key]; + const storedValue = storedDocObject[key]; + return newValue == storedValue; // Should not be type sensitive. + }); + + if (!matchList.every(Boolean)) { + await frappe.db.knex(doctype).where({ name }).del(); + return true; + } + + return false; +} diff --git a/src/utils.js b/src/utils.js index 156a335c..9fcae637 100644 --- a/src/utils.js +++ b/src/utils.js @@ -16,38 +16,24 @@ export async function createNewDatabase() { defaultPath: 'frappe-books.db', }; - let { filePath } = await ipcRenderer.invoke( + let { canceled, filePath } = await ipcRenderer.invoke( IPC_ACTIONS.GET_SAVE_FILEPATH, options ); - if (filePath) { - if (!filePath.endsWith('.db')) { - filePath = filePath + '.db'; - } - if (fs.existsSync(filePath)) { - showMessageDialog({ - // prettier-ignore - message: _('A file exists with the same name and it will be overwritten. Are you sure you want to continue?'), - buttons: [ - { - label: _('Overwrite'), - action() { - fs.unlinkSync(filePath); - return filePath; - }, - }, - { - label: _('Cancel'), - action() { - return ''; - }, - }, - ], - }); - } else { - return filePath; - } + + if (canceled || filePath.length === 0) { + return ''; } + + if (!filePath.endsWith('.db')) { + filePath = filePath + '.db'; + } + + if (fs.existsSync(filePath)) { + fs.unlinkSync(filePath); + } + + return filePath; } export async function loadExistingDatabase() { @@ -67,30 +53,31 @@ export async function loadExistingDatabase() { } } -export async function connectToLocalDatabase(filepath) { - if (!filepath) { +export async function connectToLocalDatabase(filePath) { + if (!filePath) { return false; } frappe.login('Administrator'); try { frappe.db = new SQLite({ - dbPath: filepath, + dbPath: filePath, }); await frappe.db.connect(); } catch (error) { return false; } + await migrate(); await postStart(); // set file info in config let files = config.get('files') || []; - if (!files.find((file) => file.filePath === filepath)) { + if (!files.find((file) => file.filePath === filePath)) { files = [ { companyName: frappe.AccountingSettings.companyName, - filePath: filepath, + filePath: filePath, }, ...files, ]; @@ -98,7 +85,7 @@ export async function connectToLocalDatabase(filepath) { } // set last selected file - config.set('lastSelectedFilePath', filepath); + config.set('lastSelectedFilePath', filePath); return true; } @@ -274,4 +261,4 @@ export async function runWindowAction(name) { break; } return name; -} +} \ No newline at end of file