2
0
mirror of https://github.com/frappe/books.git synced 2024-12-22 02:49:03 +00:00

fix(reports): Allow multiple root account to have the same type

In French accounting, multiple root accounts can be of the same type,
ensure that reports take all accounts and not only the first one.
This commit is contained in:
Mildred Ki'Lya 2023-12-01 20:42:14 +01:00
parent d04b1561cd
commit dd2830530b
5 changed files with 78 additions and 50 deletions

View File

@ -64,12 +64,12 @@ export abstract class AccountReport extends LedgerReport {
this._dateRanges = await this._getDateRanges();
}
getRootNode(
getRootNodes(
rootType: AccountRootType,
accountTree: AccountTree
): AccountTreeNode | undefined {
): AccountTreeNode[] | undefined {
const rootNodeList = Object.values(accountTree);
return rootNodeList.find((n) => n.rootType === rootType);
return rootNodeList.filter((n) => n.rootType === rootType);
}
getEmptyRow(): ReportRow {
@ -88,8 +88,11 @@ export abstract class AccountReport extends LedgerReport {
};
}
getTotalNode(rootNode: AccountTreeNode, name: string): AccountListNode {
const accountTree = { [rootNode.name]: rootNode };
getTotalNode(rootNodes: AccountTreeNode[], name: string): AccountListNode {
const accountTree: Tree = {};
for (const rootNode of rootNodes) {
accountTree[rootNode.name] = rootNode;
}
const leafNodes = getListOfLeafNodes(accountTree) as AccountTreeNode[];
const totalMap = leafNodes.reduce((acc, node) => {
@ -241,9 +244,9 @@ export abstract class AccountReport extends LedgerReport {
// also the last day.
_fixMonthsJump(refDate: DateTime, date: DateTime): DateTime {
if (refDate.day == refDate.daysInMonth && date.day != date.daysInMonth) {
return date.set({day: date.daysInMonth})
return date.set({ day: date.daysInMonth });
} else {
return date
return date;
}
}
@ -263,7 +266,10 @@ export abstract class AccountReport extends LedgerReport {
const months: number = monthsMap[this.periodicity];
const dateRanges: DateRange[] = [
{ toDate, fromDate: this._fixMonthsJump(toDate, toDate.minus({ months })) },
{
toDate,
fromDate: this._fixMonthsJump(toDate, toDate.minus({ months })),
},
];
let count = this.count ?? 1;
@ -275,7 +281,10 @@ export abstract class AccountReport extends LedgerReport {
const lastRange = dateRanges.at(-1)!;
dateRanges.push({
toDate: lastRange.fromDate,
fromDate: this._fixMonthsJump(toDate, lastRange.fromDate.minus({ months })),
fromDate: this._fixMonthsJump(
toDate,
lastRange.fromDate.minus({ months })
),
});
}
@ -457,13 +466,13 @@ export async function getFiscalEndpoints(
const fromDate = [
fromYear,
(fys.getMonth() + 1).toString().padStart(2, '0'),
fys.getDate().toString().padStart(2, '0')
fys.getDate().toString().padStart(2, '0'),
].join('-');
const toDate = [
toYear,
(fye.getMonth() + 1).toString().padStart(2, '0'),
fye.getDate().toString().padStart(2, '0')
fye.getDate().toString().padStart(2, '0'),
].join('-');
return { fromDate, toDate };
@ -585,15 +594,17 @@ function getPrunedChildren(children: AccountTreeNode[]): AccountTreeNode[] {
});
}
export function convertAccountRootNodeToAccountList(
rootNode: AccountTreeNode
export function convertAccountRootNodesToAccountList(
rootNodes: AccountTreeNode[]
): AccountList {
if (!rootNode) {
if (!rootNodes || rootNodes.length == 0) {
return [];
}
const accountList: AccountList = [];
for (const rootNode of rootNodes) {
pushToAccountList(rootNode, accountList, 0);
}
return accountList;
}

View File

@ -5,7 +5,7 @@ import {
} from 'models/baseModels/Account/types';
import {
AccountReport,
convertAccountRootNodeToAccountList,
convertAccountRootNodesToAccountList,
} from 'reports/AccountReport';
import { ReportData, RootTypeRow } from 'reports/types';
import { getMapFromList } from 'utils';
@ -44,15 +44,15 @@ export class BalanceSheet extends AccountReport {
const rootTypeRows: RootTypeRow[] = this.rootTypes
.map((rootType) => {
const rootNode = this.getRootNode(rootType, accountTree)!;
const rootList = convertAccountRootNodeToAccountList(rootNode);
const rootNodes = this.getRootNodes(rootType, accountTree)!;
const rootList = convertAccountRootNodesToAccountList(rootNodes);
return {
rootType,
rootNode,
rootNodes,
rows: this.getReportRowsFromAccountList(rootList),
};
})
.filter((row) => !!row.rootNode);
.filter((row) => !!row.rootNodes.length);
this.reportData = this.getReportDataFromRows(
getMapFromList(rootTypeRows, 'rootType')
@ -88,8 +88,8 @@ export class BalanceSheet extends AccountReport {
reportData.push(...row.rows);
if (row.rootNode) {
const totalNode = this.getTotalNode(row.rootNode, totalName);
if (row.rootNodes.length) {
const totalNode = this.getTotalNode(row.rootNodes, totalName);
const totalRow = this.getRowFromAccountListNode(totalNode);
reportData.push(totalRow);
}

View File

@ -5,7 +5,7 @@ import {
} from 'models/baseModels/Account/types';
import {
AccountReport,
convertAccountRootNodeToAccountList,
convertAccountRootNodesToAccountList,
} from 'reports/AccountReport';
import {
AccountListNode,
@ -45,28 +45,28 @@ export class ProfitAndLoss extends AccountReport {
/**
* Income Rows
*/
const incomeRoot = this.getRootNode(
const incomeRoots = this.getRootNodes(
AccountRootTypeEnum.Income,
accountTree
)!;
const incomeList = convertAccountRootNodeToAccountList(incomeRoot);
const incomeList = convertAccountRootNodesToAccountList(incomeRoots);
const incomeRows = this.getReportRowsFromAccountList(incomeList);
/**
* Expense Rows
*/
const expenseRoot = this.getRootNode(
const expenseRoots = this.getRootNodes(
AccountRootTypeEnum.Expense,
accountTree
)!;
const expenseList = convertAccountRootNodeToAccountList(expenseRoot);
const expenseList = convertAccountRootNodesToAccountList(expenseRoots);
const expenseRows = this.getReportRowsFromAccountList(expenseList);
this.reportData = this.getReportDataFromRows(
incomeRows,
expenseRows,
incomeRoot,
expenseRoot
incomeRoots,
expenseRoots
);
this.loading = false;
}
@ -74,43 +74,57 @@ export class ProfitAndLoss extends AccountReport {
getReportDataFromRows(
incomeRows: ReportData,
expenseRows: ReportData,
incomeRoot: AccountTreeNode | undefined,
expenseRoot: AccountTreeNode | undefined
incomeRoots: AccountTreeNode[] | undefined,
expenseRoots: AccountTreeNode[] | undefined
): ReportData {
if (incomeRoot && !expenseRoot) {
if (
incomeRoots &&
incomeRoots.length &&
!expenseRoots &&
!expenseRoots.length
) {
return this.getIncomeOrExpenseRows(
incomeRoot,
incomeRoots,
incomeRows,
t`Total Income (Credit)`
);
}
if (expenseRoot && !incomeRoot) {
if (
expenseRoots &&
expenseRoots.length &&
(!incomeRoots || !incomeRoots.length)
) {
return this.getIncomeOrExpenseRows(
expenseRoot,
expenseRoots,
expenseRows,
t`Total Income (Credit)`
);
}
if (!incomeRoot || !expenseRoot) {
if (
!incomeRoots ||
!incomeRoots.length ||
!expenseRoots ||
!expenseRoots.length
) {
return [];
}
return this.getIncomeAndExpenseRows(
incomeRows,
expenseRows,
incomeRoot,
expenseRoot
incomeRoots,
expenseRoots
);
}
getIncomeOrExpenseRows(
root: AccountTreeNode,
roots: AccountTreeNode[],
rows: ReportData,
totalRowName: string
): ReportData {
const total = this.getTotalNode(root, totalRowName);
const total = this.getTotalNode(roots, totalRowName);
const totalRow = this.getRowFromAccountListNode(total);
return [rows, totalRow].flat();
@ -119,14 +133,17 @@ export class ProfitAndLoss extends AccountReport {
getIncomeAndExpenseRows(
incomeRows: ReportData,
expenseRows: ReportData,
incomeRoot: AccountTreeNode,
expenseRoot: AccountTreeNode
incomeRoots: AccountTreeNode[],
expenseRoots: AccountTreeNode[]
) {
const totalIncome = this.getTotalNode(incomeRoot, t`Total Income (Credit)`);
const totalIncome = this.getTotalNode(
incomeRoots,
t`Total Income (Credit)`
);
const totalIncomeRow = this.getRowFromAccountListNode(totalIncome);
const totalExpense = this.getTotalNode(
expenseRoot,
expenseRoots,
t`Total Expense (Debit)`
);
const totalExpenseRow = this.getRowFromAccountListNode(totalExpense);

View File

@ -9,7 +9,7 @@ import {
AccountReport,
ACC_BAL_WIDTH,
ACC_NAME_WIDTH,
convertAccountRootNodeToAccountList,
convertAccountRootNodesToAccountList,
getFiscalEndpoints,
} from 'reports/AccountReport';
import {
@ -65,15 +65,15 @@ export class TrialBalance extends AccountReport {
const rootTypeRows: RootTypeRow[] = this.rootTypes
.map((rootType) => {
const rootNode = this.getRootNode(rootType, accountTree)!;
const rootList = convertAccountRootNodeToAccountList(rootNode);
const rootNodes = this.getRootNodes(rootType, accountTree)!;
const rootList = convertAccountRootNodesToAccountList(rootNodes);
return {
rootType,
rootNode,
rootNodes,
rows: this.getReportRowsFromAccountList(rootList),
};
})
.filter((row) => !!row.rootNode);
.filter((row) => !!(row.rootNodes && row.rootNodes.length));
this.reportData = await this.getReportDataFromRows(rootTypeRows);
this.loading = false;

View File

@ -107,6 +107,6 @@ export type Tree = Record<string, TreeNode>;
export type RootTypeRow = {
rootType: AccountRootType;
rootNode: AccountTreeNode;
rootNodes: AccountTreeNode[];
rows: ReportData;
};