2
0
mirror of https://github.com/frappe/books.git synced 2024-11-10 07:40:55 +00:00
books/reports/ProfitAndLoss/ProfitAndLoss.ts

136 lines
3.4 KiB
TypeScript
Raw Normal View History

2022-05-15 12:13:31 +00:00
import { t } from 'fyo';
import {
AccountRootType,
2022-05-16 10:10:35 +00:00
AccountRootTypeEnum,
2022-05-15 12:13:31 +00:00
} from 'models/baseModels/Account/types';
import {
2022-05-16 10:10:35 +00:00
AccountReport,
convertAccountRootNodeToAccountList,
} from 'reports/AccountReport';
2022-05-16 13:01:58 +00:00
import {
AccountListNode,
AccountTreeNode,
ReportData,
ValueMap,
} from 'reports/types';
2022-05-16 10:10:35 +00:00
export class ProfitAndLoss extends AccountReport {
static title = t`Profit And Loss`;
static reportName = 'profit-and-loss';
2022-05-18 14:58:35 +00:00
loading: boolean = false;
2022-05-16 10:10:35 +00:00
get rootTypes(): AccountRootType[] {
return [AccountRootTypeEnum.Income, AccountRootTypeEnum.Expense];
2022-05-16 07:49:01 +00:00
}
async setReportData(filter?: string, force?: boolean) {
2022-05-18 14:58:35 +00:00
this.loading = true;
if (force || filter !== 'hideGroupAmounts') {
2022-05-16 10:10:35 +00:00
await this._setRawData();
}
2022-05-16 07:49:01 +00:00
const map = this._getGroupedMap(true, 'account');
const rangeGroupedMap = await this._getGroupedByDateRanges(map);
2022-05-15 12:13:31 +00:00
const accountTree = await this._getAccountTree(rangeGroupedMap);
for (const name of Object.keys(accountTree)) {
const { rootType } = accountTree[name];
2022-05-16 10:10:35 +00:00
if (this.rootTypes.includes(rootType)) {
2022-05-15 12:13:31 +00:00
continue;
}
delete accountTree[name];
}
2022-05-16 10:10:35 +00:00
/**
* Income Rows
*/
const incomeRoot = this.getRootNode(
AccountRootTypeEnum.Income,
accountTree
)!;
const incomeList = convertAccountRootNodeToAccountList(incomeRoot);
const incomeRows = this.getReportRowsFromAccountList(incomeList);
/**
* Expense Rows
*/
const expenseRoot = this.getRootNode(
AccountRootTypeEnum.Expense,
accountTree
)!;
const expenseList = convertAccountRootNodeToAccountList(expenseRoot);
const expenseRows = this.getReportRowsFromAccountList(expenseList);
this.reportData = await this.getReportDataFromRows(
incomeRows,
expenseRows,
2022-05-16 10:10:35 +00:00
incomeRoot,
expenseRoot
);
2022-05-18 14:58:35 +00:00
this.loading = false;
}
async getReportDataFromRows(
incomeRows: ReportData,
expenseRows: ReportData,
2022-05-16 10:10:35 +00:00
incomeRoot: AccountTreeNode,
expenseRoot: AccountTreeNode
): Promise<ReportData> {
if (!incomeRoot || !expenseRoot) {
return [];
}
const totalIncome = await this.getTotalNode(
2022-05-16 10:10:35 +00:00
incomeRoot,
t`Total Income (Credit)`
);
const totalExpense = await this.getTotalNode(
2022-05-16 10:10:35 +00:00
expenseRoot,
t`Total Expense (Debit)`
);
2022-05-16 13:01:58 +00:00
const totalValueMap: ValueMap = new Map();
for (const key of totalIncome.valueMap!.keys()) {
2022-05-16 13:01:58 +00:00
const income = totalIncome.valueMap!.get(key)?.balance ?? 0;
const expense = totalExpense.valueMap!.get(key)?.balance ?? 0;
totalValueMap.set(key, { balance: income - expense });
}
const totalProfit = {
name: t`Total Profit`,
valueMap: totalValueMap,
level: 0,
} as AccountListNode;
2022-05-16 07:49:01 +00:00
const totalIncomeRow = this.getRowFromAccountListNode(totalIncome);
const totalExpenseRow = this.getRowFromAccountListNode(totalExpense);
2022-05-16 07:49:01 +00:00
const totalProfitRow = this.getRowFromAccountListNode(totalProfit);
totalProfitRow.cells.forEach((c) => {
c.bold = true;
if (typeof c.rawValue !== 'number') {
return;
}
if (c.rawValue > 0) {
c.color = 'green';
} else if (c.rawValue < 0) {
c.color = 'red';
}
});
2022-05-16 10:10:35 +00:00
const emptyRow = this.getEmptyRow();
return [
incomeRows,
totalIncomeRow,
emptyRow,
expenseRows,
totalExpenseRow,
emptyRow,
totalProfitRow,
].flat() as ReportData;
}
}