diff --git a/package.json b/package.json index 1e5effa30..ab96a1204 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "iSunFA", - "version": "0.8.2+43", + "version": "0.8.2+44", "private": false, "scripts": { "dev": "next dev", diff --git a/src/interfaces/accounting_account.ts b/src/interfaces/accounting_account.ts index fb43dfdbf..f60420724 100644 --- a/src/interfaces/accounting_account.ts +++ b/src/interfaces/accounting_account.ts @@ -38,6 +38,7 @@ export interface IAccountForSheetDisplay { amount: number | null; percentage: number | null; indent: number; + children: IAccountForSheetDisplay[]; debit?: boolean; } diff --git a/src/lib/utils/account/common.ts b/src/lib/utils/account/common.ts index 5a1b1fa3a..237caf2ba 100644 --- a/src/lib/utils/account/common.ts +++ b/src/lib/utils/account/common.ts @@ -110,17 +110,48 @@ export function updateAccountAmounts(forest: IAccountNode[], lineItemsMap: Map { + maxChildDepth = Math.max(maxChildDepth, calculateMaxHeighOfNode(child)); + }); + + return maxChildDepth + 1; +} + export function addAccountNodeToMapRecursively( accountMap: Map, account: IAccountNode, - rootAmount: number + rootAmount: number, + currentDepth: number, + maxHeight?: number ) { - const newAccountNode = { ...account, children: [] }; + // Info: (20241011 - Murky) 倒數第二層可以保有自己的child + const isSecondLastLayer = maxHeight === 1; + const newAccountNode = isSecondLastLayer ? account : { ...account, children: [] }; const percentage = rootAmount === 0 ? 0 : account.amount / rootAmount; // Info: (20240702 - Murky) Calculate percentage accountMap.set(account.code, { accountNode: newAccountNode, percentage }); + account.children.forEach((child) => { - addAccountNodeToMapRecursively(accountMap, child, rootAmount); + const maxHeightOfChild = calculateMaxHeighOfNode(child); + addAccountNodeToMapRecursively( + accountMap, + child, + rootAmount, + currentDepth + 1, + maxHeightOfChild + ); }); + + return false; } export function transformForestToMap( @@ -129,12 +160,30 @@ export function transformForestToMap( const accountMap = new Map(); forest.forEach((accountNode) => { - addAccountNodeToMapRecursively(accountMap, accountNode, accountNode.amount); + const maxHeight = calculateMaxHeighOfNode(accountNode); + addAccountNodeToMapRecursively(accountMap, accountNode, accountNode.amount, 0, maxHeight); }); return accountMap; } +export function iAccountNode2IAccountForSheetDisplay( + accountNode: IAccountNode, + percentage: number, + children?: IAccountForSheetDisplay[] +): IAccountForSheetDisplay { + const iAccountForSheetDisplay: IAccountForSheetDisplay = { + code: accountNode.code, + name: accountNode.name, + amount: accountNode.amount, + indent: accountNode.level, + debit: accountNode.debit, + percentage, + children: children || [], + }; + return iAccountForSheetDisplay; +} + export function mappingAccountToSheetDisplay( accountMap: Map, sheetMappingRow: { @@ -162,8 +211,20 @@ export function mappingAccountToSheetDisplay( indent: row.indent, debit: undefined, percentage: 0, + children: [], }); } else { + const hasChildren = account.accountNode.children.length > 0; + const children = hasChildren + ? account.accountNode.children.map((child) => { + const childAccount = accountMap.get(child.code)!; + // Info: (20241011 - Murky) 最多只有兩層,所以最底不會再有children + return iAccountNode2IAccountForSheetDisplay( + childAccount.accountNode, + childAccount.percentage + ); + }) + : []; sheetDisplay.push({ code: row.code, name: row.name, @@ -171,6 +232,7 @@ export function mappingAccountToSheetDisplay( indent: row.indent, debit: account.accountNode.debit, percentage: account.percentage, + children, }); } }); diff --git a/src/lib/utils/report/balance_sheet_generator.ts b/src/lib/utils/report/balance_sheet_generator.ts index ccedef30e..8f306cfa8 100644 --- a/src/lib/utils/report/balance_sheet_generator.ts +++ b/src/lib/utils/report/balance_sheet_generator.ts @@ -236,7 +236,6 @@ export default class BalanceSheetGenerator extends FinancialReportGenerator { const accountForest = await this.generateFinancialReportTree(curPeriod); BalanceSheetGenerator.calculateLiabilityAndEquity(accountForest); const accountMap = transformForestToMap(accountForest); - return accountMap; } diff --git a/src/lib/utils/report/cash_flow_statement_generator.ts b/src/lib/utils/report/cash_flow_statement_generator.ts index afec4c42b..bd788d12a 100644 --- a/src/lib/utils/report/cash_flow_statement_generator.ts +++ b/src/lib/utils/report/cash_flow_statement_generator.ts @@ -194,6 +194,7 @@ export default class CashFlowStatementGenerator extends FinancialReportGenerator indent: level, debit, percentage: null, + children: [], }; const newReportSheetMapping = new Map([ @@ -247,6 +248,7 @@ export default class CashFlowStatementGenerator extends FinancialReportGenerator amount: sum, indent: 0, percentage: null, + children: [], }); return indirectOperatingCashFlow; @@ -311,6 +313,7 @@ export default class CashFlowStatementGenerator extends FinancialReportGenerator amount: null, indent: 0, percentage: null, + children: [], }); let directCashFlow = 0; @@ -337,6 +340,7 @@ export default class CashFlowStatementGenerator extends FinancialReportGenerator amount: total, indent: 1, percentage: null, + children: [], }; directCashFlow += total; @@ -359,6 +363,7 @@ export default class CashFlowStatementGenerator extends FinancialReportGenerator amount: directCashFlow, indent: 1, percentage: null, + children: [], }); return reportSheetMapping; } @@ -374,6 +379,7 @@ export default class CashFlowStatementGenerator extends FinancialReportGenerator amount: directCashFlow, indent: 1, percentage: null, + children: [], }); return reportSheetMapping; } @@ -414,6 +420,7 @@ export default class CashFlowStatementGenerator extends FinancialReportGenerator amount: 0, indent: 0, percentage: null, + children: [], }); result.set(SPECIAL_ACCOUNTS.CASH_INCREASE_THIS_PERIOD.code, { @@ -422,6 +429,7 @@ export default class CashFlowStatementGenerator extends FinancialReportGenerator amount: cashFlowFromOperating, indent: 0, percentage: null, + children: [], }); result.set(SPECIAL_ACCOUNTS.CASH_AMOUNT_IN_BEGINNING.code, { @@ -430,6 +438,7 @@ export default class CashFlowStatementGenerator extends FinancialReportGenerator amount: startCashBalance, indent: 0, percentage: null, + children: [], }); result.set(SPECIAL_ACCOUNTS.CASH_AMOUNT_IN_END.code, { @@ -438,6 +447,7 @@ export default class CashFlowStatementGenerator extends FinancialReportGenerator amount: endCashBalance, indent: 0, percentage: null, + children: [], }); return result; } @@ -457,6 +467,7 @@ export default class CashFlowStatementGenerator extends FinancialReportGenerator amount: 0, indent: account.indent, percentage: 0, + children: [], }; }); return result; diff --git a/src/lib/utils/report/financial_report_generator.ts b/src/lib/utils/report/financial_report_generator.ts index 82fca8d53..5d92be2b1 100644 --- a/src/lib/utils/report/financial_report_generator.ts +++ b/src/lib/utils/report/financial_report_generator.ts @@ -144,22 +144,20 @@ export default abstract class FinancialReportGenerator extends ReportGenerator { return lineItemsFromDB; } - public async generateIAccountReadyForFrontendArray(): Promise { + private combineTwoFSReportArray( + curPeriodContent: IAccountForSheetDisplay[], + prePeriodContent: IAccountForSheetDisplay[] + ) { const curPeriodAccountReadyForFrontendArray: IAccountReadyForFrontend[] = []; - - this.curPeriodContent = await this.generateFinancialReportArray(true); - - this.prePeriodContent = await this.generateFinancialReportArray(false); - if ( - this.curPeriodContent && - this.prePeriodContent && - this.curPeriodContent.length > 0 && - this.prePeriodContent.length > 0 && - this.curPeriodContent.length === this.prePeriodContent.length + curPeriodContent && + prePeriodContent && + curPeriodContent.length > 0 && + prePeriodContent.length > 0 && + curPeriodContent.length === prePeriodContent.length ) { - this.curPeriodContent.forEach((curPeriodAccount, index) => { - const lastPeriodAccount = this.prePeriodContent[index]; + curPeriodContent.forEach((curPeriodAccount, index) => { + const lastPeriodAccount = prePeriodContent[index]; const curPeriodAmount = curPeriodAccount.amount || 0; const prePeriodAmount = lastPeriodAccount.amount || 0; const curPeriodAmountString = formatNumberSeparateByComma(curPeriodAmount); @@ -171,6 +169,11 @@ export default abstract class FinancialReportGenerator extends ReportGenerator { ? Math.round(lastPeriodAccount.percentage * 100) : 0; + const children = this.combineTwoFSReportArray( + curPeriodAccount.children, + lastPeriodAccount.children + ); + const curPeriodPercentageString = numberBeDashIfFalsy(curPeriodPercentage); const prePeriodPercentageString = numberBeDashIfFalsy(prePeriodPercentage); const accountReadyForFrontend: IAccountReadyForFrontend = { @@ -185,12 +188,21 @@ export default abstract class FinancialReportGenerator extends ReportGenerator { prePeriodAmountString, prePeriodPercentageString, indent: curPeriodAccount.indent, - children: [], + children, }; curPeriodAccountReadyForFrontendArray.push(accountReadyForFrontend); }); } + return curPeriodAccountReadyForFrontendArray; + } + + public async generateIAccountReadyForFrontendArray(): Promise { + this.curPeriodContent = await this.generateFinancialReportArray(true); + + this.prePeriodContent = await this.generateFinancialReportArray(false); + const curPeriodAccountReadyForFrontendArray: IAccountReadyForFrontend[] = + this.combineTwoFSReportArray(this.curPeriodContent, this.prePeriodContent); return curPeriodAccountReadyForFrontendArray; }