mirror of
https://github.com/frappe/books.git
synced 2024-12-22 19:09:01 +00:00
commit
5e4873cda8
@ -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) => {
|
||||
@ -236,6 +239,17 @@ export abstract class AccountReport extends LedgerReport {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Fix arythmetic on dates when adding or substracting months. If the
|
||||
// reference date was the last day in month, ensure that the resulting date is
|
||||
// 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 });
|
||||
} else {
|
||||
return date;
|
||||
}
|
||||
}
|
||||
|
||||
async _getDateRanges(): Promise<DateRange[]> {
|
||||
const endpoints = await this._getFromAndToDates();
|
||||
const fromDate = DateTime.fromISO(endpoints.fromDate);
|
||||
@ -252,7 +266,10 @@ export abstract class AccountReport extends LedgerReport {
|
||||
|
||||
const months: number = monthsMap[this.periodicity];
|
||||
const dateRanges: DateRange[] = [
|
||||
{ toDate, fromDate: toDate.minus({ months }) },
|
||||
{
|
||||
toDate,
|
||||
fromDate: this._fixMonthsJump(toDate, toDate.minus({ months })),
|
||||
},
|
||||
];
|
||||
|
||||
let count = this.count ?? 1;
|
||||
@ -264,7 +281,10 @@ export abstract class AccountReport extends LedgerReport {
|
||||
const lastRange = dateRanges.at(-1)!;
|
||||
dateRanges.push({
|
||||
toDate: lastRange.fromDate,
|
||||
fromDate: lastRange.fromDate.minus({ months }),
|
||||
fromDate: this._fixMonthsJump(
|
||||
toDate,
|
||||
lastRange.fromDate.minus({ months })
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
@ -445,14 +465,15 @@ export async function getFiscalEndpoints(
|
||||
|
||||
const fromDate = [
|
||||
fromYear,
|
||||
fys.toISOString().split('T')[0].split('-').slice(1),
|
||||
]
|
||||
.flat()
|
||||
.join('-');
|
||||
(fys.getMonth() + 1).toString().padStart(2, '0'),
|
||||
fys.getDate().toString().padStart(2, '0'),
|
||||
].join('-');
|
||||
|
||||
const toDate = [toYear, fye.toISOString().split('T')[0].split('-').slice(1)]
|
||||
.flat()
|
||||
.join('-');
|
||||
const toDate = [
|
||||
toYear,
|
||||
(fye.getMonth() + 1).toString().padStart(2, '0'),
|
||||
fye.getDate().toString().padStart(2, '0'),
|
||||
].join('-');
|
||||
|
||||
return { fromDate, toDate };
|
||||
}
|
||||
@ -573,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 = [];
|
||||
pushToAccountList(rootNode, accountList, 0);
|
||||
for (const rootNode of rootNodes) {
|
||||
pushToAccountList(rootNode, accountList, 0);
|
||||
}
|
||||
return accountList;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -107,6 +107,6 @@ export type Tree = Record<string, TreeNode>;
|
||||
|
||||
export type RootTypeRow = {
|
||||
rootType: AccountRootType;
|
||||
rootNode: AccountTreeNode;
|
||||
rootNodes: AccountTreeNode[];
|
||||
rows: ReportData;
|
||||
};
|
Loading…
Reference in New Issue
Block a user