From e070ac72dd9419f3415020847f30a595e4354c35 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Thu, 26 Oct 2023 18:59:09 +0200 Subject: [PATCH] feat: Computed Net Income under Equity in Balance Sheet report. (#271) --- packages/server/package.json | 3 +- packages/server/resources/locales/en.json | 1 + .../server/src/interfaces/BalanceSheet.ts | 33 ++- packages/server/src/interfaces/Ledger.ts | 5 + packages/server/src/locales/en.json | 1 + .../server/src/services/Accounting/Ledger.ts | 6 +- .../BalanceSheet/BalanceSheet.ts | 235 ++---------------- .../BalanceSheet/BalanceSheetAccounts.ts | 182 ++++++++++++++ .../BalanceSheet/BalanceSheetAggregators.ts | 142 +++++++++++ .../BalanceSheetComparsionPreviousPeriod.ts | 1 + .../BalanceSheetComparsionPreviousYear.ts | 10 +- .../BalanceSheet/BalanceSheetDatePeriods.ts | 8 +- .../BalanceSheet/BalanceSheetFilter.ts | 1 - .../BalanceSheet/BalanceSheetNetIncome.ts | 226 +++++++++++++++++ .../BalanceSheetNetIncomeDatePeriods.ts | 120 +++++++++ .../BalanceSheetNetIncomeDatePeriodsPP.ts | 127 ++++++++++ .../BalanceSheetNetIncomeDatePeriodsPY.ts | 122 +++++++++ .../BalanceSheet/BalanceSheetNetIncomePP.ts | 74 ++++++ .../BalanceSheet/BalanceSheetNetIncomePY.ts | 79 ++++++ .../BalanceSheet/BalanceSheetQuery.ts | 5 + .../BalanceSheet/BalanceSheetRepository.ts | 29 ++- .../BalanceSheetRepositoryNetIncome.ts | 222 +++++++++++++++++ .../BalanceSheet/BalanceSheetSchema.ts | 10 +- .../BalanceSheet/BalanceSheetTable.ts | 48 +++- .../BalanceSheet/constants.ts | 1 + packages/server/src/utils/deepdash.ts | 8 +- pnpm-lock.yaml | 17 +- 27 files changed, 1463 insertions(+), 253 deletions(-) create mode 100644 packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetAccounts.ts create mode 100644 packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetAggregators.ts delete mode 100644 packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetFilter.ts create mode 100644 packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncome.ts create mode 100644 packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncomeDatePeriods.ts create mode 100644 packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncomeDatePeriodsPP.ts create mode 100644 packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncomeDatePeriodsPY.ts create mode 100644 packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncomePP.ts create mode 100644 packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncomePY.ts create mode 100644 packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetRepositoryNetIncome.ts diff --git a/packages/server/package.json b/packages/server/package.json index 038706412..ed077abae 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -43,7 +43,7 @@ "crypto-random-string": "^3.2.0", "csurf": "^1.10.0", "deep-map": "^2.0.0", - "deepdash": "^5.3.7", + "deepdash": "^5.3.9", "dotenv": "^8.1.0", "errorhandler": "^1.5.1", "es6-weak-map": "^2.0.3", @@ -95,6 +95,7 @@ "rate-limiter-flexible": "^2.1.14", "reflect-metadata": "^0.1.13", "rtl-detect": "^1.0.4", + "source-map-loader": "^4.0.1", "ts-transformer-keys": "^0.4.2", "tsyringe": "^4.3.0", "typedi": "^0.8.0", diff --git a/packages/server/resources/locales/en.json b/packages/server/resources/locales/en.json index b021c2347..293186e9e 100644 --- a/packages/server/resources/locales/en.json +++ b/packages/server/resources/locales/en.json @@ -587,6 +587,7 @@ "balance_sheet.long_term_liabilities": "Long-Term Liabilities", "balance_sheet.non_current_liabilities": "Non-Current Liabilities", "balance_sheet.equity": "Equity", + "balance_sheet.net_income": "Net Income", "balance_sheet.account_name": "Account name", "balance_sheet.total": "Total", diff --git a/packages/server/src/interfaces/BalanceSheet.ts b/packages/server/src/interfaces/BalanceSheet.ts index dc2fe6f0b..4dad88e55 100644 --- a/packages/server/src/interfaces/BalanceSheet.ts +++ b/packages/server/src/interfaces/BalanceSheet.ts @@ -9,6 +9,7 @@ export enum BALANCE_SHEET_SCHEMA_NODE_TYPE { AGGREGATE = 'AGGREGATE', ACCOUNTS = 'ACCOUNTS', ACCOUNT = 'ACCOUNT', + NET_INCOME = 'NET_INCOME', } export enum BALANCE_SHEET_NODE_TYPE { @@ -33,6 +34,7 @@ export enum BALANCE_SHEET_SCHEMA_NODE_ID { LOGN_TERM_LIABILITY = 'LOGN_TERM_LIABILITY', NON_CURRENT_LIABILITY = 'NON_CURRENT_LIABILITY', EQUITY = 'EQUITY', + NET_INCOME = 'NET_INCOME', } // Balance sheet query. @@ -87,7 +89,6 @@ export interface IBalanceSheetDOO { meta: IBalanceSheetMeta; } - export interface IBalanceSheetCommonNode { total: IBalanceSheetTotal; horizontalTotals?: IBalanceSheetTotal[]; @@ -108,7 +109,7 @@ export interface IBalanceSheetAggregateNode extends IBalanceSheetCommonNode { id: string; name: string; nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE; - children?: (IBalanceSheetAggregateNode | IBalanceSheetAccountNode)[]; + children?: IBalanceSheetDataNode[]; } export interface IBalanceSheetTotal { @@ -118,6 +119,13 @@ export interface IBalanceSheetTotal { date?: string | Date; } +export interface IBalanceSheetAccountsNode extends IBalanceSheetCommonNode { + id: number | string; + name: string; + nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS; + children: IBalanceSheetAccountNode[]; +} + export interface IBalanceSheetAccountNode extends IBalanceSheetCommonNode { id: number; index: number; @@ -128,7 +136,17 @@ export interface IBalanceSheetAccountNode extends IBalanceSheetCommonNode { children?: IBalanceSheetAccountNode[]; } -export type IBalanceSheetDataNode = IBalanceSheetAggregateNode; +export interface IBalanceSheetNetIncomeNode extends IBalanceSheetCommonNode { + id: number; + name: string; + nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.NET_INCOME; +} + +export type IBalanceSheetDataNode = + | IBalanceSheetAggregateNode + | IBalanceSheetAccountNode + | IBalanceSheetAccountsNode + | IBalanceSheetNetIncomeNode; export interface IBalanceSheetPercentageAmount { amount: number; @@ -150,9 +168,16 @@ export interface IBalanceSheetSchemaAccountNode { accountsTypes: string[]; } +export interface IBalanceSheetSchemaNetIncomeNode { + id: string; + name: string; + type: BALANCE_SHEET_SCHEMA_NODE_TYPE; +} + export type IBalanceSheetSchemaNode = | IBalanceSheetSchemaAccountNode - | IBalanceSheetSchemaAggregateNode; + | IBalanceSheetSchemaAggregateNode + | IBalanceSheetSchemaNetIncomeNode; export interface IBalanceSheetDatePeriods { assocAccountNodeDatePeriods(node): any; diff --git a/packages/server/src/interfaces/Ledger.ts b/packages/server/src/interfaces/Ledger.ts index 2305d1ed3..2ab52a631 100644 --- a/packages/server/src/interfaces/Ledger.ts +++ b/packages/server/src/interfaces/Ledger.ts @@ -7,12 +7,14 @@ export interface ILedger { filter(cb: (entry: ILedgerEntry) => boolean): ILedger; whereAccountId(accountId: number): ILedger; + whereAccountsIds(accountsIds: number[]): ILedger; whereContactId(contactId: number): ILedger; whereFromDate(fromDate: Date | string): ILedger; whereToDate(toDate: Date | string): ILedger; whereCurrencyCode(currencyCode: string): ILedger; whereBranch(branchId: number): ILedger; whereItem(itemId: number): ILedger; + whereProject(projectId: number): ILedger; getClosingBalance(): number; getForeignClosingBalance(): number; @@ -21,6 +23,9 @@ export interface ILedger { getContactsIds(): number[]; getAccountsIds(): number[]; + + reverse(): ILedger; + isEmpty(): boolean; } export interface ILedgerEntry { diff --git a/packages/server/src/locales/en.json b/packages/server/src/locales/en.json index b021c2347..293186e9e 100644 --- a/packages/server/src/locales/en.json +++ b/packages/server/src/locales/en.json @@ -587,6 +587,7 @@ "balance_sheet.long_term_liabilities": "Long-Term Liabilities", "balance_sheet.non_current_liabilities": "Non-Current Liabilities", "balance_sheet.equity": "Equity", + "balance_sheet.net_income": "Net Income", "balance_sheet.account_name": "Account name", "balance_sheet.total": "Total", diff --git a/packages/server/src/services/Accounting/Ledger.ts b/packages/server/src/services/Accounting/Ledger.ts index e8bbcdfa4..0a3ecd41e 100644 --- a/packages/server/src/services/Accounting/Ledger.ts +++ b/packages/server/src/services/Accounting/Ledger.ts @@ -51,11 +51,11 @@ export default class Ledger implements ILedger { /** * Filters entries by the given accounts ids then returns a new ledger. - * @param {number[]} accountsIds - Accounts ids. + * @param {number[]} accountIds * @returns {ILedger} */ - public whereAccountsIds(accountsIds: number[]): ILedger { - return this.filter((entry) => accountsIds.indexOf(entry.accountId) !== -1); + public whereAccountsIds(accountIds: number[]): ILedger { + return this.filter((entry) => accountIds.indexOf(entry.accountId) !== -1); } /** diff --git a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheet.ts b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheet.ts index 28c9b9197..e07b91dbf 100644 --- a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheet.ts +++ b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheet.ts @@ -1,18 +1,10 @@ import * as R from 'ramda'; -import { defaultTo, isEmpty, sumBy } from 'lodash'; import FinancialSheet from '../FinancialSheet'; import { - IBalanceSheetAggregateNode, - IBalanceSheetAccountNode, - BALANCE_SHEET_SCHEMA_NODE_TYPE, IBalanceSheetQuery, INumberFormatQuery, - IAccount, IBalanceSheetSchemaNode, - IBalanceSheetSchemaAggregateNode, IBalanceSheetDataNode, - IBalanceSheetSchemaAccountNode, - IBalanceSheetCommonNode, } from '../../../interfaces'; import { BalanceSheetSchema } from './BalanceSheetSchema'; import { BalanceSheetPercentage } from './BalanceSheetPercentage'; @@ -24,8 +16,14 @@ import { FinancialSheetStructure } from '../FinancialSheetStructure'; import BalanceSheetRepository from './BalanceSheetRepository'; import { BalanceSheetQuery } from './BalanceSheetQuery'; import { BalanceSheetFiltering } from './BalanceSheetFiltering'; +import { BalanceSheetNetIncome } from './BalanceSheetNetIncome'; +import { BalanceSheetAggregators } from './BalanceSheetAggregators'; +import { BalanceSheetAccounts } from './BalanceSheetAccounts'; export default class BalanceSheet extends R.compose( + BalanceSheetAggregators, + BalanceSheetAccounts, + BalanceSheetNetIncome, BalanceSheetFiltering, BalanceSheetDatePeriods, BalanceSheetComparsionPreviousPeriod, @@ -53,6 +51,9 @@ export default class BalanceSheet extends R.compose( */ readonly baseCurrency: string; + /** + * Localization. + */ readonly i18n: any; /** @@ -77,216 +78,18 @@ export default class BalanceSheet extends R.compose( } /** - * Retrieve the accounts node of accounts types. - * @param {string} accountsTypes - * @returns {IAccount[]} - */ - private getAccountsByAccountTypes = (accountsTypes: string[]): IAccount[] => { - const mapAccountsByTypes = R.map((accountType) => - defaultTo(this.repository.accountsByType.get(accountType), []) - ); - return R.compose(R.flatten, mapAccountsByTypes)(accountsTypes); - }; - - /** - * Mappes the aggregate schema node type. - * @param {IBalanceSheetSchemaAggregateNode} node - Schema node. - * @return {IBalanceSheetAggregateNode} - */ - private reportSchemaAggregateNodeMapper = ( - node: IBalanceSheetSchemaAggregateNode - ): IBalanceSheetAggregateNode => { - const total = this.getTotalOfNodes(node.children); - - return { - name: this.i18n.__(node.name), - id: node.id, - nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE, - total: this.getTotalAmountMeta(total), - children: node.children, - }; - }; - - /** - * Compose shema aggregate node of balance sheet schema. - * @param {IBalanceSheetSchemaAggregateNode} node - * @returns {IBalanceSheetSchemaAggregateNode} - */ - private schemaAggregateNodeCompose = ( - node: IBalanceSheetSchemaAggregateNode - ) => { - return R.compose( - this.aggregateNodeTotalMapper, - this.reportSchemaAggregateNodeMapper - )(node); - }; - - /** - * Mappes the account model to report account node. - * @param {IAccount} account - * @returns {IBalanceSheetAccountNode} - */ - private reportSchemaAccountNodeMapper = ( - account: IAccount - ): IBalanceSheetAccountNode => { - const total = this.repository.totalAccountsLedger - .whereAccountId(account.id) - .getClosingBalance(); - - return { - id: account.id, - index: account.index, - name: account.name, - code: account.code, - total: this.getAmountMeta(total), - nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNT, - }; - }; - - /** - * - * @param {IAccount} account - * @returns {IBalanceSheetAccountNode} - */ - private reportSchemaAccountNodeComposer = ( - account: IAccount - ): IBalanceSheetAccountNode => { - return R.compose( - R.when( - this.query.isPreviousYearActive, - this.previousYearAccountNodeComposer - ), - R.when( - this.query.isPreviousPeriodActive, - this.previousPeriodAccountNodeComposer - ), - R.when( - this.query.isDatePeriodsColumnsType, - this.assocAccountNodeDatePeriods - ), - this.reportSchemaAccountNodeMapper - )(account); - }; - - /** - * Retrieve the total of the given nodes. - * @param {IBalanceSheetCommonNode[]} nodes - * @returns {number} - */ - private getTotalOfNodes = (nodes: IBalanceSheetCommonNode[]) => { - return sumBy(nodes, 'total.amount'); - }; - - /** - * Retrieve the report accounts node by the given accounts types. - * @param {string[]} accountsTypes - * @returns {} - */ - private getAccountsNodesByAccountTypes = (accountsTypes: string[]) => { - const accounts = this.getAccountsByAccountTypes(accountsTypes); - - return R.compose(R.map(this.reportSchemaAccountNodeComposer))(accounts); - }; - - /** - * Mappes the accounts schema node type. - * @param {IBalanceSheetSchemaNode} node - Schema node. - * @returns {IBalanceSheetAccountNode} - */ - private reportSchemaAccountsNodeMapper = ( - node: IBalanceSheetSchemaAccountNode - ): IBalanceSheetAccountNode => { - const accounts = this.getAccountsNodesByAccountTypes(node.accountsTypes); - const total = this.getTotalOfNodes(accounts); - - return { - id: node.id, - name: this.i18n.__(node.name), - type: node.type, - nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS, - children: accounts, - total: this.getTotalAmountMeta(total), - }; - }; - - /** - * Compose account schema node to report node. - * @param {IBalanceSheetSchemaAccountNode} node - * @returns {IBalanceSheetAccountNode} - */ - private reportSchemaAccountsNodeComposer = ( - node: IBalanceSheetSchemaAccountNode - ): IBalanceSheetAccountNode => { - return R.compose( - R.when( - this.query.isPreviousYearActive, - this.previousYearAggregateNodeComposer - ), - R.when( - this.query.isPreviousPeriodActive, - this.previousPeriodAggregateNodeComposer - ), - R.when( - this.query.isDatePeriodsColumnsType, - this.assocAccountsNodeDatePeriods - ), - this.reportSchemaAccountsNodeMapper - )(node); - }; - - /** - * Mappes the given report schema node. - * @param {IBalanceSheetSchemaNode} node - Schema node. - * @return {IBalanceSheetDataNode} - */ - private reportSchemaNodeMapper = ( - schemaNode: IBalanceSheetSchemaNode - ): IBalanceSheetDataNode => { - return R.compose( - R.when( - this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE), - this.schemaAggregateNodeCompose - ), - R.when( - this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS), - this.reportSchemaAccountsNodeComposer - ) - )(schemaNode); - }; - - /** - * Mappes the report schema nodes. - * @param {IBalanceSheetSchemaNode[]} nodes - - * @return {IBalanceSheetStructureSection[]} + * Parses report schema nodes. + * @param {IBalanceSheetSchemaNode[]} schema + * @returns {IBalanceSheetDataNode[]} */ - private reportSchemaAccountNodesMapper = ( - schemaNodes: IBalanceSheetSchemaNode[] + public parseSchemaNodes = ( + schema: IBalanceSheetSchemaNode[] ): IBalanceSheetDataNode[] => { - return this.mapNodesDeepReverse(schemaNodes, this.reportSchemaNodeMapper); - }; - - /** - * Sets total amount that calculated from node children. - * @param {IBalanceSheetSection} node - * @returns {IBalanceSheetDataNode} - */ - private aggregateNodeTotalMapper = ( - node: IBalanceSheetDataNode - ): IBalanceSheetDataNode => { return R.compose( - R.when( - this.query.isPreviousYearActive, - this.previousYearAggregateNodeComposer - ), - R.when( - this.query.isPreviousPeriodActive, - this.previousPeriodAggregateNodeComposer - ), - R.when( - this.query.isDatePeriodsColumnsType, - this.assocAggregateNodeDatePeriods - ) - )(node); + this.aggregatesSchemaParser, + this.netIncomeSchemaParser, + this.accountsSchemaParser + )(schema) as IBalanceSheetDataNode[]; }; /** @@ -299,7 +102,7 @@ export default class BalanceSheet extends R.compose( return R.compose( this.reportFilterPlugin, this.reportPercentageCompose, - this.reportSchemaAccountNodesMapper + this.parseSchemaNodes )(balanceSheetSchema); }; } diff --git a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetAccounts.ts b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetAccounts.ts new file mode 100644 index 000000000..1e077f2e0 --- /dev/null +++ b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetAccounts.ts @@ -0,0 +1,182 @@ +import * as R from 'ramda'; +import { defaultTo, toArray } from 'lodash'; +import { FinancialSheetStructure } from '../FinancialSheetStructure'; +import { + BALANCE_SHEET_SCHEMA_NODE_TYPE, + IAccount, + IBalanceSheetAccountNode, + IBalanceSheetAccountsNode, + IBalanceSheetDataNode, + IBalanceSheetSchemaAccountNode, + IBalanceSheetSchemaNode, + INumberFormatQuery, +} from '@/interfaces'; +import { BalanceSheetNetIncome } from './BalanceSheetNetIncome'; +import { BalanceSheetFiltering } from './BalanceSheetFiltering'; +import { BalanceSheetDatePeriods } from './BalanceSheetDatePeriods'; +import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod'; +import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear'; +import { BalanceSheetPercentage } from './BalanceSheetPercentage'; +import { BalanceSheetSchema } from './BalanceSheetSchema'; +import { BalanceSheetBase } from './BalanceSheetBase'; +import { BalanceSheetQuery } from './BalanceSheetQuery'; + +export const BalanceSheetAccounts = (Base: any) => + class extends R.compose( + BalanceSheetNetIncome, + BalanceSheetFiltering, + BalanceSheetDatePeriods, + BalanceSheetComparsionPreviousPeriod, + BalanceSheetComparsionPreviousYear, + BalanceSheetPercentage, + BalanceSheetSchema, + BalanceSheetBase, + FinancialSheetStructure + )(Base) { + /** + * Balance sheet query. + * @param {BalanceSheetQuery} + */ + readonly query: BalanceSheetQuery; + + /** + * Balance sheet number format query. + * @param {INumberFormatQuery} + */ + readonly numberFormat: INumberFormatQuery; + + /** + * Base currency of the organization. + * @param {string} + */ + readonly baseCurrency: string; + + /** + * Localization. + */ + readonly i18n: any; + + /** + * Retrieve the accounts node of accounts types. + * @param {string} accountsTypes + * @returns {IAccount[]} + */ + private getAccountsByAccountTypes = ( + accountsTypes: string[] + ): IAccount[] => { + const mapAccountsByTypes = R.map((accountType) => + defaultTo(this.repository.accountsByType.get(accountType), []) + ); + return R.compose(R.flatten, mapAccountsByTypes)(accountsTypes); + }; + + /** + * Mappes the account model to report account node. + * @param {IAccount} account + * @returns {IBalanceSheetAccountNode} + */ + private reportSchemaAccountNodeMapper = ( + account: IAccount + ): IBalanceSheetAccountNode => { + const total = this.repository.totalAccountsLedger + .whereAccountId(account.id) + .getClosingBalance(); + + return { + id: account.id, + index: account.index, + name: account.name, + code: account.code, + total: this.getAmountMeta(total), + nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNT, + }; + }; + + /** + * Mappes the given account model to the balance sheet account node. + * @param {IAccount} account + * @returns {IBalanceSheetAccountNode} + */ + private reportSchemaAccountNodeComposer = ( + account: IAccount + ): IBalanceSheetAccountNode => { + return R.compose( + R.when( + this.query.isPreviousYearActive, + this.previousYearAccountNodeComposer + ), + R.when( + this.query.isPreviousPeriodActive, + this.previousPeriodAccountNodeComposer + ), + R.when( + this.query.isDatePeriodsColumnsType, + this.assocAccountNodeDatePeriods + ), + this.reportSchemaAccountNodeMapper + )(account); + }; + + // ----------------------------- + // - Accounts Node Praser + // ----------------------------- + /** + * Retrieve the report accounts node by the given accounts types. + * @param {string[]} accountsTypes + * @returns {IBalanceSheetAccountNode[]} + */ + private getAccountsNodesByAccountTypes = ( + accountsTypes: string[] + ): IBalanceSheetAccountNode[] => { + const accounts = this.getAccountsByAccountTypes(accountsTypes); + return R.map(this.reportSchemaAccountNodeComposer, accounts); + }; + + /** + * Mappes the accounts schema node type. + * @param {IBalanceSheetSchemaNode} node - Schema node. + * @returns {IBalanceSheetAccountNode} + */ + private reportSchemaAccountsNodeMapper = ( + node: IBalanceSheetSchemaAccountNode + ): IBalanceSheetAccountsNode => { + const accounts = this.getAccountsNodesByAccountTypes(node.accountsTypes); + const children = toArray(node?.children); + + return { + id: node.id, + name: this.i18n.__(node.name), + nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS, + type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS, + children: [...accounts, ...children], + total: this.getTotalAmountMeta(0), + }; + }; + + /** + * Mappes the given report schema node. + * @param {IBalanceSheetSchemaNode | IBalanceSheetDataNode} node - Schema node. + * @return {IBalanceSheetSchemaNode | IBalanceSheetDataNode} + */ + private reportAccountSchemaParser = ( + node: IBalanceSheetSchemaNode | IBalanceSheetDataNode + ): IBalanceSheetSchemaNode | IBalanceSheetDataNode => { + return R.compose( + R.when( + this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS), + this.reportSchemaAccountsNodeMapper + ) + )(node); + }; + + /** + * Parses the report accounts schema nodes. + * @param {IBalanceSheetSchemaNode[]} nodes - + * @return {IBalanceSheetStructureSection[]} + */ + public accountsSchemaParser = ( + nodes: (IBalanceSheetSchemaNode | IBalanceSheetDataNode)[] + ): (IBalanceSheetDataNode | IBalanceSheetSchemaNode)[] => { + return this.mapNodesDeepReverse(nodes, this.reportAccountSchemaParser); + }; + }; diff --git a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetAggregators.ts b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetAggregators.ts new file mode 100644 index 000000000..2ed4ebbd2 --- /dev/null +++ b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetAggregators.ts @@ -0,0 +1,142 @@ +import * as R from 'ramda'; +import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod'; +import { FinancialHorizTotals } from '../FinancialHorizTotals'; +import { FinancialSheetStructure } from '../FinancialSheetStructure'; +import { + BALANCE_SHEET_SCHEMA_NODE_TYPE, + IBalanceSheetAggregateNode, + IBalanceSheetDataNode, + IBalanceSheetSchemaAggregateNode, + IBalanceSheetSchemaNode, + INumberFormatQuery, +} from '@/interfaces'; +import { BalanceSheetDatePeriods } from './BalanceSheetDatePeriods'; +import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod'; +import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear'; +import { BalanceSheetPercentage } from './BalanceSheetPercentage'; +import { BalanceSheetSchema } from './BalanceSheetSchema'; +import { BalanceSheetBase } from './BalanceSheetBase'; +import { BalanceSheetQuery } from './BalanceSheetQuery'; + +export const BalanceSheetAggregators = (Base: any) => + class extends R.compose( + BalanceSheetDatePeriods, + BalanceSheetComparsionPreviousPeriod, + BalanceSheetComparsionPreviousYear, + BalanceSheetPercentage, + BalanceSheetSchema, + BalanceSheetBase, + FinancialSheetStructure + )(Base) { + /** + * Balance sheet query. + * @param {BalanceSheetQuery} + */ + readonly query: BalanceSheetQuery; + + /** + * Balance sheet number format query. + * @param {INumberFormatQuery} + */ + readonly numberFormat: INumberFormatQuery; + + /** + * Base currency of the organization. + * @param {string} + */ + readonly baseCurrency: string; + + /** + * Localization. + */ + readonly i18n: any; + + /** + * Sets total amount that calculated from node children. + * @param {IBalanceSheetSection} node + * @returns {IBalanceSheetDataNode} + */ + private aggregateNodeTotalMapper = ( + node: IBalanceSheetDataNode + ): IBalanceSheetDataNode => { + return R.compose( + R.when( + this.query.isPreviousYearActive, + this.previousYearAggregateNodeComposer + ), + R.when( + this.query.isPreviousPeriodActive, + this.previousPeriodAggregateNodeComposer + ), + R.when( + this.query.isDatePeriodsColumnsType, + this.assocAggregateNodeDatePeriods + ) + )(node); + }; + + /** + * Mappes the aggregate schema node type. + * @param {IBalanceSheetSchemaAggregateNode} node - Schema node. + * @return {IBalanceSheetAggregateNode} + */ + private reportSchemaAggregateNodeMapper = ( + node: IBalanceSheetSchemaAggregateNode + ): IBalanceSheetAggregateNode => { + const total = this.getTotalOfNodes(node.children); + + return { + name: this.i18n.__(node.name), + id: node.id, + nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE, + type: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE, + total: this.getTotalAmountMeta(total), + children: node.children, + }; + }; + + /** + * Compose shema aggregate node of balance sheet schema. + * @param {IBalanceSheetSchemaAggregateNode} node + * @returns {IBalanceSheetSchemaAggregateNode} + */ + private schemaAggregateNodeCompose = ( + node: IBalanceSheetSchemaAggregateNode + ) => { + return R.compose( + this.aggregateNodeTotalMapper, + this.reportSchemaAggregateNodeMapper + )(node); + }; + + /** + * Mappes the given report schema node. + * @param {IBalanceSheetSchemaNode} node - Schema node. + * @return {IBalanceSheetDataNode} + */ + private reportAggregateSchemaParser = ( + node: IBalanceSheetSchemaNode + ): IBalanceSheetDataNode => { + return R.compose( + R.when( + this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE), + this.schemaAggregateNodeCompose + ), + R.when( + this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS), + this.schemaAggregateNodeCompose + ) + )(node); + }; + + /** + * Mappes the report schema nodes. + * @param {IBalanceSheetSchemaNode[]} nodes - + * @return {IBalanceSheetStructureSection[]} + */ + public aggregatesSchemaParser = ( + nodes: (IBalanceSheetSchemaNode | IBalanceSheetDataNode)[] + ): (IBalanceSheetDataNode | IBalanceSheetSchemaNode)[] => { + return this.mapNodesDeepReverse(nodes, this.reportAggregateSchemaParser); + }; + }; diff --git a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetComparsionPreviousPeriod.ts b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetComparsionPreviousPeriod.ts index 23db25a10..951176b74 100644 --- a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetComparsionPreviousPeriod.ts +++ b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetComparsionPreviousPeriod.ts @@ -6,6 +6,7 @@ import { IBalanceSheetAggregateNode, IBalanceSheetTotal, IBalanceSheetCommonNode, + IBalanceSheetComparsions, } from '@/interfaces'; import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod'; import { FinancialHorizTotals } from '../FinancialHorizTotals'; diff --git a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetComparsionPreviousYear.ts b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetComparsionPreviousYear.ts index 4486269e2..d5d048039 100644 --- a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetComparsionPreviousYear.ts +++ b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetComparsionPreviousYear.ts @@ -156,13 +156,14 @@ export const BalanceSheetComparsionPreviousYear = (Base: any) => * @param {IBalanceSheetCommonNode} node * @returns {IBalanceSheetCommonNode} */ - private assocPreviousYearAggregateHorizNode = ( + public assocPreviousYearAggregateHorizNode = ( node: IBalanceSheetCommonNode - ) => { + ): IBalanceSheetCommonNode => { const horizontalTotals = R.addIndex(R.map)( this.previousYearAggregateHorizNodeComposer(node), node.horizontalTotals - ); + ) as IBalanceSheetTotal[]; + return R.assoc('horizontalTotals', horizontalTotals, node); }; @@ -258,12 +259,11 @@ export const BalanceSheetComparsionPreviousYear = (Base: any) => // ------------------------------ // # Horizontal Nodes - Aggregate. // ------------------------------ - /** * Detarmines whether the given node has horizontal totals. * @param {IBalanceSheetCommonNode} node * @returns {boolean} */ - private isNodeHasHorizontalTotals = (node: IBalanceSheetCommonNode) => + public isNodeHasHorizontalTotals = (node: IBalanceSheetCommonNode) => !isEmpty(node.horizontalTotals); }; diff --git a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetDatePeriods.ts b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetDatePeriods.ts index 632ef9e9e..9ac6af1ce 100644 --- a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetDatePeriods.ts +++ b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetDatePeriods.ts @@ -107,9 +107,9 @@ export const BalanceSheetDatePeriods = (Base: FinancialSheet) => /** * - * @param {IBalanceSheetAccountNode} node - * @param {Date} fromDate - * @param {Date} toDate + * @param {IBalanceSheetAccountNode} node + * @param {Date} fromDate + * @param {Date} toDate * @returns {IBalanceSheetAccountNode} */ private getAccountNodeDatePeriod = ( @@ -201,7 +201,7 @@ export const BalanceSheetDatePeriods = (Base: FinancialSheet) => }; /** - * + * * @param node * @returns */ diff --git a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetFilter.ts b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetFilter.ts deleted file mode 100644 index 8b1378917..000000000 --- a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetFilter.ts +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncome.ts b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncome.ts new file mode 100644 index 000000000..a6695b370 --- /dev/null +++ b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncome.ts @@ -0,0 +1,226 @@ +import * as R from 'ramda'; +import { + BALANCE_SHEET_SCHEMA_NODE_TYPE, + IBalanceSheetDataNode, + IBalanceSheetNetIncomeNode, + IBalanceSheetSchemaNetIncomeNode, + IBalanceSheetSchemaNode, + IBalanceSheetTotalPeriod, +} from '@/interfaces'; +import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear'; +import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod'; +import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod'; +import { FinancialHorizTotals } from '../FinancialHorizTotals'; +import BalanceSheetRepository from './BalanceSheetRepository'; +import { BalanceSheetQuery } from './BalanceSheetQuery'; +import { BalanceSheetNetIncomePP } from './BalanceSheetNetIncomePP'; +import { BalanceSheetNetIncomePY } from './BalanceSheetNetIncomePY'; + +export const BalanceSheetNetIncome = (Base: any) => + class extends R.compose( + BalanceSheetNetIncomePP, + BalanceSheetNetIncomePY, + BalanceSheetComparsionPreviousYear, + BalanceSheetComparsionPreviousPeriod, + FinancialPreviousPeriod, + FinancialHorizTotals + )(Base) { + private repository: BalanceSheetRepository; + private query: BalanceSheetQuery; + + /** + * Retrieves the closing balance of income accounts. + * @returns {number} + */ + private getIncomeTotal = () => { + const closeingBalance = this.repository.incomeLedger.getClosingBalance(); + return closeingBalance; + }; + + /** + * Retrieves the closing balance of expenses accounts. + * @returns {number} + */ + private getExpensesTotal = () => { + const closingBalance = this.repository.expensesLedger.getClosingBalance(); + return closingBalance; + }; + + /** + * Retrieves the total net income. + * @returns {number} + */ + protected getNetIncomeTotal = () => { + const income = this.getIncomeTotal(); + const expenses = this.getExpensesTotal(); + + return income - expenses; + }; + + /** + * Mappes the aggregate schema node type. + * @param {IBalanceSheetSchemaNetIncomeNode} node - Schema node. + * @return {IBalanceSheetAggregateNode} + */ + protected schemaNetIncomeNodeMapper = ( + node: IBalanceSheetSchemaNetIncomeNode + ): IBalanceSheetNetIncomeNode => { + const total = this.getNetIncomeTotal(); + + return { + id: node.id, + name: this.i18n.__(node.name), + nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.NET_INCOME, + total: this.getTotalAmountMeta(total), + }; + }; + + /** + * Mapps the net income shcema node to report node. + * @param {IBalanceSheetSchemaNetIncomeNode} node + * @returns {IBalanceSheetNetIncomeNode} + */ + protected schemaNetIncomeNodeCompose = ( + node: IBalanceSheetSchemaNetIncomeNode + ): IBalanceSheetNetIncomeNode => { + return R.compose( + R.when( + this.query.isPreviousYearActive, + this.previousYearNetIncomeNodeCompose + ), + R.when( + this.query.isPreviousPeriodActive, + this.previousPeriodNetIncomeNodeCompose + ), + R.when( + this.query.isDatePeriodsColumnsType, + this.assocNetIncomeDatePeriodsNode + ), + this.schemaNetIncomeNodeMapper + )(node); + }; + + // -------------------------------- + // # Date Periods + // -------------------------------- + /** + * Retreives total income of the given date period. + * @param {number} accountId - + * @param {Date} toDate - + * @returns {number} + */ + private getIncomeDatePeriodTotal = (toDate: Date): number => { + const periodTotalBetween = this.repository.incomePeriodsAccountsLedger + .whereToDate(toDate) + .getClosingBalance(); + + const periodOpening = + this.repository.incomePeriodsOpeningAccountsLedger.getClosingBalance(); + + return periodOpening + periodTotalBetween; + }; + + /** + * Retrieves total expense of the given date period. + * @param {number} accountId - + * @param {Date} toDate - + * @returns {number} + */ + private getExpensesDatePeriodTotal = (toDate: Date): number => { + const periodTotalBetween = this.repository.expensesPeriodsAccountsLedger + .whereToDate(toDate) + .getClosingBalance(); + + const periodOpening = + this.repository.expensesOpeningAccountLedger.getClosingBalance(); + + return periodOpening + periodTotalBetween; + }; + + /** + * Retrieve the given net income date period total. + * @param {number} accountId + * @param {Date} toDate + * @returns {number} + */ + private getNetIncomeDatePeriodTotal = (toDate: Date): number => { + const income = this.getIncomeDatePeriodTotal(toDate); + const expense = this.getExpensesDatePeriodTotal(toDate); + + return income - expense; + }; + + /** + * Retrieves the net income date period node. + * @param {IBalanceSheetNetIncomeNode} node + * @param {Date} fromDate + * @param {Date} toDate + * @returns {IBalanceSheetNetIncomeNode} + */ + private getNetIncomeDatePeriodNode = ( + node: IBalanceSheetNetIncomeNode, + fromDate: Date, + toDate: Date + ): IBalanceSheetTotalPeriod => { + const periodTotal = this.getNetIncomeDatePeriodTotal(toDate); + + return this.getDatePeriodTotalMeta(periodTotal, fromDate, toDate); + }; + + /** + * Retrieve total date periods of the given net income node. + * @param {IBalanceSheetNetIncomeNode} node + * @returns {IBalanceSheetNetIncomeNode} + */ + private getNetIncomeDatePeriodsNode = ( + node: IBalanceSheetNetIncomeNode + ): IBalanceSheetTotalPeriod[] => { + return this.getReportNodeDatePeriods( + node, + this.getNetIncomeDatePeriodNode + ); + }; + + /** + * Assoc total date periods to net income node. + * @param {IBalanceSheetNetIncomeNode} node + * @returns {IBalanceSheetNetIncomeNode} + */ + public assocNetIncomeDatePeriodsNode = ( + node: IBalanceSheetNetIncomeNode + ): IBalanceSheetNetIncomeNode => { + const datePeriods = this.getNetIncomeDatePeriodsNode(node); + + return R.assoc('horizontalTotals', datePeriods, node); + }; + + // ----------------------------- + // - Net Income Nodes Praser + // ----------------------------- + /** + * Mappes the given report schema node. + * @param {IBalanceSheetSchemaNode} node - Schema node. + * @return {IBalanceSheetDataNode} + */ + private reportNetIncomeNodeSchemaParser = ( + schemaNode: IBalanceSheetSchemaNode + ): IBalanceSheetDataNode => { + return R.compose( + R.when( + this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.NET_INCOME), + this.schemaNetIncomeNodeCompose + ) + )(schemaNode); + }; + + /** + * Parses the report net income schema nodes. + * @param {(IBalanceSheetSchemaNode | IBalanceSheetDataNode)[]} nodes - + * @return {IBalanceSheetDataNode[]} + */ + public netIncomeSchemaParser = ( + nodes: (IBalanceSheetSchemaNode | IBalanceSheetDataNode)[] + ): IBalanceSheetDataNode[] => { + return this.mapNodesDeep(nodes, this.reportNetIncomeNodeSchemaParser); + }; + }; diff --git a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncomeDatePeriods.ts b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncomeDatePeriods.ts new file mode 100644 index 000000000..63bfa362a --- /dev/null +++ b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncomeDatePeriods.ts @@ -0,0 +1,120 @@ +import * as R from 'ramda'; +import { + IBalanceSheetNetIncomeNode, + IBalanceSheetTotalPeriod, +} from '@/interfaces'; +import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear'; +import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod'; +import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod'; +import { FinancialHorizTotals } from '../FinancialHorizTotals'; +import BalanceSheetRepository from './BalanceSheetRepository'; +import { BalanceSheetQuery } from './BalanceSheetQuery'; +import { BalanceSheetNetIncomePP } from './BalanceSheetNetIncomePP'; +import { BalanceSheetNetIncomePY } from './BalanceSheetNetIncomePY'; + +export const BalanceSheetNetIncomeDatePeriods = (Base: any) => + class extends R.compose( + BalanceSheetNetIncomePP, + BalanceSheetNetIncomePY, + BalanceSheetComparsionPreviousYear, + BalanceSheetComparsionPreviousPeriod, + FinancialPreviousPeriod, + FinancialHorizTotals + )(Base) { + private repository: BalanceSheetRepository; + private query: BalanceSheetQuery; + + // -------------------------------- + // # Date Periods + // -------------------------------- + /** + * Retreives total income of the given date period. + * @param {number} accountId - + * @param {Date} toDate - + * @returns {number} + */ + private getIncomeDatePeriodTotal = (toDate: Date): number => { + const periodTotalBetween = this.repository.incomePeriodsAccountsLedger + .whereToDate(toDate) + .getClosingBalance(); + + const periodOpening = + this.repository.incomePeriodsOpeningAccountsLedger.getClosingBalance(); + + return periodOpening + periodTotalBetween; + }; + + /** + * Retrieves total expense of the given date period. + * @param {number} accountId - + * @param {Date} toDate - + * @returns {number} + */ + private getExpensesDatePeriodTotal = (toDate: Date): number => { + const periodTotalBetween = this.repository.expensesPeriodsAccountsLedger + .whereToDate(toDate) + .getClosingBalance(); + + const periodOpening = + this.repository.expensesOpeningAccountLedger.getClosingBalance(); + + return periodOpening + periodTotalBetween; + }; + + /** + * Retrieve the given net income date period total. + * @param {number} accountId + * @param {Date} toDate + * @returns {number} + */ + private getNetIncomeDatePeriodTotal = (toDate: Date): number => { + const income = this.getIncomeDatePeriodTotal(toDate); + const expense = this.getExpensesDatePeriodTotal(toDate); + + return income - expense; + }; + + /** + * Retrieves the net income date period node. + * @param {IBalanceSheetNetIncomeNode} node + * @param {Date} fromDate + * @param {Date} toDate + * @returns {IBalanceSheetNetIncomeNode} + */ + private getNetIncomeDatePeriodNode = ( + node: IBalanceSheetNetIncomeNode, + fromDate: Date, + toDate: Date + ): IBalanceSheetTotalPeriod => { + const periodTotal = this.getNetIncomeDatePeriodTotal(toDate); + + return this.getDatePeriodTotalMeta(periodTotal, fromDate, toDate); + }; + + /** + * Retrieve total date periods of the given net income node. + * @param {IBalanceSheetNetIncomeNode} node + * @returns {IBalanceSheetNetIncomeNode} + */ + private getNetIncomeDatePeriodsNode = ( + node: IBalanceSheetNetIncomeNode + ): IBalanceSheetTotalPeriod[] => { + return this.getReportNodeDatePeriods( + node, + this.getNetIncomeDatePeriodNode + ); + }; + + /** + * Assoc total date periods to net income node. + * @param {IBalanceSheetNetIncomeNode} node + * @returns {IBalanceSheetNetIncomeNode} + */ + public assocNetIncomeDatePeriodsNode = ( + node: IBalanceSheetNetIncomeNode + ): IBalanceSheetNetIncomeNode => { + const datePeriods = this.getNetIncomeDatePeriodsNode(node); + + return R.assoc('horizontalTotals', datePeriods, node); + }; + }; diff --git a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncomeDatePeriodsPP.ts b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncomeDatePeriodsPP.ts new file mode 100644 index 000000000..94038de38 --- /dev/null +++ b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncomeDatePeriodsPP.ts @@ -0,0 +1,127 @@ +import * as R from 'ramda'; +import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod'; +import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod'; +import { FinancialHorizTotals } from '../FinancialHorizTotals'; +import { IBalanceSheetNetIncomeNode, IBalanceSheetTotal } from '@/interfaces'; +import { BalanceSheetQuery } from './BalanceSheetQuery'; +import BalanceSheetRepository from './BalanceSheetRepository'; + +export const BalanceSheetNetIncomeDatePeriodsPP = (Base: any) => + class extends R.compose( + BalanceSheetComparsionPreviousPeriod, + FinancialPreviousPeriod, + FinancialHorizTotals + )(Base) { + query: BalanceSheetQuery; + repository: BalanceSheetRepository; + + /** + * Retrieves the PY total income of the given date period. + * @param {number} accountId - + * @param {Date} toDate - + * @return {number} + */ + private getPPIncomeDatePeriodTotal = R.curry((toDate: Date) => { + const PYPeriodsTotal = this.repository.incomePPPeriodsAccountsLedger + .whereToDate(toDate) + .getClosingBalance(); + + const PYPeriodsOpeningTotal = + this.repository.incomePPPeriodsOpeningAccountLedger.getClosingBalance(); + + return PYPeriodsOpeningTotal + PYPeriodsTotal; + }); + + /** + * Retrieves the PY total expense of the given date period. + * @param {number} accountId - + * @param {Date} toDate - + * @returns {number} + */ + private getPPExpenseDatePeriodTotal = R.curry((toDate: Date) => { + const PYPeriodsTotal = this.repository.expensePPPeriodsAccountsLedger + .whereToDate(toDate) + .getClosingBalance(); + + const PYPeriodsOpeningTotal = + this.repository.expensePPPeriodsOpeningAccountLedger.getClosingBalance(); + + return PYPeriodsOpeningTotal + PYPeriodsTotal; + }); + + /** + * Retrieve the given net income total of the given period. + * @param {number} accountId - Account id. + * @param {Date} toDate - To date. + * @returns {number} + */ + private getPPNetIncomeDatePeriodTotal = R.curry((toDate: Date) => { + const income = this.getPPIncomeDatePeriodTotal(toDate); + const expense = this.getPPExpenseDatePeriodTotal(toDate); + + return income - expense; + }); + + /** + * Assoc preivous period to account horizontal total node. + * @param {IBalanceSheetAccountNode} node + * @returns {} + */ + private assocPreviousPeriodNetIncomeHorizTotal = R.curry( + (node: IBalanceSheetNetIncomeNode, totalNode) => { + const total = this.getPPNetIncomeDatePeriodTotal( + totalNode.previousPeriodToDate.date + ); + return R.assoc('previousPeriod', this.getAmountMeta(total), totalNode); + } + ); + + /** + * Compose previous period to aggregate horizontal nodes. + * @param {IBalanceSheetTotal} node + * @returns {IBalanceSheetTotal} + */ + private previousPeriodNetIncomeHorizNodeComposer = R.curry( + ( + node: IBalanceSheetNetIncomeNode, + horiontalTotalNode: IBalanceSheetTotal + ): IBalanceSheetTotal => { + return R.compose( + R.when( + this.query.isPreviousPeriodPercentageActive, + this.assocPreviousPeriodTotalPercentageNode + ), + R.when( + this.query.isPreviousPeriodChangeActive, + this.assocPreviousPeriodTotalChangeNode + ), + R.when( + this.query.isPreviousPeriodActive, + this.assocPreviousPeriodNetIncomeHorizTotal(node) + ), + R.when( + this.query.isPreviousPeriodActive, + this.assocPreviousPeriodHorizNodeFromToDates( + this.query.displayColumnsBy + ) + ) + )(horiontalTotalNode); + } + ); + + /** + * Associate the PP to net income horizontal nodes. + * @param {IBalanceSheetCommonNode} node + * @returns {IBalanceSheetCommonNode} + */ + public assocPreviousPeriodNetIncomeHorizNode = ( + node: IBalanceSheetNetIncomeNode + ): IBalanceSheetNetIncomeNode => { + const horizontalTotals = R.addIndex(R.map)( + this.previousPeriodNetIncomeHorizNodeComposer(node), + node.horizontalTotals + ) as IBalanceSheetTotal[]; + + return R.assoc('horizontalTotals', horizontalTotals, node); + }; + }; diff --git a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncomeDatePeriodsPY.ts b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncomeDatePeriodsPY.ts new file mode 100644 index 000000000..3e4a289e9 --- /dev/null +++ b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncomeDatePeriodsPY.ts @@ -0,0 +1,122 @@ +import * as R from 'ramda'; +import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear'; +import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod'; +import { FinancialHorizTotals } from '../FinancialHorizTotals'; +import { IBalanceSheetNetIncomeNode, IBalanceSheetTotal } from '@/interfaces'; +import { BalanceSheetQuery } from './BalanceSheetQuery'; +import BalanceSheetRepository from './BalanceSheetRepository'; + +export const BalanceSheetNetIncomeDatePeriodsPY = (Base: any) => + class extends R.compose( + BalanceSheetComparsionPreviousYear, + FinancialPreviousPeriod, + FinancialHorizTotals + )(Base) { + query: BalanceSheetQuery; + repository: BalanceSheetRepository; + + /** + * Retrieves the PY total income of the given date period. + * @param {Date} toDate - + * @return {number} + */ + private getPYIncomeDatePeriodTotal = R.curry((toDate: Date) => { + const PYPeriodsTotal = this.repository.incomePYPeriodsAccountsLedger + .whereToDate(toDate) + .getClosingBalance(); + + const PYPeriodsOpeningTotal = + this.repository.incomePYPeriodsOpeningAccountLedger.getClosingBalance(); + + return PYPeriodsOpeningTotal + PYPeriodsTotal; + }); + + /** + * Retrieves the PY total expense of the given date period. + * @param {Date} toDate - + * @returns {number} + */ + private getPYExpenseDatePeriodTotal = R.curry((toDate: Date) => { + const PYPeriodsTotal = this.repository.expensePYPeriodsAccountsLedger + .whereToDate(toDate) + .getClosingBalance(); + + const PYPeriodsOpeningTotal = + this.repository.expensePYPeriodsOpeningAccountLedger.getClosingBalance(); + + return PYPeriodsOpeningTotal + PYPeriodsTotal; + }); + + /** + * Retrieve the given net income total of the given period. + * @param {Date} toDate - To date. + * @returns {number} + */ + private getPYNetIncomeDatePeriodTotal = R.curry((toDate: Date) => { + const income = this.getPYIncomeDatePeriodTotal(toDate); + const expense = this.getPYExpenseDatePeriodTotal(toDate); + + return income - expense; + }); + + /** + * Assoc preivous year to account horizontal total node. + * @param {IBalanceSheetAccountNode} node + * @returns {} + */ + private assocPreviousYearNetIncomeHorizTotal = R.curry( + (node: IBalanceSheetNetIncomeNode, totalNode) => { + const total = this.getPYNetIncomeDatePeriodTotal( + totalNode.previousYearToDate.date + ); + return R.assoc('previousYear', this.getAmountMeta(total), totalNode); + } + ); + + /** + * Compose PY to net income horizontal nodes. + * @param {IBalanceSheetTotal} node + * @returns {IBalanceSheetTotal} + */ + private previousYearNetIncomeHorizNodeComposer = R.curry( + ( + node: IBalanceSheetNetIncomeNode, + horiontalTotalNode: IBalanceSheetTotal + ): IBalanceSheetTotal => { + return R.compose( + R.when( + this.query.isPreviousYearPercentageActive, + this.assocPreviousYearTotalPercentageNode + ), + R.when( + this.query.isPreviousYearChangeActive, + this.assocPreviousYearTotalChangeNode + ), + R.when( + this.query.isPreviousYearActive, + this.assocPreviousYearNetIncomeHorizTotal(node) + ), + R.when( + this.query.isPreviousYearActive, + this.assocPreviousYearHorizNodeFromToDates + ) + )(horiontalTotalNode); + } + ); + + /** + * Associate the PY to net income horizontal nodes. + * @param {IBalanceSheetCommonNode} node + * @returns {IBalanceSheetCommonNode} + */ + public assocPreviousYearNetIncomeHorizNode = ( + node: IBalanceSheetNetIncomeNode + ): IBalanceSheetNetIncomeNode => { + const horizontalTotals = R.addIndex(R.map)( + this.previousYearNetIncomeHorizNodeComposer(node), + node.horizontalTotals + ) as IBalanceSheetTotal[]; + + return R.assoc('horizontalTotals', horizontalTotals, node); + }; + }; diff --git a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncomePP.ts b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncomePP.ts new file mode 100644 index 000000000..40fe9c85a --- /dev/null +++ b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncomePP.ts @@ -0,0 +1,74 @@ +import * as R from 'ramda'; +import { + IBalanceSheetDataNode, + IBalanceSheetNetIncomeNode, +} from '@/interfaces'; +import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod'; +import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod'; +import { FinancialHorizTotals } from '../FinancialHorizTotals'; +import BalanceSheetRepository from './BalanceSheetRepository'; +import { BalanceSheetQuery } from './BalanceSheetQuery'; +import { BalanceSheetNetIncomeDatePeriodsPP } from './BalanceSheetNetIncomeDatePeriodsPP'; + +export const BalanceSheetNetIncomePP = (Base: any) => + class extends R.compose( + BalanceSheetNetIncomeDatePeriodsPP, + BalanceSheetComparsionPreviousPeriod, + FinancialPreviousPeriod, + FinancialHorizTotals + )(Base) { + private repository: BalanceSheetRepository; + private query: BalanceSheetQuery; + + // ------------------------------- + // # Previous Period (PP) + // ------------------------------- + /** + * Retrieves the PP net income. + * @returns {} + */ + protected getPreviousPeriodNetIncome = () => { + const income = this.repository.incomePPAccountsLedger.getClosingBalance(); + const expense = + this.repository.expensePPAccountsLedger.getClosingBalance(); + + return income - expense; + }; + + /** + * Associates the previous period to account node. + * @param {IBalanceSheetDataNode} node + * @returns {IBalanceSheetDataNode} + */ + protected assocPreviousPeriodNetIncomeNode = ( + node: IBalanceSheetDataNode + ): IBalanceSheetDataNode => { + const total = this.getPreviousPeriodNetIncome(); + + return R.assoc('previousPeriod', this.getAmountMeta(total), node); + }; + + /** + * Previous period account node composer. + * @param {IBalanceSheetNetIncomeNode} node + * @returns {IBalanceSheetNetIncomeNode} + */ + protected previousPeriodNetIncomeNodeCompose = ( + node: IBalanceSheetNetIncomeNode + ): IBalanceSheetNetIncomeNode => { + return R.compose( + R.when( + this.isNodeHasHorizTotals, + this.assocPreviousPeriodNetIncomeHorizNode + ), + R.when( + this.query.isPreviousPeriodPercentageActive, + this.assocPreviousPeriodPercentageNode + ), + R.when( + this.query.isPreviousPeriodChangeActive, + this.assocPreviousPeriodChangeNode + ), + this.assocPreviousPeriodNetIncomeNode + )(node); + }; \ No newline at end of file diff --git a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncomePY.ts b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncomePY.ts new file mode 100644 index 000000000..29d57813c --- /dev/null +++ b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetNetIncomePY.ts @@ -0,0 +1,79 @@ +import * as R from 'ramda'; +import { + IBalanceSheetDataNode, + IBalanceSheetNetIncomeNode, +} from '@/interfaces'; +import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear'; +import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod'; +import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod'; +import { FinancialHorizTotals } from '../FinancialHorizTotals'; +import BalanceSheetRepository from './BalanceSheetRepository'; +import { BalanceSheetQuery } from './BalanceSheetQuery'; +import { BalanceSheetNetIncomeDatePeriodsPY } from './BalanceSheetNetIncomeDatePeriodsPY'; + +export const BalanceSheetNetIncomePY = (Base: any) => + class extends R.compose( + BalanceSheetNetIncomeDatePeriodsPY, + BalanceSheetComparsionPreviousYear, + BalanceSheetComparsionPreviousPeriod, + FinancialPreviousPeriod, + FinancialHorizTotals + )(Base) { + private repository: BalanceSheetRepository; + private query: BalanceSheetQuery; + + // ------------------------------ + // # Previous Year (PY) + // ------------------------------ + /** + * Retrieves the previous year (PY) net income. + * @returns {number} + */ + protected getPreviousYearNetIncome = () => { + const income = + this.repository.incomePYTotalAccountsLedger.getClosingBalance(); + const expense = + this.repository.expensePYTotalAccountsLedger.getClosingBalance(); + + return income - expense; + }; + + /** + * Assoc previous year on aggregate node. + * @param {IBalanceSheetAccountNode} node + * @returns {IBalanceSheetAccountNode} + */ + protected assocPreviousYearNetIncomeNode = ( + node: IBalanceSheetNetIncomeNode + ): IBalanceSheetNetIncomeNode => { + const total = this.getPreviousYearNetIncome(); + + return R.assoc('previousYear', this.getTotalAmountMeta(total), node); + }; + + /** + * Assoc previous year attributes to aggregate node. + * @param {IBalanceSheetAccountNode} node + * @returns {IBalanceSheetAccountNode} + */ + protected previousYearNetIncomeNodeCompose = ( + node: IBalanceSheetNetIncomeNode + ): IBalanceSheetNetIncomeNode => { + return R.compose( + R.when( + this.query.isPreviousYearPercentageActive, + this.assocPreviousYearTotalPercentageNode + ), + R.when( + this.query.isPreviousYearChangeActive, + this.assocPreviousYearTotalChangeNode + ), + // Associate the PY to date periods horizontal nodes. + R.when( + this.isNodeHasHorizontalTotals, + this.assocPreviousYearNetIncomeHorizNode + ), + this.assocPreviousYearNetIncomeNode + )(node); + }; + }; diff --git a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetQuery.ts b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetQuery.ts index 0692de8d9..1a636952a 100644 --- a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetQuery.ts +++ b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetQuery.ts @@ -12,26 +12,31 @@ export class BalanceSheetQuery extends R.compose(FinancialDateRanges)( * @param {IBalanceSheetQuery} */ public readonly query: IBalanceSheetQuery; + /** * Previous year to date. * @param {Date} */ public readonly PYToDate: Date; + /** * Previous year from date. * @param {Date} */ public readonly PYFromDate: Date; + /** * Previous period to date. * @param {Date} */ public readonly PPToDate: Date; + /** * Previous period from date. * @param {Date} */ public readonly PPFromDate: Date; + /** * Constructor method * @param {IBalanceSheetQuery} query diff --git a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetRepository.ts b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetRepository.ts index e9de1be43..001f266be 100644 --- a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetRepository.ts +++ b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetRepository.ts @@ -3,6 +3,7 @@ import * as R from 'ramda'; import { Knex } from 'knex'; import { isEmpty } from 'lodash'; import { + IAccount, IAccountTransactionsGroupBy, IBalanceSheetQuery, ILedger, @@ -11,9 +12,12 @@ import { transformToMapBy } from 'utils'; import Ledger from '@/services/Accounting/Ledger'; import { BalanceSheetQuery } from './BalanceSheetQuery'; import { FinancialDatePeriods } from '../FinancialDatePeriods'; +import { ACCOUNT_PARENT_TYPE, ACCOUNT_TYPE } from '@/data/AccountTypes'; +import { BalanceSheetRepositoryNetIncome } from './BalanceSheetRepositoryNetIncome'; @Service() export default class BalanceSheetRepository extends R.compose( + BalanceSheetRepositoryNetIncome, FinancialDatePeriods )(class {}) { /** @@ -65,8 +69,22 @@ export default class BalanceSheetRepository extends R.compose( */ public readonly PPFromDate: Date; + /** + * Total closing accounts ledger. + * @param {Ledger} + */ public totalAccountsLedger: Ledger; + /** + * Total income accounts ledger. + */ + public incomeLedger: Ledger; + + /** + * Total expense accounts ledger. + */ + public expensesLedger: Ledger; + /** * Transactions group type. * @param {IAccountTransactionsGroupBy} @@ -171,6 +189,8 @@ export default class BalanceSheetRepository extends R.compose( ) { await this.initPeriodsPreviousPeriod(); } + // + await this.asyncInitializeNetIncome(); }; // ---------------------------- @@ -181,6 +201,7 @@ export default class BalanceSheetRepository extends R.compose( this.accounts = accounts; this.accountsByType = transformToMapBy(accounts, 'accountType'); + this.accountsByParentType = transformToMapBy(accounts, 'accountParentType'); }; // ---------------------------- @@ -309,14 +330,15 @@ export default class BalanceSheetRepository extends R.compose( /** * Closing accounts date periods. - * @param openingDate - * @param datePeriodsType + * @param {Date} fromDate + * @param {Date} toDate + * @param {string} datePeriodsType * @returns */ public accountsDatePeriods = async ( fromDate: Date, toDate: Date, - datePeriodsType + datePeriodsType: string ) => { const { AccountTransaction } = this.models; @@ -336,6 +358,7 @@ export default class BalanceSheetRepository extends R.compose( /** * Retrieve the opening balance transactions of the report. + * @param {Date|string} openingDate - */ public closingAccountsTotal = async (openingDate: Date | string) => { const { AccountTransaction } = this.models; diff --git a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetRepositoryNetIncome.ts b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetRepositoryNetIncome.ts new file mode 100644 index 000000000..74e21acd2 --- /dev/null +++ b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetRepositoryNetIncome.ts @@ -0,0 +1,222 @@ +import * as R from 'ramda'; +import { IAccount, ILedger } from '@/interfaces'; +import { FinancialDatePeriods } from '../FinancialDatePeriods'; +import { ACCOUNT_PARENT_TYPE } from '@/data/AccountTypes'; + +export const BalanceSheetRepositoryNetIncome = (Base) => + class extends R.compose(FinancialDatePeriods)(Base) { + // ----------------------- + // # Net Income + // ----------------------- + public incomeAccounts: IAccount[]; + public incomeAccountsIds: number[]; + + public expenseAccounts: IAccount[]; + public expenseAccountsIds: number[]; + + public incomePeriodsAccountsLedger: ILedger; + public incomePeriodsOpeningAccountsLedger: ILedger; + public expensesPeriodsAccountsLedger: ILedger; + public expensesOpeningAccountLedger: ILedger; + + public incomePPAccountsLedger: ILedger; + public expensePPAccountsLedger: ILedger; + + public incomePPPeriodsAccountsLedger: ILedger; + public incomePPPeriodsOpeningAccountLedger: ILedger; + public expensePPPeriodsAccountsLedger: ILedger; + public expensePPPeriodsOpeningAccountLedger: ILedger; + + public incomePYTotalAccountsLedger: ILedger; + public expensePYTotalAccountsLedger: ILedger; + + public incomePYPeriodsAccountsLedger: ILedger; + public incomePYPeriodsOpeningAccountLedger: ILedger; + public expensePYPeriodsAccountsLedger: ILedger; + public expensePYPeriodsOpeningAccountLedger: ILedger; + + /** + * Async initialize. + * @returns {Promise} + */ + public asyncInitializeNetIncome = async () => { + await this.initAccounts(); + await this.initAccountsTotalLedger(); + + // Net Income + this.initIncomeAccounts(); + this.initExpenseAccounts(); + + this.initIncomeTotalLedger(); + this.initExpensesTotalLedger(); + + // Date periods + if (this.query.isDatePeriodsColumnsType()) { + this.initNetIncomeDatePeriods(); + } + // Previous Year (PY). + if (this.query.isPreviousYearActive()) { + this.initNetIncomePreviousYear(); + } + // Previous Period (PP). + if (this.query.isPreviousPeriodActive()) { + this.initNetIncomePreviousPeriod(); + } + // Previous Year (PY) / Date Periods. + if ( + this.query.isPreviousYearActive() && + this.query.isDatePeriodsColumnsType() + ) { + this.initNetIncomePeriodsPreviewYear(); + } + // Previous Period (PP) / Date Periods. + if ( + this.query.isPreviousPeriodActive() && + this.query.isDatePeriodsColumnsType() + ) { + this.initNetIncomePeriodsPreviousPeriod(); + } + }; + + // ---------------------------- + // # Net Income + // ---------------------------- + /** + * Initialize income accounts. + */ + private initIncomeAccounts = () => { + const incomeAccounts = this.accountsByParentType.get( + ACCOUNT_PARENT_TYPE.INCOME + ); + const incomeAccountsIds = incomeAccounts.map((a) => a.id); + + this.incomeAccounts = incomeAccounts; + this.incomeAccountsIds = incomeAccountsIds; + }; + + /** + * Initialize expense accounts. + */ + private initExpenseAccounts = () => { + const expensesAccounts = this.accountsByParentType.get( + ACCOUNT_PARENT_TYPE.EXPENSE + ); + const expensesAccountsIds = expensesAccounts.map((a) => a.id); + + this.expenseAccounts = expensesAccounts; + this.expenseAccountsIds = expensesAccountsIds; + }; + + /** + * Initialize the income total ledger. + */ + private initIncomeTotalLedger = (): void => { + // Inject to the repository. + this.incomeLedger = this.totalAccountsLedger.whereAccountsIds( + this.incomeAccountsIds + ); + }; + + /** + * Initialize the expenses total ledger. + */ + private initExpensesTotalLedger = (): void => { + this.expensesLedger = this.totalAccountsLedger.whereAccountsIds( + this.expenseAccountsIds + ); + }; + + // ---------------------------- + // # Net Income - Date Periods + // ---------------------------- + /** + * Initialize the net income date periods. + */ + public initNetIncomeDatePeriods = () => { + this.incomePeriodsAccountsLedger = + this.periodsAccountsLedger.whereAccountsIds(this.incomeAccountsIds); + + this.incomePeriodsOpeningAccountsLedger = + this.periodsOpeningAccountLedger.whereAccountsIds( + this.incomeAccountsIds + ); + + this.expensesPeriodsAccountsLedger = + this.periodsAccountsLedger.whereAccountsIds(this.expenseAccountsIds); + + this.expensesOpeningAccountLedger = + this.periodsOpeningAccountLedger.whereAccountsIds( + this.expenseAccountsIds + ); + }; + + // ---------------------------- + // # Net Income - Previous Period + // ---------------------------- + /** + * Initialize the total net income PP. + */ + public initNetIncomePreviousPeriod = () => { + this.incomePPAccountsLedger = this.PPTotalAccountsLedger.whereAccountsIds( + this.incomeAccountsIds + ); + this.expensePPAccountsLedger = + this.PPTotalAccountsLedger.whereAccountsIds(this.expenseAccountsIds); + }; + + /** + * Initialize the net income periods of previous period. + */ + public initNetIncomePeriodsPreviousPeriod = () => { + this.incomePPPeriodsAccountsLedger = + this.PPPeriodsAccountsLedger.whereAccountsIds(this.incomeAccountsIds); + + this.incomePPPeriodsOpeningAccountLedger = + this.PPPeriodsOpeningAccountLedger.whereAccountsIds( + this.incomeAccountsIds + ); + + this.expensePPPeriodsAccountsLedger = + this.PPPeriodsAccountsLedger.whereAccountsIds(this.expenseAccountsIds); + + this.expensePPPeriodsOpeningAccountLedger = + this.PPPeriodsOpeningAccountLedger.whereAccountsIds( + this.expenseAccountsIds + ); + }; + + // ---------------------------- + // # Net Income - Previous Year + // ---------------------------- + /** + * Initialize the net income PY total. + */ + public initNetIncomePreviousYear = () => { + this.incomePYTotalAccountsLedger = + this.PYTotalAccountsLedger.whereAccountsIds(this.incomeAccountsIds); + + this.expensePYTotalAccountsLedger = + this.PYTotalAccountsLedger.whereAccountsIds(this.expenseAccountsIds); + }; + + /** + * Initialize the net income PY periods. + */ + public initNetIncomePeriodsPreviewYear = () => { + this.incomePYPeriodsAccountsLedger = + this.PYPeriodsAccountsLedger.whereAccountsIds(this.incomeAccountsIds); + + this.incomePYPeriodsOpeningAccountLedger = + this.PYPeriodsOpeningAccountLedger.whereAccountsIds( + this.incomeAccountsIds + ); + + this.expensePYPeriodsAccountsLedger = + this.PYPeriodsAccountsLedger.whereAccountsIds(this.expenseAccountsIds); + + this.expensePYPeriodsOpeningAccountLedger = + this.PYPeriodsOpeningAccountLedger.whereAccountsIds( + this.expenseAccountsIds + ); + }; + }; diff --git a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetSchema.ts b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetSchema.ts index a7faa81c5..5ef897506 100644 --- a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetSchema.ts +++ b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetSchema.ts @@ -7,12 +7,11 @@ import { import { ACCOUNT_TYPE } from '@/data/AccountTypes'; import { FinancialSchema } from '../FinancialSchema'; - export const BalanceSheetSchema = (Base) => class extends R.compose(FinancialSchema)(Base) { /** * Retrieves the balance sheet schema. - * @returns + * @returns */ getSchema = () => { return getBalanceSheetSchema(); @@ -115,6 +114,13 @@ export const getBalanceSheetSchema = () => [ id: BALANCE_SHEET_SCHEMA_NODE_ID.EQUITY, type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS, accountsTypes: [ACCOUNT_TYPE.EQUITY], + children: [ + { + name: 'balance_sheet.net_income', + id: BALANCE_SHEET_SCHEMA_NODE_ID.NET_INCOME, + type: BALANCE_SHEET_SCHEMA_NODE_TYPE.NET_INCOME, + }, + ], }, ], alwaysShow: true, diff --git a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetTable.ts b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetTable.ts index 43bf9c1b5..349b1a8f4 100644 --- a/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetTable.ts +++ b/packages/server/src/services/FinancialStatements/BalanceSheet/BalanceSheetTable.ts @@ -8,6 +8,10 @@ import { BALANCE_SHEET_SCHEMA_NODE_TYPE, IBalanceSheetDataNode, IBalanceSheetSchemaNode, + IBalanceSheetNetIncomeNode, + IBalanceSheetAccountNode, + IBalanceSheetAccountsNode, + IBalanceSheetAggregateNode, } from '@/interfaces'; import { tableRowMapper } from 'utils'; import FinancialSheet from '../FinancialSheet'; @@ -108,11 +112,13 @@ export default class BalanceSheetTable extends R.compose( }; /** - * - * @param node + * Retrieves the table row from the given report aggregate node. + * @param {IBalanceSheetAggregateNode} node * @returns {ITableRow} */ - private aggregateNodeTableRowsMapper = (node): ITableRow => { + private aggregateNodeTableRowsMapper = ( + node: IBalanceSheetAggregateNode + ): ITableRow => { const columns = this.commonColumnsAccessors(); const meta = { rowTypes: [IROW_TYPE.AGGREGATE], @@ -122,11 +128,13 @@ export default class BalanceSheetTable extends R.compose( }; /** - * - * @param node + * Retrieves the table row from the given report accounts node. + * @param {IBalanceSheetAccountsNode} node * @returns {ITableRow} */ - private accountsNodeTableRowsMapper = (node): ITableRow => { + private accountsNodeTableRowsMapper = ( + node: IBalanceSheetAccountsNode + ): ITableRow => { const columns = this.commonColumnsAccessors(); const meta = { rowTypes: [IROW_TYPE.ACCOUNTS], @@ -136,11 +144,13 @@ export default class BalanceSheetTable extends R.compose( }; /** - * - * @param {} node + * Retrieves the table row from the given report account node. + * @param {IBalanceSheetAccountNode} node * @returns {ITableRow} */ - private accountNodeTableRowsMapper = (node): ITableRow => { + private accountNodeTableRowsMapper = ( + node: IBalanceSheetAccountNode + ): ITableRow => { const columns = this.commonColumnsAccessors(); const meta = { @@ -150,6 +160,22 @@ export default class BalanceSheetTable extends R.compose( return tableRowMapper(node, columns, meta); }; + /** + * Retrieves the table row from the given report net income node. + * @param {IBalanceSheetNetIncomeNode} node + * @returns {ITableRow} + */ + private netIncomeNodeTableRowsMapper = ( + node: IBalanceSheetNetIncomeNode + ): ITableRow => { + const columns = this.commonColumnsAccessors(); + const meta = { + rowTypes: [IROW_TYPE.NET_INCOME], + id: node.id, + }; + return tableRowMapper(node, columns, meta); + }; + /** * Mappes the given report node to table rows. * @param {IBalanceSheetDataNode} node - @@ -169,6 +195,10 @@ export default class BalanceSheetTable extends R.compose( this.isNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNT), this.accountNodeTableRowsMapper, ], + [ + this.isNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.NET_INCOME), + this.netIncomeNodeTableRowsMapper, + ], ])(node); }; diff --git a/packages/server/src/services/FinancialStatements/BalanceSheet/constants.ts b/packages/server/src/services/FinancialStatements/BalanceSheet/constants.ts index ad447a199..8862408a1 100644 --- a/packages/server/src/services/FinancialStatements/BalanceSheet/constants.ts +++ b/packages/server/src/services/FinancialStatements/BalanceSheet/constants.ts @@ -9,5 +9,6 @@ export enum IROW_TYPE { AGGREGATE = 'AGGREGATE', ACCOUNTS = 'ACCOUNTS', ACCOUNT = 'ACCOUNT', + NET_INCOME = 'NET_INCOME', TOTAL = 'TOTAL', } diff --git a/packages/server/src/utils/deepdash.ts b/packages/server/src/utils/deepdash.ts index 5d4ef60df..6a97209e0 100644 --- a/packages/server/src/utils/deepdash.ts +++ b/packages/server/src/utils/deepdash.ts @@ -41,10 +41,10 @@ const mapValuesDeepReverse = (nodes, callback, config?) => { ); const mappedNode = callback(node, children); - _.set(clonedNodes, pathString, { - ...mappedNode, - ...(!_.isEmpty(children) ? { children } : {}), - }); + if (!mappedNode.children && children) { + mappedNode.children = children; + } + _.set(clonedNodes, pathString, mappedNode); }); return clonedNodes; }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fb23e7f02..faa3c040e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -102,7 +102,7 @@ importers: specifier: ^2.0.0 version: 2.0.0 deepdash: - specifier: ^5.3.7 + specifier: ^5.3.9 version: 5.3.9 dotenv: specifier: ^8.1.0 @@ -257,6 +257,9 @@ importers: rtl-detect: specifier: ^1.0.4 version: 1.0.4 + source-map-loader: + specifier: ^4.0.1 + version: 4.0.1(webpack@5.76.0) ts-transformer-keys: specifier: ^0.4.2 version: 0.4.4(typescript@3.9.10) @@ -22111,6 +22114,18 @@ packages: webpack: 5.76.0(webpack-cli@4.10.0) dev: false + /source-map-loader@4.0.1(webpack@5.76.0): + resolution: {integrity: sha512-oqXpzDIByKONVY8g1NUPOTQhe0UTU5bWUl32GSkqK2LjJj0HmwTMVKxcUip0RgAYhY1mqgOxjbQM48a0mmeNfA==} + engines: {node: '>= 14.15.0'} + peerDependencies: + webpack: ^5.72.1 + dependencies: + abab: 2.0.6 + iconv-lite: 0.6.3 + source-map-js: 1.0.2 + webpack: 5.76.0(webpack-cli@4.10.0) + dev: false + /source-map-resolve@0.5.3: resolution: {integrity: sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==} deprecated: See https://github.com/lydell/source-map-resolve#deprecated