mirror of
https://github.com/frappe/books.git
synced 2024-11-09 23:30:56 +00:00
commit
5e4873cda8
@ -64,12 +64,12 @@ export abstract class AccountReport extends LedgerReport {
|
|||||||
this._dateRanges = await this._getDateRanges();
|
this._dateRanges = await this._getDateRanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
getRootNode(
|
getRootNodes(
|
||||||
rootType: AccountRootType,
|
rootType: AccountRootType,
|
||||||
accountTree: AccountTree
|
accountTree: AccountTree
|
||||||
): AccountTreeNode | undefined {
|
): AccountTreeNode[] | undefined {
|
||||||
const rootNodeList = Object.values(accountTree);
|
const rootNodeList = Object.values(accountTree);
|
||||||
return rootNodeList.find((n) => n.rootType === rootType);
|
return rootNodeList.filter((n) => n.rootType === rootType);
|
||||||
}
|
}
|
||||||
|
|
||||||
getEmptyRow(): ReportRow {
|
getEmptyRow(): ReportRow {
|
||||||
@ -88,8 +88,11 @@ export abstract class AccountReport extends LedgerReport {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getTotalNode(rootNode: AccountTreeNode, name: string): AccountListNode {
|
getTotalNode(rootNodes: AccountTreeNode[], name: string): AccountListNode {
|
||||||
const accountTree = { [rootNode.name]: rootNode };
|
const accountTree: Tree = {};
|
||||||
|
for (const rootNode of rootNodes) {
|
||||||
|
accountTree[rootNode.name] = rootNode;
|
||||||
|
}
|
||||||
const leafNodes = getListOfLeafNodes(accountTree) as AccountTreeNode[];
|
const leafNodes = getListOfLeafNodes(accountTree) as AccountTreeNode[];
|
||||||
|
|
||||||
const totalMap = leafNodes.reduce((acc, node) => {
|
const totalMap = leafNodes.reduce((acc, node) => {
|
||||||
@ -236,6 +239,17 @@ export abstract class AccountReport extends LedgerReport {
|
|||||||
return null;
|
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[]> {
|
async _getDateRanges(): Promise<DateRange[]> {
|
||||||
const endpoints = await this._getFromAndToDates();
|
const endpoints = await this._getFromAndToDates();
|
||||||
const fromDate = DateTime.fromISO(endpoints.fromDate);
|
const fromDate = DateTime.fromISO(endpoints.fromDate);
|
||||||
@ -252,7 +266,10 @@ export abstract class AccountReport extends LedgerReport {
|
|||||||
|
|
||||||
const months: number = monthsMap[this.periodicity];
|
const months: number = monthsMap[this.periodicity];
|
||||||
const dateRanges: DateRange[] = [
|
const dateRanges: DateRange[] = [
|
||||||
{ toDate, fromDate: toDate.minus({ months }) },
|
{
|
||||||
|
toDate,
|
||||||
|
fromDate: this._fixMonthsJump(toDate, toDate.minus({ months })),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let count = this.count ?? 1;
|
let count = this.count ?? 1;
|
||||||
@ -264,7 +281,10 @@ export abstract class AccountReport extends LedgerReport {
|
|||||||
const lastRange = dateRanges.at(-1)!;
|
const lastRange = dateRanges.at(-1)!;
|
||||||
dateRanges.push({
|
dateRanges.push({
|
||||||
toDate: lastRange.fromDate,
|
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 = [
|
const fromDate = [
|
||||||
fromYear,
|
fromYear,
|
||||||
fys.toISOString().split('T')[0].split('-').slice(1),
|
(fys.getMonth() + 1).toString().padStart(2, '0'),
|
||||||
]
|
fys.getDate().toString().padStart(2, '0'),
|
||||||
.flat()
|
].join('-');
|
||||||
.join('-');
|
|
||||||
|
|
||||||
const toDate = [toYear, fye.toISOString().split('T')[0].split('-').slice(1)]
|
const toDate = [
|
||||||
.flat()
|
toYear,
|
||||||
.join('-');
|
(fye.getMonth() + 1).toString().padStart(2, '0'),
|
||||||
|
fye.getDate().toString().padStart(2, '0'),
|
||||||
|
].join('-');
|
||||||
|
|
||||||
return { fromDate, toDate };
|
return { fromDate, toDate };
|
||||||
}
|
}
|
||||||
@ -573,15 +594,17 @@ function getPrunedChildren(children: AccountTreeNode[]): AccountTreeNode[] {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertAccountRootNodeToAccountList(
|
export function convertAccountRootNodesToAccountList(
|
||||||
rootNode: AccountTreeNode
|
rootNodes: AccountTreeNode[]
|
||||||
): AccountList {
|
): AccountList {
|
||||||
if (!rootNode) {
|
if (!rootNodes || rootNodes.length == 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const accountList: AccountList = [];
|
const accountList: AccountList = [];
|
||||||
pushToAccountList(rootNode, accountList, 0);
|
for (const rootNode of rootNodes) {
|
||||||
|
pushToAccountList(rootNode, accountList, 0);
|
||||||
|
}
|
||||||
return accountList;
|
return accountList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
} from 'models/baseModels/Account/types';
|
} from 'models/baseModels/Account/types';
|
||||||
import {
|
import {
|
||||||
AccountReport,
|
AccountReport,
|
||||||
convertAccountRootNodeToAccountList,
|
convertAccountRootNodesToAccountList,
|
||||||
} from 'reports/AccountReport';
|
} from 'reports/AccountReport';
|
||||||
import { ReportData, RootTypeRow } from 'reports/types';
|
import { ReportData, RootTypeRow } from 'reports/types';
|
||||||
import { getMapFromList } from 'utils';
|
import { getMapFromList } from 'utils';
|
||||||
@ -44,15 +44,15 @@ export class BalanceSheet extends AccountReport {
|
|||||||
|
|
||||||
const rootTypeRows: RootTypeRow[] = this.rootTypes
|
const rootTypeRows: RootTypeRow[] = this.rootTypes
|
||||||
.map((rootType) => {
|
.map((rootType) => {
|
||||||
const rootNode = this.getRootNode(rootType, accountTree)!;
|
const rootNodes = this.getRootNodes(rootType, accountTree)!;
|
||||||
const rootList = convertAccountRootNodeToAccountList(rootNode);
|
const rootList = convertAccountRootNodesToAccountList(rootNodes);
|
||||||
return {
|
return {
|
||||||
rootType,
|
rootType,
|
||||||
rootNode,
|
rootNodes,
|
||||||
rows: this.getReportRowsFromAccountList(rootList),
|
rows: this.getReportRowsFromAccountList(rootList),
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter((row) => !!row.rootNode);
|
.filter((row) => !!row.rootNodes.length);
|
||||||
|
|
||||||
this.reportData = this.getReportDataFromRows(
|
this.reportData = this.getReportDataFromRows(
|
||||||
getMapFromList(rootTypeRows, 'rootType')
|
getMapFromList(rootTypeRows, 'rootType')
|
||||||
@ -88,8 +88,8 @@ export class BalanceSheet extends AccountReport {
|
|||||||
|
|
||||||
reportData.push(...row.rows);
|
reportData.push(...row.rows);
|
||||||
|
|
||||||
if (row.rootNode) {
|
if (row.rootNodes.length) {
|
||||||
const totalNode = this.getTotalNode(row.rootNode, totalName);
|
const totalNode = this.getTotalNode(row.rootNodes, totalName);
|
||||||
const totalRow = this.getRowFromAccountListNode(totalNode);
|
const totalRow = this.getRowFromAccountListNode(totalNode);
|
||||||
reportData.push(totalRow);
|
reportData.push(totalRow);
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
} from 'models/baseModels/Account/types';
|
} from 'models/baseModels/Account/types';
|
||||||
import {
|
import {
|
||||||
AccountReport,
|
AccountReport,
|
||||||
convertAccountRootNodeToAccountList,
|
convertAccountRootNodesToAccountList,
|
||||||
} from 'reports/AccountReport';
|
} from 'reports/AccountReport';
|
||||||
import {
|
import {
|
||||||
AccountListNode,
|
AccountListNode,
|
||||||
@ -45,28 +45,28 @@ export class ProfitAndLoss extends AccountReport {
|
|||||||
/**
|
/**
|
||||||
* Income Rows
|
* Income Rows
|
||||||
*/
|
*/
|
||||||
const incomeRoot = this.getRootNode(
|
const incomeRoots = this.getRootNodes(
|
||||||
AccountRootTypeEnum.Income,
|
AccountRootTypeEnum.Income,
|
||||||
accountTree
|
accountTree
|
||||||
)!;
|
)!;
|
||||||
const incomeList = convertAccountRootNodeToAccountList(incomeRoot);
|
const incomeList = convertAccountRootNodesToAccountList(incomeRoots);
|
||||||
const incomeRows = this.getReportRowsFromAccountList(incomeList);
|
const incomeRows = this.getReportRowsFromAccountList(incomeList);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expense Rows
|
* Expense Rows
|
||||||
*/
|
*/
|
||||||
const expenseRoot = this.getRootNode(
|
const expenseRoots = this.getRootNodes(
|
||||||
AccountRootTypeEnum.Expense,
|
AccountRootTypeEnum.Expense,
|
||||||
accountTree
|
accountTree
|
||||||
)!;
|
)!;
|
||||||
const expenseList = convertAccountRootNodeToAccountList(expenseRoot);
|
const expenseList = convertAccountRootNodesToAccountList(expenseRoots);
|
||||||
const expenseRows = this.getReportRowsFromAccountList(expenseList);
|
const expenseRows = this.getReportRowsFromAccountList(expenseList);
|
||||||
|
|
||||||
this.reportData = this.getReportDataFromRows(
|
this.reportData = this.getReportDataFromRows(
|
||||||
incomeRows,
|
incomeRows,
|
||||||
expenseRows,
|
expenseRows,
|
||||||
incomeRoot,
|
incomeRoots,
|
||||||
expenseRoot
|
expenseRoots
|
||||||
);
|
);
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
@ -74,43 +74,57 @@ export class ProfitAndLoss extends AccountReport {
|
|||||||
getReportDataFromRows(
|
getReportDataFromRows(
|
||||||
incomeRows: ReportData,
|
incomeRows: ReportData,
|
||||||
expenseRows: ReportData,
|
expenseRows: ReportData,
|
||||||
incomeRoot: AccountTreeNode | undefined,
|
incomeRoots: AccountTreeNode[] | undefined,
|
||||||
expenseRoot: AccountTreeNode | undefined
|
expenseRoots: AccountTreeNode[] | undefined
|
||||||
): ReportData {
|
): ReportData {
|
||||||
if (incomeRoot && !expenseRoot) {
|
if (
|
||||||
|
incomeRoots &&
|
||||||
|
incomeRoots.length &&
|
||||||
|
!expenseRoots &&
|
||||||
|
!expenseRoots.length
|
||||||
|
) {
|
||||||
return this.getIncomeOrExpenseRows(
|
return this.getIncomeOrExpenseRows(
|
||||||
incomeRoot,
|
incomeRoots,
|
||||||
incomeRows,
|
incomeRows,
|
||||||
t`Total Income (Credit)`
|
t`Total Income (Credit)`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expenseRoot && !incomeRoot) {
|
if (
|
||||||
|
expenseRoots &&
|
||||||
|
expenseRoots.length &&
|
||||||
|
(!incomeRoots || !incomeRoots.length)
|
||||||
|
) {
|
||||||
return this.getIncomeOrExpenseRows(
|
return this.getIncomeOrExpenseRows(
|
||||||
expenseRoot,
|
expenseRoots,
|
||||||
expenseRows,
|
expenseRows,
|
||||||
t`Total Income (Credit)`
|
t`Total Income (Credit)`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!incomeRoot || !expenseRoot) {
|
if (
|
||||||
|
!incomeRoots ||
|
||||||
|
!incomeRoots.length ||
|
||||||
|
!expenseRoots ||
|
||||||
|
!expenseRoots.length
|
||||||
|
) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.getIncomeAndExpenseRows(
|
return this.getIncomeAndExpenseRows(
|
||||||
incomeRows,
|
incomeRows,
|
||||||
expenseRows,
|
expenseRows,
|
||||||
incomeRoot,
|
incomeRoots,
|
||||||
expenseRoot
|
expenseRoots
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getIncomeOrExpenseRows(
|
getIncomeOrExpenseRows(
|
||||||
root: AccountTreeNode,
|
roots: AccountTreeNode[],
|
||||||
rows: ReportData,
|
rows: ReportData,
|
||||||
totalRowName: string
|
totalRowName: string
|
||||||
): ReportData {
|
): ReportData {
|
||||||
const total = this.getTotalNode(root, totalRowName);
|
const total = this.getTotalNode(roots, totalRowName);
|
||||||
const totalRow = this.getRowFromAccountListNode(total);
|
const totalRow = this.getRowFromAccountListNode(total);
|
||||||
|
|
||||||
return [rows, totalRow].flat();
|
return [rows, totalRow].flat();
|
||||||
@ -119,14 +133,17 @@ export class ProfitAndLoss extends AccountReport {
|
|||||||
getIncomeAndExpenseRows(
|
getIncomeAndExpenseRows(
|
||||||
incomeRows: ReportData,
|
incomeRows: ReportData,
|
||||||
expenseRows: ReportData,
|
expenseRows: ReportData,
|
||||||
incomeRoot: AccountTreeNode,
|
incomeRoots: AccountTreeNode[],
|
||||||
expenseRoot: 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 totalIncomeRow = this.getRowFromAccountListNode(totalIncome);
|
||||||
|
|
||||||
const totalExpense = this.getTotalNode(
|
const totalExpense = this.getTotalNode(
|
||||||
expenseRoot,
|
expenseRoots,
|
||||||
t`Total Expense (Debit)`
|
t`Total Expense (Debit)`
|
||||||
);
|
);
|
||||||
const totalExpenseRow = this.getRowFromAccountListNode(totalExpense);
|
const totalExpenseRow = this.getRowFromAccountListNode(totalExpense);
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
AccountReport,
|
AccountReport,
|
||||||
ACC_BAL_WIDTH,
|
ACC_BAL_WIDTH,
|
||||||
ACC_NAME_WIDTH,
|
ACC_NAME_WIDTH,
|
||||||
convertAccountRootNodeToAccountList,
|
convertAccountRootNodesToAccountList,
|
||||||
getFiscalEndpoints,
|
getFiscalEndpoints,
|
||||||
} from 'reports/AccountReport';
|
} from 'reports/AccountReport';
|
||||||
import {
|
import {
|
||||||
@ -65,15 +65,15 @@ export class TrialBalance extends AccountReport {
|
|||||||
|
|
||||||
const rootTypeRows: RootTypeRow[] = this.rootTypes
|
const rootTypeRows: RootTypeRow[] = this.rootTypes
|
||||||
.map((rootType) => {
|
.map((rootType) => {
|
||||||
const rootNode = this.getRootNode(rootType, accountTree)!;
|
const rootNodes = this.getRootNodes(rootType, accountTree)!;
|
||||||
const rootList = convertAccountRootNodeToAccountList(rootNode);
|
const rootList = convertAccountRootNodesToAccountList(rootNodes);
|
||||||
return {
|
return {
|
||||||
rootType,
|
rootType,
|
||||||
rootNode,
|
rootNodes,
|
||||||
rows: this.getReportRowsFromAccountList(rootList),
|
rows: this.getReportRowsFromAccountList(rootList),
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter((row) => !!row.rootNode);
|
.filter((row) => !!(row.rootNodes && row.rootNodes.length));
|
||||||
|
|
||||||
this.reportData = await this.getReportDataFromRows(rootTypeRows);
|
this.reportData = await this.getReportDataFromRows(rootTypeRows);
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
@ -107,6 +107,6 @@ export type Tree = Record<string, TreeNode>;
|
|||||||
|
|
||||||
export type RootTypeRow = {
|
export type RootTypeRow = {
|
||||||
rootType: AccountRootType;
|
rootType: AccountRootType;
|
||||||
rootNode: AccountTreeNode;
|
rootNodes: AccountTreeNode[];
|
||||||
rows: ReportData;
|
rows: ReportData;
|
||||||
};
|
};
|
Loading…
Reference in New Issue
Block a user