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';
|
2022-05-14 08:46:05 +00:00
|
|
|
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-14 08:46:05 +00:00
|
|
|
|
2022-05-16 10:10:35 +00:00
|
|
|
export class ProfitAndLoss extends AccountReport {
|
2022-05-14 08:46:05 +00:00
|
|
|
static title = t`Profit And Loss`;
|
|
|
|
static reportName = 'profit-and-loss';
|
|
|
|
|
2022-05-16 10:10:35 +00:00
|
|
|
get rootTypes(): AccountRootType[] {
|
|
|
|
return [AccountRootTypeEnum.Income, AccountRootTypeEnum.Expense];
|
2022-05-16 07:49:01 +00:00
|
|
|
}
|
|
|
|
|
2022-05-16 10:10:35 +00:00
|
|
|
async setReportData(filter?: string) {
|
2022-05-16 13:01:58 +00:00
|
|
|
if (filter !== 'hideGroupAmounts') {
|
2022-05-16 10:10:35 +00:00
|
|
|
await this._setRawData();
|
2022-05-14 08:46:05 +00:00
|
|
|
}
|
2022-05-16 07:49:01 +00:00
|
|
|
|
2022-05-15 18:48:57 +00:00
|
|
|
const map = this._getGroupedMap(true, 'account');
|
2022-05-14 08:46:05 +00:00
|
|
|
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-15 18:48:57 +00:00
|
|
|
|
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);
|
2022-05-16 06:54:58 +00:00
|
|
|
|
|
|
|
this.reportData = await this.getReportDataFromRows(
|
|
|
|
incomeRows,
|
|
|
|
expenseRows,
|
2022-05-16 10:10:35 +00:00
|
|
|
incomeRoot,
|
|
|
|
expenseRoot
|
2022-05-16 06:54:58 +00:00
|
|
|
);
|
2022-05-14 08:46:05 +00:00
|
|
|
}
|
|
|
|
|
2022-05-16 06:54:58 +00:00
|
|
|
async getReportDataFromRows(
|
|
|
|
incomeRows: ReportData,
|
|
|
|
expenseRows: ReportData,
|
2022-05-16 10:10:35 +00:00
|
|
|
incomeRoot: AccountTreeNode,
|
|
|
|
expenseRoot: AccountTreeNode
|
2022-05-16 06:54:58 +00:00
|
|
|
): Promise<ReportData> {
|
|
|
|
const totalIncome = await this.getTotalNode(
|
2022-05-16 10:10:35 +00:00
|
|
|
incomeRoot,
|
2022-05-16 06:54:58 +00:00
|
|
|
t`Total Income (Credit)`
|
|
|
|
);
|
|
|
|
const totalExpense = await this.getTotalNode(
|
2022-05-16 10:10:35 +00:00
|
|
|
expenseRoot,
|
2022-05-16 06:54:58 +00:00
|
|
|
t`Total Expense (Debit)`
|
2022-05-15 18:48:57 +00:00
|
|
|
);
|
|
|
|
|
2022-05-16 13:01:58 +00:00
|
|
|
const totalValueMap: ValueMap = new Map();
|
2022-05-16 06:54:58 +00:00
|
|
|
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 });
|
2022-05-16 06:54:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 06:54:58 +00:00
|
|
|
|
2022-05-16 07:49:01 +00:00
|
|
|
const totalProfitRow = this.getRowFromAccountListNode(totalProfit);
|
2022-05-16 06:54:58 +00:00
|
|
|
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();
|
2022-05-16 06:54:58 +00:00
|
|
|
|
|
|
|
return [
|
|
|
|
incomeRows,
|
|
|
|
totalIncomeRow,
|
|
|
|
emptyRow,
|
|
|
|
expenseRows,
|
|
|
|
totalExpenseRow,
|
|
|
|
emptyRow,
|
|
|
|
totalProfitRow,
|
|
|
|
].flat() as ReportData;
|
|
|
|
}
|
|
|
|
}
|