From 71800dffd624d83c0dcec8fd306ebe15dfefaa15 Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Thu, 14 Apr 2022 09:57:48 +0530 Subject: [PATCH] incr: type ledgerposting --- .../{ledgerPosting.js => ledgerPosting.ts} | 111 ++++++++++++------ accounting/types.ts | 27 +++++ frappe/index.ts | 4 + 3 files changed, 103 insertions(+), 39 deletions(-) rename accounting/{ledgerPosting.js => ledgerPosting.ts} (51%) create mode 100644 accounting/types.ts diff --git a/accounting/ledgerPosting.js b/accounting/ledgerPosting.ts similarity index 51% rename from accounting/ledgerPosting.js rename to accounting/ledgerPosting.ts index aa83689d..ddb7f383 100644 --- a/accounting/ledgerPosting.js +++ b/accounting/ledgerPosting.ts @@ -1,33 +1,66 @@ import frappe from 'frappe'; +import Doc from 'frappe/model/doc'; +import Money from 'pesa/dist/types/src/money'; +import { + AccountEntry, + LedgerEntry, + LedgerPostingOptions, + TransactionType, +} from './types'; -export default class LedgerPosting { - constructor({ reference, party, date, description }) { +export class LedgerPosting { + reference: Doc; + party?: string; + date?: string; + description?: string; + entries: LedgerEntry[]; + entryMap: Record; + reverted: boolean; + accountEntries: AccountEntry[]; + + constructor({ reference, party, date, description }: LedgerPostingOptions) { this.reference = reference; this.party = party; this.date = date; - this.description = description; + this.description = description ?? ''; this.entries = []; this.entryMap = {}; - this.reverted = 0; + this.reverted = false; // To change balance while entering ledger entries this.accountEntries = []; } - async debit(account, amount, referenceType, referenceName) { + async debit( + account: string, + amount: Money, + referenceType?: string, + referenceName?: string + ) { const entry = this.getEntry(account, referenceType, referenceName); entry.debit = entry.debit.add(amount); await this.setAccountBalanceChange(account, 'debit', amount); } - async credit(account, amount, referenceType, referenceName) { + async credit( + account: string, + amount: Money, + referenceType?: string, + referenceName?: string + ) { const entry = this.getEntry(account, referenceType, referenceName); entry.credit = entry.credit.add(amount); await this.setAccountBalanceChange(account, 'credit', amount); } - async setAccountBalanceChange(accountName, type, amount) { + async setAccountBalanceChange( + accountName: string, + type: TransactionType, + amount: Money + ) { const debitAccounts = ['Asset', 'Expense']; - const { rootType } = await frappe.doc.getDoc('Account', accountName); + const accountDoc = await frappe.doc.getDoc('Account', accountName); + const rootType = accountDoc.rootType as string; + if (debitAccounts.indexOf(rootType) === -1) { const change = type == 'credit' ? amount : amount.neg(); this.accountEntries.push({ @@ -43,14 +76,14 @@ export default class LedgerPosting { } } - getEntry(account, referenceType, referenceName) { + getEntry(account: string, referenceType?: string, referenceName?: string) { if (!this.entryMap[account]) { - const entry = { + const entry: LedgerEntry = { account: account, - party: this.party || '', - date: this.date || this.reference.date, - referenceType: referenceType || this.reference.doctype, - referenceName: referenceName || this.reference.name, + party: this.party ?? '', + date: this.date ?? (this.reference.date as string), + referenceType: referenceType ?? this.reference.schemaName, + referenceName: referenceName ?? this.reference.name!, description: this.description, reverted: this.reverted, debit: frappe.pesa(0), @@ -72,47 +105,46 @@ export default class LedgerPosting { async postReverse() { this.validateEntries(); - let data = await frappe.db.getAll({ - doctype: 'AccountingLedgerEntry', + const data = await frappe.db.getAll('AccountingLedgerEntry', { fields: ['name'], filters: { - referenceName: this.reference.name, - reverted: 0, + referenceName: this.reference.name!, + reverted: false, }, }); - for (let entry of data) { - let entryDoc = await frappe.doc.getDoc( + for (const entry of data) { + const entryDoc = await frappe.doc.getDoc( 'AccountingLedgerEntry', - entry.name + entry.name as string ); - entryDoc.reverted = 1; + entryDoc.reverted = true; await entryDoc.update(); } let temp; - for (let entry of this.entries) { + for (const entry of this.entries) { temp = entry.debit; entry.debit = entry.credit; entry.credit = temp; - entry.reverted = 1; + entry.reverted = true; } - for (let entry of this.accountEntries) { - entry.balanceChange = entry.balanceChange.neg(); + for (const entry of this.accountEntries) { + entry.balanceChange = (entry.balanceChange as Money).neg(); } await this.insertEntries(); } makeRoundOffEntry() { - let { debit, credit } = this.getTotalDebitAndCredit(); - let difference = debit.sub(credit); - let absoluteValue = difference.abs(); - let allowance = 0.5; + const { debit, credit } = this.getTotalDebitAndCredit(); + const difference = debit.sub(credit); + const absoluteValue = difference.abs(); + const allowance = 0.5; if (absoluteValue.eq(0)) { return; } - let roundOffAccount = this.getRoundOffAccount(); + const roundOffAccount = this.getRoundOffAccount(); if (absoluteValue.lte(allowance)) { if (difference.gt(0)) { this.credit(roundOffAccount, absoluteValue); @@ -123,7 +155,7 @@ export default class LedgerPosting { } validateEntries() { - let { debit, credit } = this.getTotalDebitAndCredit(); + const { debit, credit } = this.getTotalDebitAndCredit(); if (debit.neq(credit)) { throw new frappe.errors.ValidationError( `Total Debit: ${frappe.format( @@ -138,7 +170,7 @@ export default class LedgerPosting { let debit = frappe.pesa(0); let credit = frappe.pesa(0); - for (let entry of this.entries) { + for (const entry of this.entries) { debit = debit.add(entry.debit); credit = credit.add(entry.credit); } @@ -147,19 +179,20 @@ export default class LedgerPosting { } async insertEntries() { - for (let entry of this.entries) { - let entryDoc = frappe.doc.getNewDoc('AccountingLedgerEntry'); + for (const entry of this.entries) { + const entryDoc = frappe.doc.getNewDoc('AccountingLedgerEntry'); Object.assign(entryDoc, entry); await entryDoc.insert(); } - for (let entry of this.accountEntries) { - let entryDoc = await frappe.doc.getDoc('Account', entry.name); - entryDoc.balance = entryDoc.balance.add(entry.balanceChange); + for (const entry of this.accountEntries) { + const entryDoc = await frappe.doc.getDoc('Account', entry.name); + const balance = entryDoc.get('balance') as Money; + entryDoc.balance = balance.add(entry.balanceChange); await entryDoc.update(); } } getRoundOffAccount() { - return frappe.AccountingSettings.roundOffAccount; + return frappe.singles.AccountingSettings!.roundOffAccount as string; } } diff --git a/accounting/types.ts b/accounting/types.ts new file mode 100644 index 00000000..be5d091c --- /dev/null +++ b/accounting/types.ts @@ -0,0 +1,27 @@ +import Doc from 'frappe/model/doc'; +import Money from 'pesa/dist/types/src/money'; + +export interface LedgerPostingOptions { + reference: Doc; + party?: string; + date?: string; + description?: string; +} +export interface LedgerEntry { + account: string; + party: string; + date: string; + referenceType: string; + referenceName: string; + description?: string; + reverted: boolean; + debit: Money; + credit: Money; +} + +export interface AccountEntry { + name: string; + balanceChange: Money; +} + +export type TransactionType = 'credit' | 'debit'; diff --git a/frappe/index.ts b/frappe/index.ts index 77181b36..7b5e7b8c 100644 --- a/frappe/index.ts +++ b/frappe/index.ts @@ -72,6 +72,10 @@ export class Frappe { return this.doc.models; } + get singles() { + return this.doc.singles; + } + get schemaMap() { return this.db.schemaMap; }