From 813ddb4c97026ab14f28c03bda45846818dac864 Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Wed, 20 Nov 2024 22:33:33 +0100 Subject: [PATCH] feat: add opening balances to general ledger report --- reports/GeneralLedger/GeneralLedger.ts | 109 +++++++++++++++++++++---- reports/LedgerReport.ts | 11 ++- 2 files changed, 100 insertions(+), 20 deletions(-) diff --git a/reports/GeneralLedger/GeneralLedger.ts b/reports/GeneralLedger/GeneralLedger.ts index 1752c9933..5b5ebffd8 100644 --- a/reports/GeneralLedger/GeneralLedger.ts +++ b/reports/GeneralLedger/GeneralLedger.ts @@ -27,6 +27,7 @@ export class GeneralLedger extends LedgerReport { usePagination = true; loading = false; + includeOpenings = true; ascending = false; reverted = false; referenceType: ReferenceType = 'All'; @@ -52,7 +53,54 @@ export class GeneralLedger extends LedgerReport { sort = false; } - const map = this._getGroupedMap(sort); + let reportData = this._rawData; + let mapOpenings: GroupedMap; + let openingCredit = 0; + let openingDebit = 0; + if (this.fromDate && this.includeOpenings) { + const fromDate = new Date(this.fromDate); + reportData = []; + const openingData = []; + for (const entry of this._rawData) { + if (entry.date < fromDate) { + openingData.push(entry); + } else { + reportData.push(entry); + } + } + + mapOpenings = this._getGroupedMap(sort, null, openingData); + this._setIndexOnEntries(mapOpenings); + this._getTotalsAndSetBalance(mapOpenings); + } + + const map = this._getGroupedMap(sort, null, reportData); + + if (mapOpenings) { + for (const key of mapOpenings.keys()) { + const { debit, credit } = this._getBalance(mapOpenings.get(key)); + + openingDebit += debit; + openingCredit += credit; + + if (this.groupBy !== 'none') { + map.get(key)?.unshift({ + name: -1, // Italics + account: t`Opening`, + date: null, + debit, + credit, + balance: debit - credit, + referenceType: '', + referenceName: '', + party: '', + reverted: false, + reverts: '', + }); + } + } + } + this._setIndexOnEntries(map); const { totalDebit, totalCredit } = this._getTotalsAndSetBalance(map); const consolidated = this._consolidateEntries(map); @@ -64,6 +112,22 @@ export class GeneralLedger extends LedgerReport { this._pushBlankEntry(consolidated); } + if (this.groupBy === 'none') { + consolidated.unshift({ + name: -2, // Bold + account: t`Opening`, + date: null, + debit: openingDebit, + credit: openingCredit, + balance: openingDebit - openingCredit, + referenceType: '', + referenceName: '', + party: '', + reverted: false, + reverts: '', + }); + } + /** * Set the closing row */ @@ -71,9 +135,9 @@ export class GeneralLedger extends LedgerReport { name: -2, // Bold account: t`Closing`, date: null, - debit: totalDebit, - credit: totalCredit, - balance: totalDebit - totalCredit, + debit: openingDebit + totalDebit, + credit: openingCredit + totalCredit, + balance: openingDebit + totalDebit - openingCredit - totalCredit, referenceType: '', referenceName: '', party: '', @@ -192,23 +256,29 @@ export class GeneralLedger extends LedgerReport { }); } + _getBalance(entries: LedgerEntry[]) { + let balance = 0; + let debit = 0; + let credit = 0; + + for (const entry of entries) { + debit += entry.debit!; + credit += entry.credit!; + + const diff = entry.debit! - entry.credit!; + balance += diff; + entry.balance = balance; + } + + return { balance, debit, credit }; + } + _getTotalsAndSetBalance(map: GroupedMap) { let totalDebit = 0; let totalCredit = 0; for (const key of map.keys()) { - let balance = 0; - let debit = 0; - let credit = 0; - - for (const entry of map.get(key)!) { - debit += entry.debit!; - credit += entry.credit!; - - const diff = entry.debit! - entry.credit!; - balance += diff; - entry.balance = balance; - } + const { debit, credit } = this._getBalance(map.get(key)!); /** * Total row incase groupBy is used @@ -261,7 +331,7 @@ export class GeneralLedger extends LedgerReport { (filters.date as string[]).push('<=', this.toDate as string); } - if (this.fromDate) { + if (!this.includeOpenings && this.fromDate) { filters.date ??= []; (filters.date as string[]).push('>=', this.fromDate as string); } @@ -352,6 +422,11 @@ export class GeneralLedger extends LedgerReport { label: t`Ascending Order`, fieldname: 'ascending', }, + { + fieldtype: 'Check', + label: t`Include Openings`, + fieldname: 'includeOpenings', + }, ] as Field[]; } diff --git a/reports/LedgerReport.ts b/reports/LedgerReport.ts index fd4b2e9ae..3584cf25d 100644 --- a/reports/LedgerReport.ts +++ b/reports/LedgerReport.ts @@ -43,13 +43,18 @@ export abstract class LedgerReport extends Report { return groupBy; } - _getGroupedMap(sort: boolean, groupBy?: GroupByKey): GroupedMap { + _getGroupedMap( + sort: boolean, + groupBy?: GroupByKey, + data?: LedgerEntry[] + ): GroupedMap { groupBy ??= this._getGroupByKey(); + data ??= this._rawData; /** * Sort rows by ascending or descending */ if (sort) { - this._rawData.sort((a, b) => { + data.sort((a, b) => { if (this.ascending) { return +a.date! - +b.date!; } @@ -63,7 +68,7 @@ export abstract class LedgerReport extends Report { * ∴ presorting maintains grouping order */ const map: GroupedMap = new Map(); - for (const entry of this._rawData) { + for (const entry of data) { const groupingKey = entry[groupBy]; if (!map.has(groupingKey)) { map.set(groupingKey, []);