2
0
mirror of https://github.com/frappe/books.git synced 2025-01-22 14:48:25 +00:00

incr: type renderer

- move setup logic outside components, type it
This commit is contained in:
18alantom 2022-04-20 16:11:27 +05:30
parent c56850d08f
commit 835deb4ce8
42 changed files with 844 additions and 856 deletions

View File

@ -1,78 +0,0 @@
import frappe from 'fyo';
import standardCOA from '../fixtures/verified/standardCOA.json';
import { getCOAList } from '../src/utils';
const accountFields = ['accountType', 'accountNumber', 'rootType', 'isGroup'];
function getAccountName(accountName, accountNumber) {
if (accountNumber) {
return `${accountName} - ${accountNumber}`;
}
return accountName;
}
async function importAccounts(children, parentAccount, rootType, rootAccount) {
for (let rootName in children) {
if (accountFields.includes(rootName)) {
continue;
}
const child = children[rootName];
if (rootAccount) {
rootType = child.rootType;
}
const { accountType, accountNumber } = child;
const accountName = getAccountName(rootName, accountNumber);
const isGroup = identifyIsGroup(child);
const doc = frappe.doc.getNewDoc('Account', {
name: accountName,
parentAccount,
isGroup,
rootType,
balance: 0,
accountType,
});
await doc.insert();
await importAccounts(child, accountName, rootType);
}
}
function identifyIsGroup(child) {
if (child.isGroup) {
return child.isGroup;
}
const keys = Object.keys(child);
const children = keys.filter((key) => !accountFields.includes(key));
if (children.length) {
return 1;
}
return 0;
}
export async function getCountryCOA(chartOfAccounts) {
const coaList = getCOAList();
const coa = coaList.find(({ name }) => name === chartOfAccounts);
const conCode = coa.countryCode;
if (!conCode) {
return standardCOA;
}
try {
const countryCoa = (
await import('../fixtures/verified/' + conCode + '.json')
).default;
return countryCoa.tree;
} catch (e) {
return standardCOA;
}
}
export default async function importCharts(chartOfAccounts) {
const chart = await getCountryCOA(chartOfAccounts);
await importAccounts(chart, '', '', true);
}

View File

@ -1,172 +0,0 @@
import { t } from 'fyo';
export default {
[t`Application of Funds (Assets)`]: {
[t`Current Assets`]: {
[t`Accounts Receivable`]: {
[t`Debtors`]: {
accountType: 'Receivable',
},
},
[t`Bank Accounts`]: {
accountType: 'Bank',
isGroup: 1,
},
[t`Cash In Hand`]: {
[t`Cash`]: {
accountType: 'Cash',
},
accountType: 'Cash',
},
[t`Loans and Advances (Assets)`]: {
isGroup: 1,
},
[t`Securities and Deposits`]: {
[t`Earnest Money`]: {},
},
[t`Stock Assets`]: {
[t`Stock In Hand`]: {
accountType: 'Stock',
},
accountType: 'Stock',
},
[t`Tax Assets`]: {
isGroup: 1,
},
},
[t`Fixed Assets`]: {
[t`Capital Equipments`]: {
accountType: 'Fixed Asset',
},
[t`Electronic Equipments`]: {
accountType: 'Fixed Asset',
},
[t`Furnitures and Fixtures`]: {
accountType: 'Fixed Asset',
},
[t`Office Equipments`]: {
accountType: 'Fixed Asset',
},
[t`Plants and Machineries`]: {
accountType: 'Fixed Asset',
},
[t`Buildings`]: {
accountType: 'Fixed Asset',
},
[t`Softwares`]: {
accountType: 'Fixed Asset',
},
[t`Accumulated Depreciation`]: {
accountType: 'Accumulated Depreciation',
},
},
[t`Investments`]: {
isGroup: 1,
},
[t`Temporary Accounts`]: {
[t`Temporary Opening`]: {
accountType: 'Temporary',
},
},
rootType: 'Asset',
},
[t`Expenses`]: {
[t`Direct Expenses`]: {
[t`Stock Expenses`]: {
[t`Cost of Goods Sold`]: {
accountType: 'Cost of Goods Sold',
},
[t`Expenses Included In Valuation`]: {
accountType: 'Expenses Included In Valuation',
},
[t`Stock Adjustment`]: {
accountType: 'Stock Adjustment',
},
},
},
[t`Indirect Expenses`]: {
[t`Administrative Expenses`]: {},
[t`Commission on Sales`]: {},
[t`Depreciation`]: {
accountType: 'Depreciation',
},
[t`Entertainment Expenses`]: {},
[t`Freight and Forwarding Charges`]: {
accountType: 'Chargeable',
},
[t`Legal Expenses`]: {},
[t`Marketing Expenses`]: {
accountType: 'Chargeable',
},
[t`Miscellaneous Expenses`]: {
accountType: 'Chargeable',
},
[t`Office Maintenance Expenses`]: {},
[t`Office Rent`]: {},
[t`Postal Expenses`]: {},
[t`Print and Stationery`]: {},
[t`Round Off`]: {
accountType: 'Round Off',
},
[t`Salary`]: {},
[t`Sales Expenses`]: {},
[t`Telephone Expenses`]: {},
[t`Travel Expenses`]: {},
[t`Utility Expenses`]: {},
[t`Write Off`]: {},
[t`Exchange Gain/Loss`]: {},
[t`Gain/Loss on Asset Disposal`]: {},
},
rootType: 'Expense',
},
[t`Income`]: {
[t`Direct Income`]: {
[t`Sales`]: {},
[t`Service`]: {},
},
[t`Indirect Income`]: {
isGroup: 1,
},
rootType: 'Income',
},
[t`Source of Funds (Liabilities)`]: {
[t`Current Liabilities`]: {
[t`Accounts Payable`]: {
[t`Creditors`]: {
accountType: 'Payable',
},
[t`Payroll Payable`]: {},
},
[t`Stock Liabilities`]: {
[t`Stock Received But Not Billed`]: {
accountType: 'Stock Received But Not Billed',
},
},
[t`Duties and Taxes`]: {
accountType: 'Tax',
isGroup: 1,
},
[t`Loans (Liabilities)`]: {
[t`Secured Loans`]: {},
[t`Unsecured Loans`]: {},
[t`Bank Overdraft Account`]: {},
},
},
rootType: 'Liability',
},
[t`Equity`]: {
[t`Capital Stock`]: {
accountType: 'Equity',
},
[t`Dividends Paid`]: {
accountType: 'Equity',
},
[t`Opening Balance Equity`]: {
accountType: 'Equity',
},
[t`Retained Earnings`]: {
accountType: 'Equity',
},
rootType: 'Equity',
},
};

View File

@ -386,7 +386,7 @@
},
"Duties and Taxes": {
"accountType": "Tax",
"isGroup": 1
"isGroup": true
},
"Reservations & Credit Notes": {
"Credit Notes": {

View File

@ -11,7 +11,7 @@
},
"Banque": {
"accountType": "Bank",
"isGroup": 1
"isGroup": true
},
"Comptes \u00e0 recevoir": {
"Comptes clients": {
@ -20,7 +20,7 @@
"Provision pour cr\u00e9ances douteuses": {}
},
"Encaisse": {
"isGroup": 1
"isGroup": true
},
"Frais pay\u00e9s d\u2019avance": {
"Assurances pay\u00e9s d'avance": {},
@ -29,7 +29,7 @@
},
"Petite caisse": {
"accountType": "Cash",
"isGroup": 1
"isGroup": true
},
"Stocks": {
"Mati\u00e8res premi\u00e8res": {},
@ -175,7 +175,7 @@
"Loyer - b\u00e2timent": {},
"Loyer - entrep\u00f4t": {},
"Op\u00e9rations indirectes de la main-d\u2019\u0153uvre directe": {
"isGroup": 1
"isGroup": true
},
"R\u00e9parations et entretien - b\u00e2timent": {},
"R\u00e9parations et entretien - machinerie et \u00e9quipement": {},

View File

@ -9,12 +9,12 @@
"Animales": {
"accountNumber": "1.5.2.1",
"accountType": "Stock",
"isGroup": 1
"isGroup": true
},
"Plantas": {
"accountNumber": "1.5.2.2",
"accountType": "Stock",
"isGroup": 1
"isGroup": true
},
"accountNumber": "1.5.2",
"accountType": "Stock"
@ -22,7 +22,7 @@
"Activos Biol\u00f3gicos al Costo": {
"accountNumber": "1.5.1",
"accountType": "Stock",
"isGroup": 1
"isGroup": true
},
"accountNumber": "1.5",
"accountType": "Stock"
@ -32,7 +32,7 @@
"Cr\u00e9dito Fiscal (IVA Por Cobrar)": {
"accountNumber": "1.1.2.1",
"accountType": "Chargeable",
"isGroup": 1
"isGroup": true
},
"accountNumber": "1.1.2",
"accountType": "Chargeable"
@ -47,22 +47,22 @@
"Activos Adicionales y Otros": {
"accountNumber": "1.6.6",
"accountType": "Chargeable",
"isGroup": 1
"isGroup": true
},
"Cobrables Relacionados con Impuestos": {
"accountNumber": "1.6.2",
"accountType": "Chargeable",
"isGroup": 1
"isGroup": true
},
"Contratos de Construccion": {
"accountNumber": "1.6.4",
"accountType": "Chargeable",
"isGroup": 1
"isGroup": true
},
"Costos de Montaje": {
"accountNumber": "1.6.5",
"accountType": "Chargeable",
"isGroup": 1
"isGroup": true
},
"Pagos Anticipados y Otros Activos Circulantes": {
"Seguro Pagado Anticipadamente": {
@ -75,7 +75,7 @@
"Proveedores de Servicio": {
"accountNumber": "1.6.3",
"accountType": "Chargeable",
"isGroup": 1
"isGroup": true
},
"accountNumber": "1.6",
"accountType": "Chargeable"
@ -84,32 +84,32 @@
"Activos Financieros Clasificados por Designaci\u00f3n": {
"accountNumber": "1.4.6",
"accountType": "Chargeable",
"isGroup": 1
"isGroup": true
},
"Activos Financieros Derivados": {
"accountNumber": "1.4.3",
"accountType": "Chargeable",
"isGroup": 1
"isGroup": true
},
"Inversion o Participaci\u00f3n Accionaria en Empresas Afiliadas": {
"accountNumber": "1.4.1",
"accountType": "Chargeable",
"isGroup": 1
"isGroup": true
},
"Inversiones Burs\u00e1tiles e Instrumentos Financieros": {
"accountNumber": "1.4.2",
"accountType": "Chargeable",
"isGroup": 1
"isGroup": true
},
"Otros Activos Financieros": {
"accountNumber": "1.4.4",
"accountType": "Chargeable",
"isGroup": 1
"isGroup": true
},
"Provisi\u00f3n por Riesgo de Cr\u00e9dito (agregado) (Contra-activo)": {
"accountNumber": "1.4.5",
"accountType": "Round Off",
"isGroup": 1
"isGroup": true
},
"accountNumber": "1.4",
"accountType": "Chargeable"
@ -117,13 +117,13 @@
"Activos Intangibles": {
"accountNumber": "1.3",
"accountType": "Chargeable",
"isGroup": 1
"isGroup": true
},
"Caja y Equivalentes": {
"Caja": {
"accountNumber": "1.9.1",
"accountType": "Cash",
"isGroup": 1
"isGroup": true
},
"Equivalentes de Efectivo (Bancos)": {
"Bancos Internacionales": {
@ -146,7 +146,7 @@
"Banco Industrial": {
"accountNumber": "1.9.2.1.1",
"accountType": "Bank",
"isGroup": 1
"isGroup": true
},
"Banco Internacional": {
"accountNumber": "1.9.2.1.6",
@ -189,12 +189,12 @@
"Inversiones a Corto Plazo": {
"accountNumber": "1.9.3",
"accountType": "Bank",
"isGroup": 1
"isGroup": true
},
"Otros Equivalentes de Caja y Bancos": {
"accountNumber": "1.9.4",
"accountType": "Cash",
"isGroup": 1
"isGroup": true
},
"accountNumber": "1.9",
"accountType": "Bank"
@ -203,12 +203,12 @@
"Activos bajo Contrato": {
"accountNumber": "1.8.2",
"accountType": "Receivable",
"isGroup": 1
"isGroup": true
},
"Ajustes": {
"accountNumber": "1.8.4",
"accountType": "Chargeable",
"isGroup": 1
"isGroup": true
},
"Otras Cuentas por Cobrar": {
"Cuentas Por Cobrar Compa\u00f1\u00edas Afiliadas": {
@ -241,7 +241,7 @@
"Ventas al Cr\u00e9dito": {
"accountNumber": "1.8.1",
"accountType": "Receivable",
"isGroup": 1
"isGroup": true
},
"accountNumber": "1.8",
"accountType": "Receivable"
@ -253,38 +253,38 @@
"Art\u00edculos de Inventario Adicionales": {
"accountNumber": "1.7.8",
"accountType": "Stock",
"isGroup": 1
"isGroup": true
},
"Combustibles": {
"accountNumber": "1.7.5",
"accountType": "Stock",
"isGroup": 1
"isGroup": true
},
"Inventarios Pignorados Como Garant\u00eda de Pasivo": {
"accountNumber": "1.7.10",
"accountType": "Stock",
"isGroup": 1
"isGroup": true
},
"Inventarios a Valor Razonable Menos Costos de Venta": {
"accountNumber": "1.7.11",
"accountType": "Stock",
"isGroup": 1
"isGroup": true
},
"Materia Prima": {
"accountNumber": "1.7.1",
"accountType": "Stock",
"isGroup": 1
"isGroup": true
},
"Mercader\u00eda (Mercanc\u00edas)": {
"accountNumber": "1.7.2",
"accountType": "Stock",
"isGroup": 1
"isGroup": true
},
"Otros Inventarios": {
"Merma o Ajuste de Inventario": {
"accountNumber": "1.7.9.1",
"accountType": "Stock Adjustment",
"isGroup": 1
"isGroup": true
},
"accountNumber": "1.7.9",
"accountType": "Stock"
@ -292,13 +292,13 @@
"Producto Terminado": {
"accountNumber": "1.7.7",
"accountType": "Stock",
"isGroup": 1
"isGroup": true
},
"Repuestos": {
"Respuestos en Transito": {
"accountNumber": "1.7.4.0",
"accountType": "Stock",
"isGroup": 1
"isGroup": true
},
"accountNumber": "1.7.4",
"accountType": "Stock"
@ -306,12 +306,12 @@
"Suministros de Producci\u00f3n y Consumibles": {
"accountNumber": "1.7.3",
"accountType": "Stock",
"isGroup": 1
"isGroup": true
},
"Trabajo en Progeso": {
"accountNumber": "1.7.6",
"accountType": "Stock",
"isGroup": 1
"isGroup": true
},
"accountNumber": "1.7",
"accountType": "Stock"
@ -324,7 +324,7 @@
"Inversion Inmobiliaria Construida": {
"accountNumber": "1.2.2",
"accountType": "Chargeable",
"isGroup": 1
"isGroup": true
},
"accountNumber": "1.2",
"accountType": "Chargeable"

View File

@ -29,16 +29,16 @@
},
"123. \u00c9p\u00fcletek, \u00e9p\u00fcletr\u00e9szek, tulajdoni h\u00e1nyadok ": {
"accountType": "Fixed Asset",
"isGroup": 1
"isGroup": true
},
"124. Egy\u00e9b ingatlanok": {
"isGroup": 1
"isGroup": true
},
"125. \u00dczemk\u00f6r\u00f6n k\u00edv\u00fcli ingatlanok, \u00e9p\u00fcletek ": {
"isGroup": 1
"isGroup": true
},
"126. Ingatlanokhoz kapcsol\u00f3d\u00f3 vagyoni \u00e9rt\u00e9k\u0171 jogok": {
"isGroup": 1
"isGroup": true
},
"127. Ingatlanok \u00e9rt\u00e9khelyesb\u00edt\u00e9se": {},
"129. Kis \u00e9rt\u00e9k\u0171 ingatlanok": {}
@ -148,7 +148,7 @@
"239. Befejezetlen termel\u00e9s \u00e9s f\u00e9lk\u00e9sz term\u00e9kek \u00e9rt\u00e9kveszt\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": {}
},
"24. N\u00d6VEND\u00c9K-, H\u00cdZ\u00d3- \u00c9S EGY\u00c9B \u00c1LLATOK": {
"isGroup": 1
"isGroup": true
},
"25. K\u00c9SZTERM\u00c9KEK": {
"251-257. K\u00e9szterm\u00e9kek": {},
@ -158,23 +158,23 @@
"26-28. \u00c1RUK ": {
"261. Kereskedelmi \u00e1ruk": {
"accountType": "Stock",
"isGroup": 0
"isGroup": false
},
"262. Idegen helyen t\u00e1rolt, bizom\u00e1nyba \u00e1tadott \u00e1ruk": {
"accountType": "Stock",
"isGroup": 0
"isGroup": false
},
"263. T\u00e1rgyi eszk\u00f6z\u00f6k k\u00f6z\u00fcl \u00e1tsorolt \u00e1ruk": {
"accountType": "Stock",
"isGroup": 0
"isGroup": false
},
"264. Bels\u0151 (egys\u00e9gek, tev\u00e9kenys\u00e9gek k\u00f6z\u00f6tti) \u00e1tad\u00e1s-\u00e1tv\u00e9tel \u00fctk\u00f6z\u0151sz\u00e1mla": {
"accountType": "Stock",
"isGroup": 0
"isGroup": false
},
"269. Kereskedelmi \u00e1ruk \u00e9rt\u00e9kveszt\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": {
"accountType": "Stock",
"isGroup": 0
"isGroup": false
},
"accountType": "Stock"
},
@ -183,7 +183,7 @@
"279. K\u00f6zvet\u00edtett szolg\u00e1ltat\u00e1sok \u00e9rt\u00e9kveszt\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": {}
},
"28. BET\u00c9TD\u00cdJAS G\u00d6NGY\u00d6LEGEK": {
"isGroup": 1
"isGroup": true
},
"rootType": "Asset"
},
@ -205,13 +205,13 @@
"319. K\u00fclf\u00f6ldi k\u00f6vetel\u00e9sek \u00e9rt\u00e9kveszt\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": {}
},
"32. K\u00d6VETEL\u00c9SEK KAPCSOLT V\u00c1LLALKOZ\u00c1SSAL SZEMBEN": {
"isGroup": 1
"isGroup": true
},
"33. K\u00d6VETEL\u00c9SEK EGY\u00c9B R\u00c9SZESED\u00c9SI VISZONYBAN L\u00c9V\u00d5 V\u00c1LLALKOZ\u00c1SSAL SZEMBEN ": {
"isGroup": 1
"isGroup": true
},
"34. V\u00c1LT\u00d3K\u00d6VETEL\u00c9SEK": {
"isGroup": 1
"isGroup": true
},
"35. ADOTT EL\u00d5LEGEK": {
"351. Immateri\u00e1lis javakra adott el\u0151legek": {},
@ -227,17 +227,17 @@
"3613. Egy\u00e9b elsz\u00e1mol\u00e1sok a munkav\u00e1llal\u00f3kkal": {}
},
"362. K\u00f6lts\u00e9gvet\u00e9ssel szembeni k\u00f6vetel\u00e9sek": {
"isGroup": 1
"isGroup": true
},
"363. R\u00f6vid lej\u00e1ratra k\u00f6lcs\u00f6nadott p\u00e9nzeszk\u00f6z\u00f6k": {
"isGroup": 1
"isGroup": true
},
"364. R\u00e9szesed\u00e9sekkel, \u00e9rt\u00e9kpap\u00edrokkal kapcsolatos k\u00f6vetel\u00e9sek": {
"3641. R\u00f6vid lej\u00e1rat\u00fa k\u00f6lcs\u00f6n\u00f6k": {},
"3642. Tart\u00f3san adott k\u00f6lcs\u00f6n\u00f6kb\u0151l \u00e1tsorolt k\u00f6vetel\u00e9sek": {}
},
"365. V\u00e1s\u00e1rolt \u00e9s kapott k\u00f6vetel\u00e9sek ": {
"isGroup": 1
"isGroup": true
},
"366. R\u00e9szesed\u00e9sekkel, \u00e9rt\u00e9kpap\u00edrokkal kapcsolatos k\u00f6vetel\u00e9sek": {},
"367. Hat\u00e1rid\u0151s, opci\u00f3s \u00e9s swap \u00fcgyletekkel kapcsolatos k\u00f6vetel\u00e9sek": {},
@ -285,7 +285,7 @@
"383. Csekkek": {},
"384. Elsz\u00e1mol\u00e1si bet\u00e9tsz\u00e1mla ": {
"accountType": "Bank",
"isGroup": 1
"isGroup": true
},
"385. Elk\u00fcl\u00f6n\u00edtett bet\u00e9tsz\u00e1ml\u00e1k ": {
"3851. Kamatoz\u00f3 bet\u00e9tsz\u00e1ml\u00e1k": {},
@ -373,7 +373,7 @@
"4452. Egy\u00e9b hossz\u00fa lej\u00e1rat\u00fa hitelek deviz\u00e1ban": {}
},
"446. Tart\u00f3s k\u00f6telezetts\u00e9gek kapcsolt v\u00e1llalkoz\u00e1ssal szemben ": {
"isGroup": 1
"isGroup": true
},
"447. Tart\u00f3s k\u00f6telezetts\u00e9gek egy\u00e9b r\u00e9szesed\u00e9si viszonyban l\u00e9v\u0151 v\u00e1llalkoz\u00e1ssal szemben": {},
"448. P\u00e9nz\u00fcgyi l\u00edzing miatti k\u00f6telezetts\u00e9gek ": {},
@ -449,7 +449,7 @@
"463-9. C\u00e9gaut\u00f3ad\u00f3 ": {}
},
"464. G\u00e9pj\u00e1rm\u0171 ad\u00f3 (c\u00e9gaut\u00f3ad\u00f3) elsz\u00e1mol\u00e1sa": {
"isGroup": 1
"isGroup": true
},
"465. V\u00e1m- \u00e9s p\u00e9nz\u00fcgy\u0151rs\u00e9g elsz\u00e1mol\u00e1si sz\u00e1mla ": {
"4651. V\u00e1mk\u00f6lts\u00e9gek \u00e9s egy\u00e9b v\u00e1mterhek elsz\u00e1mol\u00e1si sz\u00e1mla": {},
@ -619,7 +619,7 @@
"589. Aktiv\u00e1lt saj\u00e1t teljes\u00edtm\u00e9nyek \u00e1tvezet\u00e9si sz\u00e1mla": {}
},
"59. K\u00d6LTS\u00c9GNEM \u00c1TVEZET\u00c9SI SZ\u00c1MLA": {
"isGroup": 1
"isGroup": true
},
"rootType": "Expense"
},
@ -647,7 +647,7 @@
"rootType": "Expense"
},
"7. SZ\u00c1MLAOSZT\u00c1LY TEV\u00c9KENYS\u00c9GEKK\u00d6LTS\u00c9GEI": {
"isGroup": 1,
"isGroup": true,
"rootType": "Expense"
},
"8. SZ\u00c1MLAOSZT\u00c1LY \u00c9RT\u00c9KES\u00cdT\u00c9S ELSZ\u00c1MOLT \u00d6NK\u00d6LTS\u00c9GE \u00c9S R\u00c1FORD\u00cdT\u00c1SOK": {
@ -812,7 +812,7 @@
"9684. R\u00e9szesed\u00e9sek \u00e9rt\u00e9kveszt\u00e9s\u00e9nek vissza\u00edr\u00e1sa": {}
},
"969. K\u00fcl\u00f6nf\u00e9le egy\u00e9b bev\u00e9telek": {
"isGroup": 1
"isGroup": true
}
},
"97. P\u00c9NZ\u00dcGYI M\u0170VELETEK BEV\u00c9TELEI": {

View File

@ -14,11 +14,11 @@
"Bank ": {
"Bank Other Currency": {
"accountNumber": "1122.000",
"isGroup": 1
"isGroup": true
},
"Bank Rupiah": {
"accountNumber": "1121.000",
"isGroup": 1
"isGroup": true
},
"accountNumber": "1120.000",
"accountType": "Bank"
@ -70,7 +70,7 @@
"Persediaan Barang": {
"accountNumber": "1141.000",
"accountType": "Stock",
"isGroup": 1
"isGroup": true
},
"Uang Muka Pembelian": {
"Uang Muka Pembelian": {
@ -122,7 +122,7 @@
"Investasi": {
"Deposito": {
"accountNumber": "1231.003",
"isGroup": 1
"isGroup": true
},
"Investai Saham": {
"Investasi Saham": {

View File

@ -6,13 +6,13 @@
"Current Assets": {
"Accounts Receivable": {
"Debtors": {
"isGroup": 0,
"isGroup": false,
"accountType": "Receivable"
}
},
"Bank Accounts": {
"accountType": "Bank",
"isGroup": 1
"isGroup": true
},
"Cash In Hand": {
"Cash": {
@ -21,7 +21,7 @@
"accountType": "Cash"
},
"Loans and Advances (Assets)": {
"isGroup": 1
"isGroup": true
},
"Securities and Deposits": {
"Earnest Money": {}
@ -33,7 +33,7 @@
"accountType": "Stock"
},
"Tax Assets": {
"isGroup": 1
"isGroup": true
}
},
"Fixed Assets": {
@ -60,7 +60,7 @@
}
},
"Investments": {
"isGroup": 1
"isGroup": true
},
"Temporary Accounts": {
"Temporary Opening": {
@ -126,7 +126,7 @@
},
"Indirect Income": {
"accountType": "Income Account",
"isGroup": 1
"isGroup": true
},
"rootType": "Income"
},

View File

@ -110,7 +110,7 @@
},
"INVENTARIOS": {
"accountType": "Stock",
"isGroup": 1
"isGroup": true
}
},
"ACTIVO LARGO PLAZO": {

View File

@ -57,7 +57,7 @@
},
"Otros Equivalentes a Efectivo": {
"accountType": "Cash",
"isGroup": 1
"isGroup": true
}
},
"Impuestos Acreditables": {
@ -91,41 +91,41 @@
},
"Todos los Almacenes": {
"accountType": "Stock",
"isGroup": 1
"isGroup": true
},
"accountType": "Stock"
},
"Otras Cuentas por Cobrar": {
"accountType": "Receivable",
"isGroup": 1
"isGroup": true
}
},
"Activo no Corriente": {
"Activo por Impuestos Diferidos": {
"isGroup": 1
"isGroup": true
},
"Activos Intangibles": {
"Amortizacion de Activos Intangibles": {
"isGroup": 1
"isGroup": true
},
"Concesiones": {
"isGroup": 1
"isGroup": true
},
"Derechos de Autor": {
"isGroup": 1
"isGroup": true
},
"Deterioro de Valor de Activos Intangibles": {},
"Gastos de investigacion": {
"isGroup": 1
"isGroup": true
},
"Licencias": {
"isGroup": 1
"isGroup": true
},
"Marcas Registradas": {
"isGroup": 1
"isGroup": true
},
"Patentes": {
"isGroup": 1
"isGroup": true
}
},
"Amortizables": {
@ -138,7 +138,7 @@
"accountType": "Expenses Included In Valuation"
},
"Mejoras en Bienes Arrendados": {
"isGroup": 1
"isGroup": true
}
},
"Bienes en Arrendamiento Financiero": {
@ -147,28 +147,28 @@
},
"Cuentas por Cobrar a Largo Plazo": {
"Creditos a Largo Plazo": {
"isGroup": 1
"isGroup": true
}
},
"Inversiones Permanentes": {
"Inversiones Permanentes 1": {
"accountType": "Fixed Asset",
"isGroup": 1
"isGroup": true
},
"Negocios Conjuntos": {
"accountType": "Fixed Asset",
"isGroup": 1
"isGroup": true
}
},
"Inversiones a Largo Plazo": {
"Depositos Bancarios a Plazo": {
"isGroup": 1
"isGroup": true
},
"Intereses percibidos por adelantado": {
"isGroup": 1
"isGroup": true
},
"Titulos y Acciones": {
"isGroup": 1
"isGroup": true
}
},
"Propiedad Planta y Equipo": {
@ -203,7 +203,7 @@
}
},
"Donaciones": {
"isGroup": 1
"isGroup": true
},
"Ganancias Acumuladas": {
"Reservas": {
@ -319,7 +319,7 @@
},
"Pasivo": {
"Obligaciones por Arrendamiento Financiero a Largo Plazo": {
"isGroup": 1
"isGroup": true
},
"Pasivo Corriente": {
"Anticipos de Clientes": {},
@ -345,11 +345,11 @@
},
"Gastos por Pagar": {
"Prestaciones Sociales": {
"isGroup": 1
"isGroup": true
},
"Salarios por Pagar": {},
"Servicios Basicos 1": {
"isGroup": 1
"isGroup": true
}
},
"Impuestos por Pagar": {
@ -372,17 +372,17 @@
}
},
"Otras Cuentas por Pagar": {
"isGroup": 1
"isGroup": true
},
"Pasivos Financieros a Corto Plazo": {
"Otras Deudas Bancarias": {
"isGroup": 1
"isGroup": true
},
"Prestamos por Pagar a Corto Plazo": {
"isGroup": 1
"isGroup": true
},
"Sobregiros Bancarios": {
"isGroup": 1
"isGroup": true
}
},
"Provisiones por Pagar": {
@ -473,20 +473,20 @@
},
"Pasivo No Corriente": {
"Cuentas por Pagar a Largo Plaso": {
"isGroup": 1
"isGroup": true
},
"Otras Cuentas por Pagar a Largo Plazo": {
"isGroup": 1
"isGroup": true
},
"Otros Pasivos Financieros a Largo Plaso": {
"isGroup": 1
"isGroup": true
},
"Prestamos a Largo Plazo": {
"isGroup": 1
"isGroup": true
}
},
"Pasivo por Impuestos Diferidos": {
"isGroup": 1
"isGroup": true
},
"rootType": "Liability"
}

View File

@ -3,7 +3,7 @@
"name": "Netherlands - Grootboekschema",
"tree": {
"FABRIKAGEREKENINGEN": {
"isGroup": 1,
"isGroup": true,
"rootType": "Expense"
},
"FINANCIELE REKENINGEN, KORTLOPENDE VORDERINGEN EN SCHULDEN": {
@ -106,7 +106,7 @@
"rootType": "Asset"
},
"INDIRECTE KOSTEN": {
"isGroup": 1,
"isGroup": true,
"rootType": "Expense"
},
"KOSTENREKENINGEN": {

View File

@ -7,8 +7,8 @@
}
},
"Bank Accounts": {
"accountType": "Bank",
"isGroup": 1
"accountType": "Bank",
"isGroup": true
},
"Cash In Hand": {
"Cash": {
@ -17,7 +17,7 @@
"accountType": "Cash"
},
"Loans and Advances (Assets)": {
"isGroup": 1
"isGroup": true
},
"Securities and Deposits": {
"Earnest Money": {}
@ -29,7 +29,7 @@
"accountType": "Stock"
},
"Tax Assets": {
"isGroup": 1
"isGroup": true
}
},
"Fixed Assets": {
@ -59,7 +59,7 @@
}
},
"Investments": {
"isGroup": 1
"isGroup": true
},
"Temporary Accounts": {
"Temporary Opening": {
@ -123,7 +123,7 @@
"Service": {}
},
"Indirect Income": {
"isGroup": 1
"isGroup": true
},
"rootType": "Income"
},
@ -141,8 +141,8 @@
}
},
"Duties and Taxes": {
"accountType": "Tax",
"isGroup": 1
"accountType": "Tax",
"isGroup": true
},
"Loans (Liabilities)": {
"Secured Loans": {},

View File

@ -21,6 +21,7 @@ export class DatabaseHandler extends DatabaseBase {
#fyo: Fyo;
converter: Converter;
#demux: DatabaseDemuxBase;
dbPath?: string;
schemaMap: Readonly<SchemaMap> = {};
fieldValueMap: Record<string, Record<string, Field>> = {};
@ -39,12 +40,14 @@ export class DatabaseHandler extends DatabaseBase {
async createNewDatabase(dbPath: string, countryCode: string) {
countryCode = await this.#demux.createNewDatabase(dbPath, countryCode);
await this.init();
this.dbPath = dbPath;
return countryCode;
}
async connectToDatabase(dbPath: string, countryCode?: string) {
countryCode = await this.#demux.connectToDatabase(dbPath, countryCode);
await this.init();
this.dbPath = dbPath;
return countryCode;
}

View File

@ -34,7 +34,7 @@ export enum ConfigKeys {
export interface ConfigFile {
id: string;
companyName: string;
filePath: string;
dbPath: string;
}
export interface FyoConfig {

View File

@ -676,6 +676,14 @@ export default class Doc extends Observable<DocValue | Doc[]> {
return this.fyo.doc.getCachedValue(schemaName, name, fieldname);
}
async setAndUpdate(
fieldname: string | DocValueMap,
value?: DocValue | Doc[]
) {
await this.set(fieldname, value);
return await this.update();
}
async duplicate(shouldInsert: boolean = true): Promise<Doc> {
const updateMap: DocValueMap = {};
const docValueMap = this.getValidDict();

View File

@ -79,7 +79,7 @@ function addNewFile(
): UniqueId {
const newFile: ConfigFile = {
companyName,
filePath: fyo.config.get(ConfigKeys.LastSelectedFilePath, '') as string,
dbPath: fyo.config.get(ConfigKeys.LastSelectedFilePath, '') as string,
id: getId(),
};

View File

@ -25,3 +25,19 @@ export type AccountRootType =
| 'Equity'
| 'Income'
| 'Expense';
export interface COARootAccount {
rootType: AccountRootType;
[key: string]: COAChildAccount | AccountRootType;
}
export interface COAChildAccount {
accountType?: AccountType;
accountNumber?: string;
isGroup?: boolean;
[key: string]: COAChildAccount | boolean | AccountType | string | undefined;
}
export interface COATree {
[key: string]: COARootAccount;
}

View File

@ -1,109 +0,0 @@
import frappe from 'fyo';
import telemetry from '../src/telemetry/telemetry';
import { Verb } from '../src/telemetry/types';
import { getSavePath, saveData, showExportInFolder } from '../src/utils';
function templateToInnerText(innerHTML) {
const temp = document.createElement('template');
temp.innerHTML = innerHTML.trim();
return temp.content.firstChild.innerText;
}
function deObjectify(value) {
if (typeof value !== 'object') return value;
if (value === null) return '';
const innerHTML = value.template;
if (!innerHTML) return '';
return templateToInnerText(innerHTML);
}
function csvFormat(value) {
if (typeof value === 'string') {
return `"${value}"`;
} else if (value === null) {
return '';
} else if (typeof value === 'object') {
const innerHTML = value.template;
if (!innerHTML) return '';
return csvFormat(deObjectify(value));
}
return value;
}
export async function exportCsv(rows, columns, filePath) {
const fieldnames = columns.map(({ fieldname }) => fieldname);
const labels = columns.map(({ label }) => csvFormat(label));
const csvRows = [
labels.join(','),
...rows.map((row) => fieldnames.map((f) => csvFormat(row[f])).join(',')),
];
saveExportData(csvRows.join('\n'), filePath);
}
async function exportJson(rows, columns, filePath, filters, reportName) {
const exportObject = {};
const fieldnames = columns.map(({ fieldname }) => fieldname);
exportObject.columns = columns.map(({ fieldname, label }) => ({
fieldname,
label,
}));
exportObject.rows = rows.map((row) =>
fieldnames.reduce((acc, f) => {
acc[f] = deObjectify(row[f]);
return acc;
}, {})
);
exportObject.filters = Object.keys(filters)
.filter((name) => filters[name] !== null && filters[name] !== undefined)
.reduce((acc, name) => {
acc[name] = filters[name];
return acc;
}, {});
exportObject.timestamp = new Date().toISOString();
exportObject.reportName = reportName;
exportObject.softwareName = 'Frappe Books';
exportObject.softwareVersion = frappe.store.appVersion;
await saveExportData(JSON.stringify(exportObject), filePath);
}
async function exportReport(extention, reportName, getReportData) {
const { rows, columns, filters } = getReportData();
const { filePath, canceled } = await getSavePath(reportName, extention);
if (canceled || !filePath) return;
switch (extention) {
case 'csv':
await exportCsv(rows, columns, filePath);
break;
case 'json':
await exportJson(rows, columns, filePath, filters, reportName);
break;
default:
return;
}
telemetry.log(Verb.Exported, reportName, { extention });
}
export default function getCommonExportActions(reportName) {
return ['csv', 'json'].map((ext) => ({
group: frappe.t`Export`,
label: ext.toUpperCase(),
type: 'primary',
action: async (getReportData) =>
await exportReport(ext, reportName, getReportData),
}));
}
export async function saveExportData(data, filePath) {
await saveData(data, filePath);
showExportInFolder(frappe.t`Export Successful`, filePath);
}

View File

@ -1,3 +1,15 @@
import { Fyo } from 'fyo';
import models, { getRegionalModels } from 'models';
export const fyo = new Fyo({ isTest: false, isElectron: true });
export async function initializeModels(dbPath: string, countryCode?: string) {
if (countryCode) {
countryCode = await fyo.db.createNewDatabase(dbPath, countryCode);
} else {
countryCode = await fyo.db.connectToDatabase(dbPath);
}
const regionalModels = await getRegionalModels(countryCode);
await fyo.initializeAndRegister(models, regionalModels);
}

View File

@ -1,9 +1,3 @@
import PageHeader from 'src/components/PageHeader';
import SearchBar from 'src/components/SearchBar';
import { openQuickEdit } from 'src/utils';
import frappe from 'frappe';
import { nextTick } from 'vue';
import { handleErrorWithDialog } from '../errorHandling';
<template>
<div class="flex flex-col overflow-y-hidden">
<PageHeader>

View File

@ -101,9 +101,8 @@
</template>
<script>
import frappe from 'frappe';
import { getYMax } from 'src/components/Charts/chartUtils';
import LineChart from 'src/components/Charts/LineChart.vue';
import { formatXLabels } from 'src/utils';
import { formatXLabels, getYMax } from 'src/utils/chart';
import Cashflow from '../../../reports/Cashflow/Cashflow';
import { getDatesAndPeriodicity } from './getDatesAndPeriodicity';
import PeriodSelector from './PeriodSelector';

View File

@ -30,8 +30,7 @@
<script>
import frappe from 'frappe';
import BarChart from 'src/components/Charts/BarChart.vue';
import { getYMax, getYMin } from 'src/components/Charts/chartUtils';
import { formatXLabels } from 'src/utils';
import { formatXLabels, getYMax, getYMin } from 'src/utils/chart';
import ProfitAndLoss from '../../../reports/ProfitAndLoss/ProfitAndLoss';
import { getDatesAndPeriodicity } from './getDatesAndPeriodicity';
import PeriodSelector from './PeriodSelector';

View File

@ -1,25 +0,0 @@
import Account from '../../../models/doctype/Account/AccountList';
import AccountingLedgerEntry from '../../../models/doctype/AccountingLedgerEntry/AccountingLedgerEntryList';
import Item from '../../../models/doctype/Item/ItemList';
import JournalEntry from '../../../models/doctype/JournalEntry/JournalEntryList';
import Customer from '../../../models/doctype/Party/CustomerList';
import Party from '../../../models/doctype/Party/PartyList';
import Supplier from '../../../models/doctype/Party/SupplierList';
import Payment from '../../../models/doctype/Payment/PaymentList';
import PurchaseInvoice from '../../../models/doctype/PurchaseInvoice/PurchaseInvoiceList';
import SalesInvoice from '../../../models/doctype/SalesInvoice/SalesInvoiceList';
import Tax from '../../../models/doctype/Tax/TaxList';
export default {
SalesInvoice,
PurchaseInvoice,
Customer,
Supplier,
Party,
Item,
Payment,
Tax,
JournalEntry,
Account,
AccountingLedgerEntry,
};

View File

@ -1,192 +0,0 @@
import countryList from 'fixtures/countryInfo.json';
import frappe from 'fyo';
import { DEFAULT_LOCALE } from 'fyo/utils/consts';
import config from 'src/config';
import importCharts from '../../../accounting/importCOA';
import generateTaxes from '../../../models/doctype/Tax/RegionalEntries';
import regionalModelUpdates from '../../../models/regionalModelUpdates';
import { getId } from '../../telemetry/helpers';
import { callInitializeMoneyMaker } from '../../utils';
export default async function setupCompany(setupWizardValues) {
const {
companyLogo,
companyName,
country,
name,
email,
bankName,
currency: companyCurrency,
fiscalYearStart,
fiscalYearEnd,
chartOfAccounts,
} = setupWizardValues;
const accountingSettings = frappe.AccountingSettings;
const currency = companyCurrency || countryList[country]['currency'];
const locale = countryList[country]['locale'] ?? DEFAULT_LOCALE;
await callInitializeMoneyMaker(currency);
const accountingSettingsUpdateMap = {
companyName,
country,
fullname: name,
email,
bankName,
fiscalYearStart,
fiscalYearEnd,
currency,
};
await accountingSettings.setMultiple(accountingSettingsUpdateMap);
await accountingSettings.update();
const printSettings = await frappe.getSingle('PrintSettings');
const printSettingsUpdateMap = {
logo: companyLogo,
companyName,
email,
displayLogo: companyLogo ? true : false,
};
await printSettings.setMultiple(printSettingsUpdateMap);
await printSettings.update();
await setupGlobalCurrencies(countryList);
await setupChartOfAccounts(bankName, country, chartOfAccounts);
await setupRegionalChanges(country);
updateInitializationConfig();
await accountingSettings.setMultiple({ setupComplete: true });
await accountingSettings.update();
frappe.AccountingSettings = accountingSettings;
const systemSettings = await frappe.getSingle('SystemSettings');
systemSettings.setMultiple({ locale });
systemSettings.update();
}
async function setupGlobalCurrencies(countries) {
const promises = [];
const queue = [];
for (let country of Object.values(countries)) {
const {
currency,
currency_fraction: fraction,
currency_fraction_units: fractionUnits,
smallest_currency_fraction_value: smallestValue,
currency_symbol: symbol,
} = country;
if (!currency || queue.includes(currency)) {
continue;
}
const docObject = {
doctype: 'Currency',
name: currency,
fraction,
fractionUnits,
smallestValue,
symbol,
};
const doc = checkAndCreateDoc(docObject);
if (doc) {
promises.push(doc);
queue.push(currency);
}
}
return Promise.all(promises);
}
async function setupChartOfAccounts(bankName, country, chartOfAccounts) {
await importCharts(chartOfAccounts);
const parentAccount = await getBankAccountParentName(country);
const docObject = {
doctype: 'Account',
name: bankName,
rootType: 'Asset',
parentAccount,
accountType: 'Bank',
isGroup: 0,
};
await checkAndCreateDoc(docObject);
}
async function setupRegionalChanges(country) {
await generateTaxes(country);
await regionalModelUpdates({ country });
await frappe.db.migrate();
}
function updateInitializationConfig(language) {
let filePath = frappe.db.dbPath;
let files = config.get('files', []);
files.forEach((file) => {
if (file.filePath === filePath) {
file.companyName = frappe.AccountingSettings.companyName;
file.id = getId();
}
});
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.getAllRaw(doctype, {
fields: ['*'],
filters: { 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.delete(doctype, name);
return true;
}
return false;
}
async function checkAndCreateDoc(docObject) {
const canCreate = await checkIfExactRecordAbsent(docObject);
if (!canCreate) {
return;
}
const doc = await frappe.doc.getNewDoc(docObject.doctype, docObject);
return doc.insert();
}
async function getBankAccountParentName(country) {
const parentBankAccount = await frappe.db.getAllRaw('Account', {
fields: ['*'],
filters: { isGroup: true, accountType: 'Bank' },
});
if (parentBankAccount.length === 0) {
// This should not happen if the fixtures are correct.
return 'Bank Accounts';
} else if (parentBankAccount.length > 1) {
switch (country) {
case 'Indonesia':
return 'Bank Rupiah - 1121.000';
default:
break;
}
}
return parentBankAccount[0].name;
}

View File

@ -1,50 +0,0 @@
import { fyo } from 'src/initFyo';
export type TaxType = 'GST' | 'IGST' | 'Exempt-GST' | 'Exempt-IGST';
export default async function generateTaxes(country: string) {
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],
};
const newTax = await fyo.doc.getEmptyDoc('Tax');
for (const type of Object.keys(GSTs)) {
for (const percent of GSTs[type as TaxType]) {
const name = `${type}-${percent}`;
// Not cross checking cause hardcoded values.
await fyo.db.delete('Tax', name);
const details = getTaxDetails(type as TaxType, percent);
await newTax.set({ name, details });
await newTax.insert();
}
}
}
}
function getTaxDetails(type: TaxType, percent: number) {
if (type === 'GST') {
return [
{
account: 'CGST',
rate: percent / 2,
},
{
account: 'SGST',
rate: percent / 2,
},
];
} else {
return [
{
account: type.toString().split('-')[0],
rate: percent,
},
];
}
}

52
src/regional/in/in.ts Normal file
View File

@ -0,0 +1,52 @@
import { fyo } from 'src/initFyo';
export type TaxType = 'GST' | 'IGST' | 'Exempt-GST' | 'Exempt-IGST';
export async function createIndianRecords() {
await generateTaxes();
}
async function generateTaxes() {
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],
};
const newTax = await fyo.doc.getEmptyDoc('Tax');
for (const type of Object.keys(GSTs)) {
for (const percent of GSTs[type as TaxType]) {
const name = `${type}-${percent}`;
// Not cross checking cause hardcoded values.
await fyo.db.delete('Tax', name);
const details = getTaxDetails(type as TaxType, percent);
await newTax.set({ name, details });
await newTax.insert();
}
}
}
function getTaxDetails(type: TaxType, percent: number) {
if (type === 'GST') {
return [
{
account: 'CGST',
rate: percent / 2,
},
{
account: 'SGST',
rate: percent / 2,
},
];
} else {
return [
{
account: type.toString().split('-')[0],
rate: percent,
},
];
}
}

9
src/regional/index.ts Normal file
View File

@ -0,0 +1,9 @@
import { createIndianRecords } from './in/in';
export async function createRegionalRecords(country: string) {
if (country === 'India') {
await createIndianRecords();
}
return;
}

View File

@ -1,55 +1,45 @@
import { ipcRenderer } from 'electron';
import { createApp } from 'vue';
import App from './App';
import FeatherIcon from './components/FeatherIcon';
import config, { ConfigKeys } from './config';
import { ConfigKeys } from 'fyo/core/types';
import { IPC_ACTIONS } from 'utils/messages';
import { App as VueApp, createApp } from 'vue';
import App from './App.vue';
import Badge from './components/Badge.vue';
import FeatherIcon from './components/FeatherIcon.vue';
import { getErrorHandled, handleError } from './errorHandling';
import { fyo } from './initFyo';
import { IPC_ACTIONS } from './messages';
import { incrementOpenCount } from './renderer/helpers';
import { incrementOpenCount, outsideClickDirective } from './renderer/helpers';
import registerIpcRendererListeners from './renderer/registerIpcRendererListeners';
import router from './router';
import { outsideClickDirective } from './ui';
import { setLanguageMap, stringifyCircular } from './utils';
import { stringifyCircular } from './utils';
import { setLanguageMap } from './utils/language';
(async () => {
const language = fyo.config.get(ConfigKeys.Language);
const language = fyo.config.get(ConfigKeys.Language) as string;
if (language) {
await setLanguageMap(language);
}
if (process.env.NODE_ENV === 'development') {
window.config = config;
}
fyo.isElectron = true;
const models = (await import('../models')).default;
await fyo.initializeAndRegister(models);
setOnWindow();
ipcRenderer.send = getErrorHandled(ipcRenderer.send);
ipcRenderer.invoke = getErrorHandled(ipcRenderer.invoke);
window.frappe = fyo;
window.onerror = (message, source, lineno, colno, error) => {
error = error ?? new Error('triggered in window.onerror');
handleError(true, error, { message, source, lineno, colno });
};
registerIpcRendererListeners();
const app = createApp({
template: '<App/>',
template: '<h1>Hellow, World</h1>',
});
setErrorHandlers(app);
app.use(router);
app.component('App', App);
app.component('feather-icon', FeatherIcon);
app.component('Badge', Badge);
app.directive('on-outside-click', outsideClickDirective);
app.mixin({
computed: {
frappe() {
fyo() {
return fyo;
},
platform() {
@ -71,8 +61,20 @@ import { setLanguageMap, stringifyCircular } from './utils';
},
});
fyo.store.appVersion = await ipcRenderer.invoke(IPC_ACTIONS.GET_VERSION);
incrementOpenCount();
app.mount('body');
})();
function setErrorHandlers(app: VueApp) {
window.onerror = (message, source, lineno, colno, error) => {
error = error ?? new Error('triggered in window.onerror');
handleError(true, error, { message, source, lineno, colno });
};
app.config.errorHandler = (err, vm, info) => {
const more = {
const more: Record<string, unknown> = {
info,
};
@ -84,19 +86,26 @@ import { setLanguageMap, stringifyCircular } from './utils';
more.props = stringifyCircular(vm.$props ?? {}, true, true);
}
handleError(false, err, more);
handleError(false, err as Error, more);
console.error(err, vm, info);
};
fyo.store.appVersion = await ipcRenderer.invoke(IPC_ACTIONS.GET_VERSION);
incrementOpenCount();
app.mount('body');
process.on('unhandledRejection', (error) => {
handleError(true, error);
handleError(true, error as Error);
});
process.on('uncaughtException', (error) => {
handleError(true, error, {}, () => process.exit(1));
});
})();
}
function setOnWindow() {
if (process.env.NODE_ENV === 'development') {
// @ts-ignore
window.config = config;
// @ts-ignore
window.router = router;
// @ts-ignore
window.fyo = fyo;
}
}

View File

@ -1,5 +1,40 @@
import { ConfigKeys } from 'fyo/core/types';
import { fyo } from 'src/initFyo';
import { Directive } from 'vue';
const instances: OutsideClickCallback[] = [];
type OutsideClickCallback = (e: Event) => void;
export const outsideClickDirective: Directive<
HTMLElement,
OutsideClickCallback
> = {
beforeMount(el, binding) {
el.dataset.outsideClickIndex = String(instances.length);
const fn = binding.value;
const click = function (e: Event) {
onDocumentClick(e, el, fn);
};
document.addEventListener('click', click);
instances.push(click);
},
unmounted(el) {
const index = parseInt(el.dataset.outsideClickIndex ?? '0');
const handler = instances[index];
document.addEventListener('click', handler);
instances.splice(index, 1);
},
};
function onDocumentClick(e: Event, el: HTMLElement, fn: OutsideClickCallback) {
const target = e.target;
if (el !== target && !el.contains(target as Node)) {
fn(e);
}
}
export function incrementOpenCount() {
let openCount = fyo.config.get(ConfigKeys.OpenCount);

View File

@ -10,10 +10,10 @@ import PrintView from 'src/pages/PrintView/PrintView.vue';
import QuickEditForm from 'src/pages/QuickEditForm.vue';
import Report from 'src/pages/Report.vue';
import Settings from 'src/pages/Settings/Settings.vue';
import { createRouter, createWebHistory } from 'vue-router';
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
import { fyo } from './initFyo';
const routes = [
const routes: RouteRecordRaw[] = [
{
path: '/',
component: Dashboard,
@ -62,9 +62,12 @@ const routes = [
},
props: {
default: (route) => {
let { doctype, filters, fieldname, value } = route.params;
const { doctype, fieldname, value } = route.params;
let { filters } = route.params;
if (filters === undefined && fieldname && value) {
filters = { [fieldname]: value };
// @ts-ignore
filters = { [fieldname as string]: value };
}
return {
@ -114,7 +117,7 @@ const routes = [
const router = createRouter({ routes, history: createWebHistory() });
function removeDetails(path) {
function removeDetails(path: string) {
if (!path) {
return path;
}
@ -124,7 +127,7 @@ function removeDetails(path) {
return path;
}
return path.slice(0, match.index + 4);
return path.slice(0, match.index! + 4);
}
router.afterEach((to, from) => {
@ -136,8 +139,4 @@ router.afterEach((to, from) => {
fyo.telemetry.log(Verb.Navigated, NounEnum.Route, more);
});
if (process.env.NODE_ENV === 'development') {
window.router = router;
}
export default router;

98
src/setup/createCOA.ts Normal file
View File

@ -0,0 +1,98 @@
import {
AccountRootType,
COAChildAccount,
COARootAccount,
COATree,
} from 'models/baseModels/Account/types';
import { getCOAList } from 'models/baseModels/SetupWizard/SetupWizard';
import { fyo } from 'src/initFyo';
import { getStandardCOA } from './standardCOA';
const accountFields = ['accountType', 'accountNumber', 'rootType', 'isGroup'];
function getAccountName(accountName: string, accountNumber?: string) {
if (accountNumber) {
return `${accountName} - ${accountNumber}`;
}
return accountName;
}
async function createCOAAccounts(
children: COATree | COARootAccount | COAChildAccount,
parentAccount: string,
rootType: AccountRootType | '',
rootAccount: boolean
) {
for (const rootName in children) {
if (accountFields.includes(rootName)) {
continue;
}
const child = children[rootName];
if (rootAccount) {
rootType = (child as COARootAccount).rootType as AccountRootType;
}
const accountType = (child as COAChildAccount).accountType ?? '';
const accountNumber = (child as COAChildAccount).accountNumber;
const accountName = getAccountName(rootName, accountNumber);
const isGroup = identifyIsGroup(child as COAChildAccount | COARootAccount);
const doc = fyo.doc.getNewDoc('Account', {
name: accountName,
parentAccount,
isGroup,
rootType,
balance: 0,
accountType,
});
await doc.insert();
await createCOAAccounts(
child as COAChildAccount,
accountName,
rootType,
false
);
}
}
function identifyIsGroup(child: COARootAccount | COAChildAccount): boolean {
if (child.isGroup !== undefined) {
return child.isGroup as boolean;
}
const keys = Object.keys(child);
const children = keys.filter((key) => !accountFields.includes(key));
if (children.length) {
return true;
}
return false;
}
async function getCOA(chartOfAccounts: string) {
const coaList = getCOAList();
const coa = coaList.find(({ name }) => name === chartOfAccounts);
const conCode = coa?.countryCode;
if (!conCode) {
return getStandardCOA();
}
try {
const countryCoa = (await import('fixtures/verified/' + conCode + '.json'))
.default;
return countryCoa.tree;
} catch (e) {
return getStandardCOA();
}
}
export async function createCOA(chartOfAccounts: string) {
const chart = await getCOA(chartOfAccounts);
await createCOAAccounts(chart, '', '', true);
}

217
src/setup/setupInstance.ts Normal file
View File

@ -0,0 +1,217 @@
import countryInfo from 'fixtures/countryInfo.json';
import { ConfigFile, DocValueMap } from 'fyo/core/types';
import Doc from 'fyo/model/doc';
import { getId } from 'fyo/telemetry/helpers';
import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from 'fyo/utils/consts';
import { AccountingSettings } from 'models/baseModels/AccountingSettings/AccountingSettings';
import { fyo } from 'src/initFyo';
import { createRegionalRecords } from 'src/regional';
import { createCOA } from './createCOA';
import { CountrySettings, SetupWizardOptions } from './types';
export default async function setupInstance(
setupWizardOptions: SetupWizardOptions
) {
const { companyName, country, bankName, chartOfAccounts } =
setupWizardOptions;
await updateSystemSettings(setupWizardOptions);
await updateAccountingSettings(setupWizardOptions);
await updatePrintSettings(setupWizardOptions);
await createCurrencyRecords();
await createAccountRecords(bankName, country, chartOfAccounts);
await createRegionalRecords(country);
await completeSetup(companyName);
}
async function updateAccountingSettings({
companyName,
country,
name,
email,
bankName,
fiscalYearStart,
fiscalYearEnd,
}: SetupWizardOptions) {
const accountingSettings = (await fyo.doc.getSingle(
'AccountingSettings'
)) as AccountingSettings;
await accountingSettings.setAndUpdate({
companyName,
country,
fullname: name,
email,
bankName,
fiscalYearStart,
fiscalYearEnd,
});
return accountingSettings;
}
async function updatePrintSettings({
companyLogo,
companyName,
email,
}: SetupWizardOptions) {
const printSettings = await fyo.doc.getSingle('PrintSettings');
await printSettings.setAndUpdate({
logo: companyLogo,
companyName,
email,
displayLogo: companyLogo ? true : false,
});
}
async function updateSystemSettings({
country,
currency: companyCurrency,
}: SetupWizardOptions) {
// @ts-ignore
const countryOptions = countryInfo[country] as CountrySettings;
const currency =
companyCurrency ?? countryOptions.currency ?? DEFAULT_CURRENCY;
const locale = countryOptions.locale ?? DEFAULT_LOCALE;
const systemSettings = await fyo.doc.getSingle('SystemSettings');
systemSettings.setAndUpdate({
locale,
currency,
});
}
async function createCurrencyRecords() {
const promises: Promise<Doc | undefined>[] = [];
const queue: string[] = [];
const countrySettings: CountrySettings[] = Object.values(
// @ts-ignore
countryInfo as Record<string, CountrySettings>
);
for (const country of countrySettings) {
const {
currency,
currency_fraction,
currency_fraction_units,
smallest_currency_fraction_value,
currency_symbol,
} = country;
if (!currency || queue.includes(currency)) {
continue;
}
const docObject = {
name: currency,
fraction: currency_fraction ?? '',
fractionUnits: currency_fraction_units ?? 100,
smallestValue: smallest_currency_fraction_value ?? 0.01,
symbol: currency_symbol ?? '',
};
const doc = checkAndCreateDoc('Currency', docObject);
if (doc) {
promises.push(doc);
queue.push(currency);
}
}
return Promise.all(promises);
}
async function createAccountRecords(
bankName: string,
country: string,
chartOfAccounts: string
) {
await createCOA(chartOfAccounts);
const parentAccount = await getBankAccountParentName(country);
const docObject = {
name: bankName,
rootType: 'Asset',
parentAccount,
accountType: 'Bank',
isGroup: false,
};
await checkAndCreateDoc('Account', docObject);
}
async function completeSetup(companyName: string) {
updateInitializationConfig(companyName);
await fyo.singles.AccountingSettings!.set('setupComplete', true);
await fyo.singles.AccountingSettings!.update();
}
function updateInitializationConfig(companyName: string) {
const dbPath = fyo.db.dbPath;
const files = fyo.config.get('files', []) as ConfigFile[];
files.forEach((file) => {
if (file.dbPath === dbPath) {
file.companyName = companyName;
file.id = getId();
}
});
fyo.config.set('files', files);
}
async function checkAndCreateDoc(schemaName: string, docObject: DocValueMap) {
const canCreate = await checkIfExactRecordAbsent(schemaName, docObject);
if (!canCreate) {
return;
}
const doc = await fyo.doc.getNewDoc(schemaName, docObject);
return doc.insert();
}
async function checkIfExactRecordAbsent(
schemaName: string,
docMap: DocValueMap
) {
const name = docMap.name as string;
const newDocObject = Object.assign({}, docMap);
const rows = await fyo.db.getAllRaw(schemaName, {
fields: ['*'],
filters: { 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 fyo.db.delete(schemaName, name);
return true;
}
return false;
}
async function getBankAccountParentName(country: string) {
const parentBankAccount = await fyo.db.getAllRaw('Account', {
fields: ['*'],
filters: { isGroup: true, accountType: 'Bank' },
});
if (parentBankAccount.length === 0) {
// This should not happen if the fixtures are correct.
return 'Bank Accounts';
} else if (parentBankAccount.length > 1) {
switch (country) {
case 'Indonesia':
return 'Bank Rupiah - 1121.000';
default:
break;
}
}
return parentBankAccount[0].name;
}

175
src/setup/standardCOA.ts Normal file
View File

@ -0,0 +1,175 @@
import { t } from 'fyo';
import { COATree } from 'models/baseModels/Account/types';
export function getStandardCOA(): COATree {
return {
[t`Application of Funds (Assets)`]: {
[t`Current Assets`]: {
[t`Accounts Receivable`]: {
[t`Debtors`]: {
accountType: 'Receivable',
},
},
[t`Bank Accounts`]: {
accountType: 'Bank',
isGroup: true,
},
[t`Cash In Hand`]: {
[t`Cash`]: {
accountType: 'Cash',
},
accountType: 'Cash',
},
[t`Loans and Advances (Assets)`]: {
isGroup: true,
},
[t`Securities and Deposits`]: {
[t`Earnest Money`]: {},
},
[t`Stock Assets`]: {
[t`Stock In Hand`]: {
accountType: 'Stock',
},
accountType: 'Stock',
},
[t`Tax Assets`]: {
isGroup: true,
},
},
[t`Fixed Assets`]: {
[t`Capital Equipments`]: {
accountType: 'Fixed Asset',
},
[t`Electronic Equipments`]: {
accountType: 'Fixed Asset',
},
[t`Furnitures and Fixtures`]: {
accountType: 'Fixed Asset',
},
[t`Office Equipments`]: {
accountType: 'Fixed Asset',
},
[t`Plants and Machineries`]: {
accountType: 'Fixed Asset',
},
[t`Buildings`]: {
accountType: 'Fixed Asset',
},
[t`Softwares`]: {
accountType: 'Fixed Asset',
},
[t`Accumulated Depreciation`]: {
accountType: 'Accumulated Depreciation',
},
},
[t`Investments`]: {
isGroup: true,
},
[t`Temporary Accounts`]: {
[t`Temporary Opening`]: {
accountType: 'Temporary',
},
},
rootType: 'Asset',
},
[t`Expenses`]: {
[t`Direct Expenses`]: {
[t`Stock Expenses`]: {
[t`Cost of Goods Sold`]: {
accountType: 'Cost of Goods Sold',
},
[t`Expenses Included In Valuation`]: {
accountType: 'Expenses Included In Valuation',
},
[t`Stock Adjustment`]: {
accountType: 'Stock Adjustment',
},
},
},
[t`Indirect Expenses`]: {
[t`Administrative Expenses`]: {},
[t`Commission on Sales`]: {},
[t`Depreciation`]: {
accountType: 'Depreciation',
},
[t`Entertainment Expenses`]: {},
[t`Freight and Forwarding Charges`]: {
accountType: 'Chargeable',
},
[t`Legal Expenses`]: {},
[t`Marketing Expenses`]: {
accountType: 'Chargeable',
},
[t`Miscellaneous Expenses`]: {
accountType: 'Chargeable',
},
[t`Office Maintenance Expenses`]: {},
[t`Office Rent`]: {},
[t`Postal Expenses`]: {},
[t`Print and Stationery`]: {},
[t`Round Off`]: {
accountType: 'Round Off',
},
[t`Salary`]: {},
[t`Sales Expenses`]: {},
[t`Telephone Expenses`]: {},
[t`Travel Expenses`]: {},
[t`Utility Expenses`]: {},
[t`Write Off`]: {},
[t`Exchange Gain/Loss`]: {},
[t`Gain/Loss on Asset Disposal`]: {},
},
rootType: 'Expense',
},
[t`Income`]: {
[t`Direct Income`]: {
[t`Sales`]: {},
[t`Service`]: {},
},
[t`Indirect Income`]: {
isGroup: true,
},
rootType: 'Income',
},
[t`Source of Funds (Liabilities)`]: {
[t`Current Liabilities`]: {
[t`Accounts Payable`]: {
[t`Creditors`]: {
accountType: 'Payable',
},
[t`Payroll Payable`]: {},
},
[t`Stock Liabilities`]: {
[t`Stock Received But Not Billed`]: {
accountType: 'Stock Received But Not Billed',
},
},
[t`Duties and Taxes`]: {
accountType: 'Tax',
isGroup: true,
},
[t`Loans (Liabilities)`]: {
[t`Secured Loans`]: {},
[t`Unsecured Loans`]: {},
[t`Bank Overdraft Account`]: {},
},
},
rootType: 'Liability',
},
[t`Equity`]: {
[t`Capital Stock`]: {
accountType: 'Equity',
},
[t`Dividends Paid`]: {
accountType: 'Equity',
},
[t`Opening Balance Equity`]: {
accountType: 'Equity',
},
[t`Retained Earnings`]: {
accountType: 'Equity',
},
rootType: 'Equity',
},
};
}

25
src/setup/types.ts Normal file
View File

@ -0,0 +1,25 @@
export interface SetupWizardOptions {
companyLogo: string;
companyName: string;
country: string;
fullname: string;
name: string;
email: string;
bankName: string;
currency: string;
fiscalYearStart: string;
fiscalYearEnd: string;
chartOfAccounts: string;
}
export interface CountrySettings {
code: string;
currency: string;
fiscal_year_start: string;
fiscal_year_end: string;
locale: string;
currency_fraction?: string;
currency_fraction_units?: number;
smallest_currency_fraction_value?: number;
currency_symbol?: string;
}

View File

@ -1,8 +1,9 @@
import frappe, { t } from 'frappe';
import { t } from 'fyo';
import { fyo } from './initFyo';
const config = {
getTitle: async () => {
const { companyName } = await frappe.getSingle('AccountingSettings');
const { companyName } = await fyo.doc.getSingle('AccountingSettings');
return companyName;
},
getGroups: () => [
@ -113,12 +114,12 @@ const config = {
{
label: t`GSTR1`,
route: '/report/gstr-1',
hidden: () => frappe.AccountingSettings.country !== 'India',
hidden: () => fyo.singles.AccountingSettings!.country !== 'India',
},
{
label: t`GSTR2`,
route: '/report/gstr-2',
hidden: () => frappe.AccountingSettings.country !== 'India',
hidden: () => fyo.singles.AccountingSettings!.country !== 'India',
},
],
},

View File

@ -1,30 +0,0 @@
let instances = [];
function onDocumentClick(e, el, fn) {
let target = e.target;
if (el !== target && !el.contains(target)) {
fn(e);
}
}
export const outsideClickDirective = {
beforeMount(el, binding) {
el.dataset.outsideClickIndex = instances.length;
const fn = binding.value;
const click = function (e) {
onDocumentClick(e, el, fn);
};
document.addEventListener('click', click);
instances.push(click);
},
unmounted(el) {
const index = el.dataset.outsideClickIndex;
const handler = instances[index];
document.addEventListener('click', handler);
instances.splice(index, 1);
},
};
// https://github.com/frappe/frappejs/commits/master/ui/plugins/outsideClickDirective.js

View File

@ -60,3 +60,12 @@ export function getYMin(points: Array<Array<number>>): number {
return getVal(minVal);
}
export function formatXLabels(label: string) {
// Format: Mmm YYYY -> Mm YY
const splits = label.split(' ');
const month = splits[0];
const year = splits[1].slice(2);
return `${month} ${year}`;
}

View File

@ -67,15 +67,6 @@ export function fuzzyMatch(keyword: string, candidate: string) {
return { isMatch, distance };
}
export function formatXLabels(label: string) {
// Format: Mmm YYYY -> Mm YY
const splits = label.split(' ');
const month = splits[0];
const year = splits[1].slice(2);
return `${month} ${year}`;
}
export function convertPesaValuesToFloat(obj: Record<string, unknown>) {
Object.keys(obj).forEach((key) => {
const value = obj[key];
@ -86,12 +77,3 @@ export function convertPesaValuesToFloat(obj: Record<string, unknown>) {
obj[key] = (value as Money).float;
});
}
export async function getIsSetupComplete() {
try {
const { setupComplete } = await fyo.doc.getSingle('AccountingSettings');
return !!setupComplete;
} catch {
return false;
}
}

View File

@ -1,10 +1,12 @@
import frappe from 'frappe';
import { DateTime } from 'luxon';
import { fyo } from 'src/initFyo';
export async function getDatesAndPeriodicity(period) {
export async function getDatesAndPeriodicity(
period: 'This Year' | 'This Quarter' | 'This Month'
) {
let fromDate, toDate;
let periodicity = 'Monthly';
let accountingSettings = await frappe.getSingle('AccountingSettings');
const periodicity = 'Monthly';
const accountingSettings = await fyo.doc.getSingle('AccountingSettings');
if (period === 'This Year') {
fromDate = accountingSettings.fiscalYearStart;

View File

@ -26,4 +26,4 @@ export interface QuickEditOptions {
hideFields?: string[];
showFields?: string[];
defaults?: Record<string, unknown>;
}
}

View File

@ -27,11 +27,11 @@ module.exports = {
},
pages: {
index: {
entry: 'src/main.js',
entry: 'src/renderer.ts',
filename: 'index.html',
},
print: {
entry: 'src/print.js',
entry: 'src/print.ts',
filename: 'print.html',
},
},