mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 05:40:31 +00:00
refactor: balance sheet to nestjs
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
import { Response } from 'express';
|
||||
import { Controller, Headers, Query, Res } from '@nestjs/common';
|
||||
import { IBalanceSheetQuery } from './BalanceSheet.types';
|
||||
import { AcceptType } from '@/constants/accept-type';
|
||||
import { BalanceSheetApplication } from './BalanceSheetApplication';
|
||||
|
||||
@Controller('/reports/balance-sheet')
|
||||
export class BalanceSheetStatementController {
|
||||
constructor(private readonly balanceSheetApp: BalanceSheetApplication) {}
|
||||
|
||||
/**
|
||||
* Retrieve the balance sheet.
|
||||
* @param {IBalanceSheetQuery} query - Balance sheet query.
|
||||
* @param {Response} res - Response.
|
||||
* @param {string} acceptHeader - Accept header.
|
||||
*/
|
||||
public async balanceSheet(
|
||||
@Query() query: IBalanceSheetQuery,
|
||||
@Res() res: Response,
|
||||
@Headers('accept') acceptHeader: string,
|
||||
) {
|
||||
// Retrieves the json table format.
|
||||
if (acceptHeader.includes(AcceptType.ApplicationJsonTable)) {
|
||||
const table = await this.balanceSheetApp.table(query);
|
||||
|
||||
return res.status(200).send(table);
|
||||
// Retrieves the csv format.
|
||||
} else if (acceptHeader.includes(AcceptType.ApplicationCsv)) {
|
||||
const buffer = await this.balanceSheetApp.csv(query);
|
||||
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
|
||||
res.setHeader('Content-Type', 'text/csv');
|
||||
|
||||
return res.send(buffer);
|
||||
// Retrieves the xlsx format.
|
||||
} else if (acceptHeader.includes(AcceptType.ApplicationXlsx)) {
|
||||
const buffer = await this.balanceSheetApp.xlsx(query);
|
||||
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
|
||||
res.setHeader(
|
||||
'Content-Type',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
);
|
||||
return res.send(buffer);
|
||||
// Retrieves the pdf format.
|
||||
} else if (acceptHeader.includes(AcceptType.ApplicationPdf)) {
|
||||
const pdfContent = await this.balanceSheetApp.pdf(query);
|
||||
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
res.send(pdfContent);
|
||||
} else {
|
||||
const sheet = await this.balanceSheetApp.sheet(query);
|
||||
|
||||
return res.status(200).send(sheet);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { BalanceSheetInjectable } from './BalanceSheetInjectable';
|
||||
import { BalanceSheetApplication } from './BalanceSheetApplication';
|
||||
import { BalanceSheetTableInjectable } from './BalanceSheetTableInjectable';
|
||||
import { BalanceSheetExportInjectable } from './BalanceSheetExportInjectable';
|
||||
import { BalanceSheetMetaInjectable } from './BalanceSheetMeta';
|
||||
import { BalanceSheetPdfInjectable } from './BalanceSheetPdfInjectable';
|
||||
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
||||
import { FinancialSheetCommonModule } from '../../common/FinancialSheetCommon.module';
|
||||
import { BalanceSheetStatementController } from './BalanceSheet.controller';
|
||||
import { BalanceSheetRepository } from './BalanceSheetRepository';
|
||||
import { AccountsModule } from '@/modules/Accounts/Accounts.module';
|
||||
|
||||
@Module({
|
||||
imports: [FinancialSheetCommonModule, AccountsModule],
|
||||
controllers: [BalanceSheetStatementController],
|
||||
providers: [
|
||||
BalanceSheetRepository,
|
||||
BalanceSheetInjectable,
|
||||
BalanceSheetTableInjectable,
|
||||
BalanceSheetExportInjectable,
|
||||
BalanceSheetMetaInjectable,
|
||||
BalanceSheetApplication,
|
||||
BalanceSheetPdfInjectable,
|
||||
TenancyContext,
|
||||
],
|
||||
exports: [BalanceSheetInjectable],
|
||||
})
|
||||
export class BalanceSheetModule {}
|
||||
@@ -0,0 +1,114 @@
|
||||
import * as R from 'ramda';
|
||||
import { I18nService } from 'nestjs-i18n';
|
||||
import {
|
||||
IBalanceSheetQuery,
|
||||
IBalanceSheetSchemaNode,
|
||||
IBalanceSheetDataNode,
|
||||
} from './BalanceSheet.types';
|
||||
import { BalanceSheetSchema } from './BalanceSheetSchema';
|
||||
import { BalanceSheetPercentage } from './BalanceSheetPercentage';
|
||||
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
|
||||
import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear';
|
||||
import { BalanceSheetDatePeriods } from './BalanceSheetDatePeriods';
|
||||
import { BalanceSheetBase } from './BalanceSheetBase';
|
||||
import { FinancialSheetStructure } from '../../common/FinancialSheetStructure';
|
||||
import { BalanceSheetRepository } from './BalanceSheetRepository';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { BalanceSheetFiltering } from './BalanceSheetFiltering';
|
||||
import { BalanceSheetNetIncome } from './BalanceSheetNetIncome';
|
||||
import { BalanceSheetAggregators } from './BalanceSheetAggregators';
|
||||
import { BalanceSheetAccounts } from './BalanceSheetAccounts';
|
||||
import { INumberFormatQuery } from '../../types/Report.types';
|
||||
import { FinancialSheet } from '../../common/FinancialSheet';
|
||||
|
||||
export class BalanceSheet extends R.pipe(
|
||||
BalanceSheetAggregators,
|
||||
BalanceSheetAccounts,
|
||||
BalanceSheetNetIncome,
|
||||
BalanceSheetFiltering,
|
||||
BalanceSheetDatePeriods,
|
||||
BalanceSheetComparsionPreviousPeriod,
|
||||
BalanceSheetComparsionPreviousYear,
|
||||
BalanceSheetPercentage,
|
||||
BalanceSheetSchema,
|
||||
BalanceSheetBase,
|
||||
FinancialSheetStructure,
|
||||
)(FinancialSheet) {
|
||||
/**
|
||||
* Balance sheet query.
|
||||
* @param {BalanceSheetQuery}
|
||||
*/
|
||||
readonly query: BalanceSheetQuery;
|
||||
|
||||
/**
|
||||
* Balance sheet number format query.
|
||||
* @param {INumberFormatQuery}
|
||||
*/
|
||||
readonly numberFormat: INumberFormatQuery;
|
||||
|
||||
/**
|
||||
* Base currency of the organization.
|
||||
* @param {string}
|
||||
*/
|
||||
readonly baseCurrency: string;
|
||||
|
||||
/**
|
||||
* Localization.
|
||||
*/
|
||||
readonly i18n: I18nService;
|
||||
|
||||
/**
|
||||
* Balance sheet repository.
|
||||
*/
|
||||
readonly repository: BalanceSheetRepository;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {IBalanceSheetQuery} query -
|
||||
* @param {IAccount[]} accounts -
|
||||
* @param {string} baseCurrency -
|
||||
*/
|
||||
constructor(
|
||||
query: IBalanceSheetQuery,
|
||||
repository: BalanceSheetRepository,
|
||||
baseCurrency: string,
|
||||
i18n: I18nService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.query = new BalanceSheetQuery(query);
|
||||
this.repository = repository;
|
||||
this.baseCurrency = baseCurrency;
|
||||
this.numberFormat = this.query.query.numberFormat;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses report schema nodes.
|
||||
* @param {IBalanceSheetSchemaNode[]} schema
|
||||
* @returns {IBalanceSheetDataNode[]}
|
||||
*/
|
||||
public parseSchemaNodes = (
|
||||
schema: IBalanceSheetSchemaNode[],
|
||||
): IBalanceSheetDataNode[] => {
|
||||
return R.compose(
|
||||
this.aggregatesSchemaParser,
|
||||
this.netIncomeSchemaParser,
|
||||
this.accountsSchemaParser,
|
||||
)(schema) as IBalanceSheetDataNode[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the report statement data.
|
||||
* @returns {IBalanceSheetDataNode[]}
|
||||
*/
|
||||
public reportData = () => {
|
||||
const balanceSheetSchema = this.getSchema();
|
||||
|
||||
return R.compose(
|
||||
this.reportFilterPlugin,
|
||||
this.reportPercentageCompose,
|
||||
this.parseSchemaNodes,
|
||||
)(balanceSheetSchema);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
import {
|
||||
IFinancialSheetBranchesQuery,
|
||||
IFinancialSheetCommonMeta,
|
||||
IFormatNumberSettings,
|
||||
INumberFormatQuery,
|
||||
} from '../../types/Report.types';
|
||||
import { IFinancialTable } from '../../types/Table.types';
|
||||
|
||||
// Balance sheet schema nodes types.
|
||||
export enum BALANCE_SHEET_SCHEMA_NODE_TYPE {
|
||||
AGGREGATE = 'AGGREGATE',
|
||||
ACCOUNTS = 'ACCOUNTS',
|
||||
ACCOUNT = 'ACCOUNT',
|
||||
NET_INCOME = 'NET_INCOME',
|
||||
}
|
||||
|
||||
export enum BALANCE_SHEET_NODE_TYPE {
|
||||
AGGREGATE = 'AGGREGATE',
|
||||
ACCOUNTS = 'ACCOUNTS',
|
||||
ACCOUNT = 'ACCOUNT',
|
||||
}
|
||||
|
||||
// Balance sheet schema nodes ids.
|
||||
export enum BALANCE_SHEET_SCHEMA_NODE_ID {
|
||||
ASSETS = 'ASSETS',
|
||||
CURRENT_ASSETS = 'CURRENT_ASSETS',
|
||||
CASH_EQUIVALENTS = 'CASH_EQUIVALENTS',
|
||||
ACCOUNTS_RECEIVABLE = 'ACCOUNTS_RECEIVABLE',
|
||||
NON_CURRENT_ASSET = 'NON_CURRENT_ASSET',
|
||||
FIXED_ASSET = 'FIXED_ASSET',
|
||||
OTHER_CURRENT_ASSET = 'OTHER_CURRENT_ASSET',
|
||||
INVENTORY = 'INVENTORY',
|
||||
LIABILITY_EQUITY = 'LIABILITY_EQUITY',
|
||||
LIABILITY = 'LIABILITY',
|
||||
CURRENT_LIABILITY = 'CURRENT_LIABILITY',
|
||||
LOGN_TERM_LIABILITY = 'LOGN_TERM_LIABILITY',
|
||||
NON_CURRENT_LIABILITY = 'NON_CURRENT_LIABILITY',
|
||||
EQUITY = 'EQUITY',
|
||||
NET_INCOME = 'NET_INCOME',
|
||||
}
|
||||
|
||||
// Balance sheet query.
|
||||
export interface IBalanceSheetQuery extends IFinancialSheetBranchesQuery {
|
||||
displayColumnsType: 'total' | 'date_periods';
|
||||
displayColumnsBy: string;
|
||||
|
||||
fromDate: string;
|
||||
toDate: string;
|
||||
|
||||
numberFormat: INumberFormatQuery;
|
||||
noneTransactions: boolean;
|
||||
noneZero: boolean;
|
||||
basis: 'cash' | 'accrual';
|
||||
accountIds: number[];
|
||||
|
||||
percentageOfColumn: boolean;
|
||||
percentageOfRow: boolean;
|
||||
|
||||
previousPeriod: boolean;
|
||||
previousPeriodAmountChange: boolean;
|
||||
previousPeriodPercentageChange: boolean;
|
||||
|
||||
previousYear: boolean;
|
||||
previousYearAmountChange: boolean;
|
||||
previousYearPercentageChange: boolean;
|
||||
}
|
||||
|
||||
// Balance sheet meta.
|
||||
export interface IBalanceSheetMeta extends IFinancialSheetCommonMeta {
|
||||
formattedAsDate: string;
|
||||
formattedDateRange: string;
|
||||
}
|
||||
|
||||
export interface IBalanceSheetFormatNumberSettings
|
||||
extends IFormatNumberSettings {
|
||||
type: string;
|
||||
}
|
||||
|
||||
// Balance sheet service.
|
||||
export interface IBalanceSheetStatementService {
|
||||
balanceSheet(
|
||||
tenantId: number,
|
||||
query: IBalanceSheetQuery,
|
||||
): Promise<IBalanceSheetDOO>;
|
||||
}
|
||||
|
||||
export type IBalanceSheetStatementData = IBalanceSheetDataNode[];
|
||||
|
||||
export interface IBalanceSheetDOO {
|
||||
query: IBalanceSheetQuery;
|
||||
data: IBalanceSheetStatementData;
|
||||
meta: IBalanceSheetMeta;
|
||||
}
|
||||
|
||||
export interface IBalanceSheetCommonNode {
|
||||
total: IBalanceSheetTotal;
|
||||
horizontalTotals?: IBalanceSheetTotal[];
|
||||
|
||||
percentageRow?: IBalanceSheetPercentageAmount;
|
||||
percentageColumn?: IBalanceSheetPercentageAmount;
|
||||
|
||||
previousPeriod?: IBalanceSheetTotal;
|
||||
previousPeriodChange?: IBalanceSheetTotal;
|
||||
previousPeriodPercentage?: IBalanceSheetPercentageAmount;
|
||||
|
||||
previousYear?: IBalanceSheetTotal;
|
||||
previousYearChange?: IBalanceSheetTotal;
|
||||
previousYearPercentage?: IBalanceSheetPercentageAmount;
|
||||
}
|
||||
|
||||
export interface IBalanceSheetAggregateNode extends IBalanceSheetCommonNode {
|
||||
id: string;
|
||||
name: string;
|
||||
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE;
|
||||
children?: IBalanceSheetDataNode[];
|
||||
}
|
||||
|
||||
export interface IBalanceSheetTotal {
|
||||
amount: number;
|
||||
formattedAmount: string;
|
||||
currencyCode: string;
|
||||
date?: string | Date;
|
||||
}
|
||||
|
||||
export interface IBalanceSheetAccountsNode extends IBalanceSheetCommonNode {
|
||||
id: number | string;
|
||||
name: string;
|
||||
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS;
|
||||
children: IBalanceSheetAccountNode[];
|
||||
}
|
||||
|
||||
export interface IBalanceSheetAccountNode extends IBalanceSheetCommonNode {
|
||||
id: number;
|
||||
index: number;
|
||||
name: string;
|
||||
code: string;
|
||||
parentAccountId?: number;
|
||||
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNT;
|
||||
children?: IBalanceSheetAccountNode[];
|
||||
}
|
||||
|
||||
export interface IBalanceSheetNetIncomeNode extends IBalanceSheetCommonNode {
|
||||
id: number;
|
||||
name: string;
|
||||
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.NET_INCOME;
|
||||
}
|
||||
|
||||
export type IBalanceSheetDataNode =
|
||||
| IBalanceSheetAggregateNode
|
||||
| IBalanceSheetAccountNode
|
||||
| IBalanceSheetAccountsNode
|
||||
| IBalanceSheetNetIncomeNode;
|
||||
|
||||
export interface IBalanceSheetPercentageAmount {
|
||||
amount: number;
|
||||
formattedAmount: string;
|
||||
}
|
||||
|
||||
export interface IBalanceSheetSchemaAggregateNode {
|
||||
name: string;
|
||||
id: string;
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE;
|
||||
children: IBalanceSheetSchemaNode[];
|
||||
alwaysShow: boolean;
|
||||
}
|
||||
|
||||
export interface IBalanceSheetSchemaAccountNode {
|
||||
name: string;
|
||||
id: string;
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE;
|
||||
accountsTypes: string[];
|
||||
}
|
||||
|
||||
export interface IBalanceSheetSchemaNetIncomeNode {
|
||||
id: string;
|
||||
name: string;
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE;
|
||||
}
|
||||
|
||||
export type IBalanceSheetSchemaNode =
|
||||
| IBalanceSheetSchemaAccountNode
|
||||
| IBalanceSheetSchemaAggregateNode
|
||||
| IBalanceSheetSchemaNetIncomeNode;
|
||||
|
||||
export interface IBalanceSheetDatePeriods {
|
||||
assocAccountNodeDatePeriods(node): any;
|
||||
initDateRangeCollection(): void;
|
||||
}
|
||||
|
||||
export interface IBalanceSheetComparsions {
|
||||
assocPreviousYearAccountNode(node);
|
||||
hasPreviousPeriod(): boolean;
|
||||
hasPreviousYear(): boolean;
|
||||
assocPreviousPeriodAccountNode(node);
|
||||
}
|
||||
|
||||
export interface IBalanceSheetTotalPeriod extends IFinancialSheetTotalPeriod {
|
||||
percentageRow?: IBalanceSheetPercentageAmount;
|
||||
percentageColumn?: IBalanceSheetPercentageAmount;
|
||||
}
|
||||
|
||||
export interface IFinancialSheetTotalPeriod {
|
||||
fromDate: any;
|
||||
toDate: any;
|
||||
total: any;
|
||||
}
|
||||
|
||||
export enum IFinancialDatePeriodsUnit {
|
||||
Day = 'day',
|
||||
Month = 'month',
|
||||
Year = 'year',
|
||||
}
|
||||
|
||||
export enum IAccountTransactionsGroupBy {
|
||||
Quarter = 'quarter',
|
||||
Year = 'year',
|
||||
Day = 'day',
|
||||
Month = 'month',
|
||||
Week = 'week',
|
||||
}
|
||||
|
||||
export interface IBalanceSheetTable extends IFinancialTable {
|
||||
meta: IBalanceSheetMeta;
|
||||
query: IBalanceSheetQuery;
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
import * as R from 'ramda';
|
||||
import { defaultTo, toArray } from 'lodash';
|
||||
import { FinancialSheetStructure } from '../../common/FinancialSheetStructure';
|
||||
import {
|
||||
BALANCE_SHEET_SCHEMA_NODE_TYPE,
|
||||
IBalanceSheetAccountNode,
|
||||
IBalanceSheetAccountsNode,
|
||||
IBalanceSheetDataNode,
|
||||
IBalanceSheetSchemaAccountNode,
|
||||
IBalanceSheetSchemaNode,
|
||||
} from './BalanceSheet.types';
|
||||
import { BalanceSheetNetIncome } from './BalanceSheetNetIncome';
|
||||
import { BalanceSheetFiltering } from './BalanceSheetFiltering';
|
||||
import { BalanceSheetDatePeriods } from './BalanceSheetDatePeriods';
|
||||
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
|
||||
import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear';
|
||||
import { BalanceSheetPercentage } from './BalanceSheetPercentage';
|
||||
import { BalanceSheetSchema } from './BalanceSheetSchema';
|
||||
import { BalanceSheetBase } from './BalanceSheetBase';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { BalanceSheetRepository } from './BalanceSheetRepository';
|
||||
import { GConstructor } from '@/common/types/Constructor';
|
||||
import { INumberFormatQuery } from '../../types/Report.types';
|
||||
import { Account } from '@/modules/Accounts/models/Account.model';
|
||||
import { flatToNestedArray } from '@/utils/flat-to-nested-array';
|
||||
import { FinancialSheet } from '../../common/FinancialSheet';
|
||||
|
||||
export const BalanceSheetAccounts = <T extends GConstructor<FinancialSheet>>(
|
||||
Base: T,
|
||||
) =>
|
||||
class extends R.pipe(
|
||||
BalanceSheetNetIncome,
|
||||
BalanceSheetFiltering,
|
||||
BalanceSheetDatePeriods,
|
||||
BalanceSheetComparsionPreviousPeriod,
|
||||
BalanceSheetComparsionPreviousYear,
|
||||
BalanceSheetPercentage,
|
||||
BalanceSheetSchema,
|
||||
BalanceSheetBase,
|
||||
FinancialSheetStructure,
|
||||
)(Base) {
|
||||
/**
|
||||
* Balance sheet query.
|
||||
* @param {BalanceSheetQuery}
|
||||
*/
|
||||
readonly query: BalanceSheetQuery;
|
||||
|
||||
/**
|
||||
* Balance sheet number format query.
|
||||
* @param {INumberFormatQuery}
|
||||
*/
|
||||
readonly numberFormat: INumberFormatQuery;
|
||||
|
||||
/**
|
||||
* Base currency of the organization.
|
||||
* @param {string}
|
||||
*/
|
||||
readonly baseCurrency: string;
|
||||
|
||||
/**
|
||||
* Localization.
|
||||
*/
|
||||
readonly i18n: any;
|
||||
|
||||
/**
|
||||
* Balance sheet repository.
|
||||
*/
|
||||
readonly repository: BalanceSheetRepository;
|
||||
|
||||
/**
|
||||
* Retrieve the accounts node of accounts types.
|
||||
* @param {string} accountsTypes
|
||||
* @returns {IAccount[]}
|
||||
*/
|
||||
private getAccountsByAccountTypes = (
|
||||
accountsTypes: string[],
|
||||
): Account[] => {
|
||||
const mapAccountsByTypes = R.map((accountType) =>
|
||||
defaultTo(this.repository.accountsByType.get(accountType), []),
|
||||
);
|
||||
return R.compose(R.flatten, mapAccountsByTypes)(accountsTypes);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the account model to report account node.
|
||||
* @param {Account} account
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
private reportSchemaAccountNodeMapper = (
|
||||
account: Account,
|
||||
): IBalanceSheetAccountNode => {
|
||||
const childrenAccountsIds = this.repository.accountsGraph.dependenciesOf(
|
||||
account.id,
|
||||
);
|
||||
const accountIds = R.uniq(R.append(account.id, childrenAccountsIds));
|
||||
const total = this.repository.totalAccountsLedger
|
||||
.whereAccountsIds(accountIds)
|
||||
.getClosingBalance();
|
||||
|
||||
return {
|
||||
id: account.id,
|
||||
index: account.index,
|
||||
name: account.name,
|
||||
code: account.code,
|
||||
total: this.getAmountMeta(total),
|
||||
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNT,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the given account model to the balance sheet account node.
|
||||
* @param {IAccount} account
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
private reportSchemaAccountNodeComposer = (
|
||||
account: Account,
|
||||
): IBalanceSheetAccountNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.previousYearAccountNodeComposer,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.previousPeriodAccountNodeComposer,
|
||||
),
|
||||
R.when(
|
||||
this.query.isDatePeriodsColumnsType,
|
||||
this.assocAccountNodeDatePeriods,
|
||||
),
|
||||
this.reportSchemaAccountNodeMapper,
|
||||
)(account);
|
||||
};
|
||||
|
||||
// -----------------------------
|
||||
// - Accounts Node Praser
|
||||
// -----------------------------
|
||||
/**
|
||||
* Retrieve the report accounts node by the given accounts types.
|
||||
* @param {string[]} accountsTypes
|
||||
* @returns {IBalanceSheetAccountNode[]}
|
||||
*/
|
||||
private getAccountsNodesByAccountTypes = (
|
||||
accountsTypes: string[],
|
||||
): IBalanceSheetAccountNode[] => {
|
||||
// Retrieves accounts from the given defined node account types.
|
||||
const accounts = this.getAccountsByAccountTypes(accountsTypes);
|
||||
|
||||
// Converts the flatten accounts to tree.
|
||||
const accountsTree = flatToNestedArray(accounts, {
|
||||
id: 'id',
|
||||
parentId: 'parentAccountId',
|
||||
});
|
||||
// Maps over the accounts tree.
|
||||
return this.mapNodesDeep(
|
||||
accountsTree,
|
||||
this.reportSchemaAccountNodeComposer,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the accounts schema node type.
|
||||
* @param {IBalanceSheetSchemaNode} node - Schema node.
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
private reportSchemaAccountsNodeMapper = (
|
||||
node: IBalanceSheetSchemaAccountNode,
|
||||
): IBalanceSheetAccountsNode => {
|
||||
const accounts = this.getAccountsNodesByAccountTypes(node.accountsTypes);
|
||||
const children = toArray(node?.children);
|
||||
|
||||
return {
|
||||
id: node.id,
|
||||
name: this.i18n.__(node.name),
|
||||
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
children: [...accounts, ...children],
|
||||
total: this.getTotalAmountMeta(0),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the given report schema node.
|
||||
* @param {IBalanceSheetSchemaNode | IBalanceSheetDataNode} node - Schema node.
|
||||
* @return {IBalanceSheetSchemaNode | IBalanceSheetDataNode}
|
||||
*/
|
||||
private reportAccountSchemaParser = (
|
||||
node: IBalanceSheetSchemaNode | IBalanceSheetDataNode,
|
||||
): IBalanceSheetSchemaNode | IBalanceSheetDataNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS),
|
||||
this.reportSchemaAccountsNodeMapper,
|
||||
),
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses the report accounts schema nodes.
|
||||
* @param {IBalanceSheetSchemaNode[]} nodes -
|
||||
* @return {IBalanceSheetStructureSection[]}
|
||||
*/
|
||||
public accountsSchemaParser = (
|
||||
nodes: (IBalanceSheetSchemaNode | IBalanceSheetDataNode)[],
|
||||
): (IBalanceSheetDataNode | IBalanceSheetSchemaNode)[] => {
|
||||
return this.mapNodesDeepReverse(nodes, this.reportAccountSchemaParser);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,144 @@
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
BALANCE_SHEET_SCHEMA_NODE_TYPE,
|
||||
IBalanceSheetAggregateNode,
|
||||
IBalanceSheetDataNode,
|
||||
IBalanceSheetSchemaAggregateNode,
|
||||
IBalanceSheetSchemaNode,
|
||||
} from './BalanceSheet.types';
|
||||
import { BalanceSheetDatePeriods } from './BalanceSheetDatePeriods';
|
||||
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
|
||||
import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear';
|
||||
import { BalanceSheetPercentage } from './BalanceSheetPercentage';
|
||||
import { BalanceSheetSchema } from './BalanceSheetSchema';
|
||||
import { BalanceSheetBase } from './BalanceSheetBase';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { FinancialSheetStructure } from '../../common/FinancialSheetStructure';
|
||||
import { GConstructor } from '@/common/types/Constructor';
|
||||
import { INumberFormatQuery } from '../../types/Report.types';
|
||||
import { FinancialSheet } from '../../common/FinancialSheet';
|
||||
|
||||
export const BalanceSheetAggregators = <T extends GConstructor<FinancialSheet>>(
|
||||
Base: T,
|
||||
) =>
|
||||
class extends R.pipe(
|
||||
BalanceSheetDatePeriods,
|
||||
BalanceSheetComparsionPreviousPeriod,
|
||||
BalanceSheetComparsionPreviousYear,
|
||||
BalanceSheetPercentage,
|
||||
BalanceSheetSchema,
|
||||
BalanceSheetBase,
|
||||
FinancialSheetStructure,
|
||||
)(Base) {
|
||||
/**
|
||||
* Balance sheet query.
|
||||
* @param {BalanceSheetQuery}
|
||||
*/
|
||||
public readonly query: BalanceSheetQuery;
|
||||
|
||||
/**
|
||||
* Balance sheet number format query.
|
||||
* @param {INumberFormatQuery}
|
||||
*/
|
||||
readonly numberFormat: INumberFormatQuery;
|
||||
|
||||
/**
|
||||
* Base currency of the organization.
|
||||
* @param {string}
|
||||
*/
|
||||
readonly baseCurrency: string;
|
||||
|
||||
/**
|
||||
* Localization.
|
||||
*/
|
||||
readonly i18n: any;
|
||||
|
||||
/**
|
||||
* Sets total amount that calculated from node children.
|
||||
* @param {IBalanceSheetSection} node
|
||||
* @returns {IBalanceSheetDataNode}
|
||||
*/
|
||||
public aggregateNodeTotalMapper = (
|
||||
node: IBalanceSheetDataNode,
|
||||
): IBalanceSheetDataNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.previousYearAggregateNodeComposer,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.previousPeriodAggregateNodeComposer,
|
||||
),
|
||||
R.when(
|
||||
this.query.isDatePeriodsColumnsType,
|
||||
this.assocAggregateNodeDatePeriods,
|
||||
),
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the aggregate schema node type.
|
||||
* @param {IBalanceSheetSchemaAggregateNode} node - Schema node.
|
||||
* @return {IBalanceSheetAggregateNode}
|
||||
*/
|
||||
public reportSchemaAggregateNodeMapper = (
|
||||
node: IBalanceSheetSchemaAggregateNode,
|
||||
): IBalanceSheetAggregateNode => {
|
||||
const total = this.getTotalOfNodes(node.children);
|
||||
|
||||
return {
|
||||
name: this.i18n.__(node.name),
|
||||
id: node.id,
|
||||
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE,
|
||||
total: this.getTotalAmountMeta(total),
|
||||
children: node.children,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Compose shema aggregate node of balance sheet schema.
|
||||
* @param {IBalanceSheetSchemaAggregateNode} node
|
||||
* @returns {IBalanceSheetSchemaAggregateNode}
|
||||
*/
|
||||
public schemaAggregateNodeCompose = (
|
||||
node: IBalanceSheetSchemaAggregateNode,
|
||||
) => {
|
||||
return R.compose(
|
||||
this.aggregateNodeTotalMapper,
|
||||
this.reportSchemaAggregateNodeMapper,
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the given report schema node.
|
||||
* @param {IBalanceSheetSchemaNode} node - Schema node.
|
||||
* @return {IBalanceSheetDataNode}
|
||||
*/
|
||||
public reportAggregateSchemaParser = (
|
||||
node: IBalanceSheetSchemaNode,
|
||||
): IBalanceSheetDataNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE),
|
||||
this.schemaAggregateNodeCompose,
|
||||
),
|
||||
R.when(
|
||||
this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS),
|
||||
this.schemaAggregateNodeCompose,
|
||||
),
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the report schema nodes.
|
||||
* @param {IBalanceSheetSchemaNode[]} nodes -
|
||||
* @return {IBalanceSheetStructureSection[]}
|
||||
*/
|
||||
public aggregatesSchemaParser = (
|
||||
nodes: (IBalanceSheetSchemaNode | IBalanceSheetDataNode)[],
|
||||
): (IBalanceSheetDataNode | IBalanceSheetSchemaNode)[] => {
|
||||
return this.mapNodesDeepReverse(nodes, this.reportAggregateSchemaParser);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { IBalanceSheetQuery } from './BalanceSheet.types';
|
||||
import { BalanceSheetExportInjectable } from './BalanceSheetExportInjectable';
|
||||
import { BalanceSheetTableInjectable } from './BalanceSheetTableInjectable';
|
||||
import { BalanceSheetInjectable } from './BalanceSheetInjectable';
|
||||
|
||||
@Injectable()
|
||||
export class BalanceSheetApplication {
|
||||
/**
|
||||
* @param {BalanceSheetExportInjectable} balanceSheetExportService - The balance sheet export service.
|
||||
* @param {BalanceSheetTableInjectable} balanceSheetTableService - The balance sheet table service.
|
||||
* @param {BalanceSheetInjectable} balanceSheetService - The balance sheet service.
|
||||
*/
|
||||
constructor(
|
||||
private readonly balanceSheetExportService: BalanceSheetExportInjectable,
|
||||
private readonly balanceSheetTableService: BalanceSheetTableInjectable,
|
||||
private readonly balanceSheetService: BalanceSheetInjectable,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieves the balnace sheet in json format.
|
||||
* @param {IBalanceSheetQuery} query
|
||||
* @returns {Promise<IBalanceSheetStatement>}
|
||||
*/
|
||||
public sheet(query: IBalanceSheetQuery) {
|
||||
return this.balanceSheetService.balanceSheet(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the balance sheet in table format.
|
||||
* @param {IBalanceSheetQuery} query
|
||||
* @returns {Promise<IBalanceSheetTable>}
|
||||
*/
|
||||
public table(query: IBalanceSheetQuery) {
|
||||
return this.balanceSheetTableService.table(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the balance sheet in XLSX format.
|
||||
* @param {IBalanceSheetQuery} query
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
public xlsx(query: IBalanceSheetQuery) {
|
||||
return this.balanceSheetExportService.xlsx(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the balance sheet in CSV format.
|
||||
* @param {IBalanceSheetQuery} query
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
public csv(query: IBalanceSheetQuery): Promise<string> {
|
||||
return this.balanceSheetExportService.csv(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the balance sheet in pdf format.
|
||||
* @param {IBalanceSheetQuery} query
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
public pdf(query: IBalanceSheetQuery) {
|
||||
return this.balanceSheetExportService.pdf(query);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
IBalanceSheetDataNode,
|
||||
IBalanceSheetSchemaNode,
|
||||
} from './BalanceSheet.types';
|
||||
import { GConstructor } from '@/common/types/Constructor';
|
||||
import { FinancialSheet } from '../../common/FinancialSheet';
|
||||
|
||||
export const BalanceSheetBase = <T extends GConstructor<FinancialSheet>>(
|
||||
Base: T,
|
||||
) =>
|
||||
class BalanceSheetBase extends Base {
|
||||
/**
|
||||
* Detarmines the node type of the given schema node.
|
||||
* @param {IBalanceSheetStructureSection} node -
|
||||
* @param {string} type -
|
||||
* @return {boolean}
|
||||
*/
|
||||
public isSchemaNodeType = R.curry(
|
||||
(type: string, node: IBalanceSheetSchemaNode): boolean => {
|
||||
return node.type === type;
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Detarmines the node type of the given schema node.
|
||||
* @param {IBalanceSheetStructureSection} node -
|
||||
* @param {string} type -
|
||||
* @return {boolean}
|
||||
*/
|
||||
public isNodeType = R.curry(
|
||||
(type: string, node: IBalanceSheetDataNode): boolean => {
|
||||
return node.nodeType === type;
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Detarmines the given display columns by type.
|
||||
* @param {string} displayColumnsBy
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isDisplayColumnsBy = (displayColumnsBy: string): boolean => {
|
||||
return this.query.displayColumnsType === displayColumnsBy;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,273 @@
|
||||
import * as R from 'ramda';
|
||||
import { sumBy } from 'lodash';
|
||||
import {
|
||||
IBalanceSheetAccountNode,
|
||||
IBalanceSheetDataNode,
|
||||
IBalanceSheetAggregateNode,
|
||||
IBalanceSheetTotal,
|
||||
IBalanceSheetCommonNode,
|
||||
} from './BalanceSheet.types';
|
||||
import { FinancialPreviousPeriod } from '../../common/FinancialPreviousPeriod';
|
||||
import { FinancialHorizTotals } from '../../common/FinancialHorizTotals';
|
||||
import { GConstructor } from '@/common/types/Constructor';
|
||||
import { FinancialSheet } from '../../common/FinancialSheet';
|
||||
|
||||
export const BalanceSheetComparsionPreviousPeriod = <
|
||||
T extends GConstructor<FinancialSheet>,
|
||||
>(
|
||||
Base: T,
|
||||
) =>
|
||||
class BalanceSheetComparsionPreviousPeriod extends R.pipe(
|
||||
FinancialHorizTotals,
|
||||
FinancialPreviousPeriod,
|
||||
)(Base) {
|
||||
// ------------------------------
|
||||
// # Account
|
||||
// ------------------------------
|
||||
/**
|
||||
* Associates the previous period to account node.
|
||||
* @param {IBalanceSheetDataNode} node
|
||||
* @returns {IBalanceSheetDataNode}
|
||||
*/
|
||||
public assocPreviousPeriodAccountNode = (
|
||||
node: IBalanceSheetDataNode,
|
||||
): IBalanceSheetDataNode => {
|
||||
const total = this.repository.PPTotalAccountsLedger.whereAccountId(
|
||||
node.id,
|
||||
).getClosingBalance();
|
||||
|
||||
return R.assoc('previousPeriod', this.getAmountMeta(total), node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Previous period account node composer.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
public previousPeriodAccountNodeComposer = (
|
||||
node: IBalanceSheetAccountNode,
|
||||
): IBalanceSheetAccountNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizTotals,
|
||||
this.assocPreivousPeriodAccountHorizNodeComposer,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
this.assocPreviousPeriodPercentageNode,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
this.assocPreviousPeriodChangeNode,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.assocPreviousPeriodAccountNode,
|
||||
),
|
||||
)(node);
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
// # Aggregate
|
||||
// ------------------------------
|
||||
/**
|
||||
* Assoc previous period total to aggregate node.
|
||||
* @param {IBalanceSheetAggregateNode} node
|
||||
* @returns {IBalanceSheetAggregateNode}
|
||||
*/
|
||||
public assocPreviousPeriodAggregateNode = (
|
||||
node: IBalanceSheetAggregateNode,
|
||||
): IBalanceSheetAggregateNode => {
|
||||
const total = sumBy(node.children, 'previousYear.amount');
|
||||
|
||||
return R.assoc('previousPeriod', this.getTotalAmountMeta(total), node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Previous period aggregate node composer.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
public previousPeriodAggregateNodeComposer = (
|
||||
node: IBalanceSheetAccountNode,
|
||||
): IBalanceSheetAccountNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizTotals,
|
||||
this.assocPreviousPeriodAggregateHorizNode,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
this.assocPreviousPeriodTotalPercentageNode,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
this.assocPreviousPeriodTotalChangeNode,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.assocPreviousPeriodAggregateNode,
|
||||
),
|
||||
)(node);
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
// # Horizontal Nodes - Account.
|
||||
// ------------------------------
|
||||
/**
|
||||
* Retrieve the given account total in the given period.
|
||||
* @param {number} accountId - Account id.
|
||||
* @param {Date} fromDate - From date.
|
||||
* @param {Date} toDate - To date.
|
||||
* @returns {number}
|
||||
*/
|
||||
private getAccountPPDatePeriodTotal = R.curry(
|
||||
(accountId: number, fromDate: Date, toDate: Date): number => {
|
||||
const PPPeriodsTotal =
|
||||
this.repository.PPPeriodsAccountsLedger.whereAccountId(accountId)
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
const PPPeriodsOpeningTotal =
|
||||
this.repository.PPPeriodsOpeningAccountLedger.whereAccountId(
|
||||
accountId,
|
||||
).getClosingBalance();
|
||||
|
||||
return PPPeriodsOpeningTotal + PPPeriodsTotal;
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Assoc preivous period to account horizontal total node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {}
|
||||
*/
|
||||
private assocPreviousPeriodAccountHorizTotal = R.curry(
|
||||
(node: IBalanceSheetAccountNode, totalNode) => {
|
||||
const total = this.getAccountPPDatePeriodTotal(
|
||||
node.id,
|
||||
totalNode.previousPeriodFromDate.date,
|
||||
totalNode.previousPeriodToDate.date,
|
||||
);
|
||||
return R.assoc('previousPeriod', this.getAmountMeta(total), totalNode);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Previous year account horizontal node composer.
|
||||
* @param {IBalanceSheetAccountNode} node -
|
||||
* @param {IBalanceSheetTotal}
|
||||
* @returns {IBalanceSheetTotal}
|
||||
*/
|
||||
private previousPeriodAccountHorizNodeCompose = R.curry(
|
||||
(
|
||||
node: IBalanceSheetAccountNode,
|
||||
horizontalTotalNode: IBalanceSheetTotal,
|
||||
): IBalanceSheetTotal => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
this.assocPreviousPeriodPercentageNode,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
this.assocPreviousPeriodChangeNode,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.assocPreviousPeriodAccountHorizTotal(node),
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.assocPreviousPeriodHorizNodeFromToDates(
|
||||
this.query.displayColumnsBy,
|
||||
),
|
||||
),
|
||||
)(horizontalTotalNode);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns
|
||||
*/
|
||||
private assocPreivousPeriodAccountHorizNodeComposer = (
|
||||
node: IBalanceSheetAccountNode,
|
||||
) => {
|
||||
const horizontalTotals = R.map(
|
||||
this.previousPeriodAccountHorizNodeCompose(node),
|
||||
node.horizontalTotals,
|
||||
);
|
||||
return R.assoc('horizontalTotals', horizontalTotals, node);
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
// # Horizontal Nodes - Aggregate
|
||||
// ------------------------------
|
||||
/**
|
||||
* Assoc previous year total to horizontal node.
|
||||
* @param node
|
||||
* @returns
|
||||
*/
|
||||
private assocPreviousPeriodAggregateHorizTotalNode = R.curry(
|
||||
(node, index: number, totalNode) => {
|
||||
const total = this.getPPHorizNodesTotalSumation(index, node);
|
||||
|
||||
return R.assoc(
|
||||
'previousPeriod',
|
||||
this.getTotalAmountMeta(total),
|
||||
totalNode,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Compose previous period to aggregate horizontal nodes.
|
||||
* @param {IBalanceSheetTotal} node
|
||||
* @returns {IBalanceSheetTotal}
|
||||
*/
|
||||
private previousPeriodAggregateHorizNodeComposer = R.curry(
|
||||
(
|
||||
node: IBalanceSheetCommonNode,
|
||||
horiontalTotalNode: IBalanceSheetTotal,
|
||||
index: number,
|
||||
): IBalanceSheetTotal => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
this.assocPreviousPeriodTotalPercentageNode,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
this.assocPreviousPeriodTotalChangeNode,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.assocPreviousPeriodAggregateHorizTotalNode(node, index),
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.assocPreviousPeriodHorizNodeFromToDates(
|
||||
this.query.displayColumnsBy,
|
||||
),
|
||||
),
|
||||
)(horiontalTotalNode);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Assoc
|
||||
* @param {IBalanceSheetCommonNode} node
|
||||
* @returns {IBalanceSheetCommonNode}
|
||||
*/
|
||||
private assocPreviousPeriodAggregateHorizNode = (
|
||||
node: IBalanceSheetCommonNode,
|
||||
) => {
|
||||
const horizontalTotals = R.addIndex(R.map)(
|
||||
this.previousPeriodAggregateHorizNodeComposer(node),
|
||||
node.horizontalTotals,
|
||||
);
|
||||
return R.assoc('horizontalTotals', horizontalTotals, node);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,278 @@
|
||||
import * as R from 'ramda';
|
||||
import { sumBy, isEmpty } from 'lodash';
|
||||
import {
|
||||
IBalanceSheetAccountNode,
|
||||
IBalanceSheetCommonNode,
|
||||
IBalanceSheetDataNode,
|
||||
IBalanceSheetTotal,
|
||||
} from './BalanceSheet.types';
|
||||
import { FinancialPreviousYear } from '../../common/FinancialPreviousYear';
|
||||
import { GConstructor } from '@/common/types/Constructor';
|
||||
import { FinancialSheet } from '../../common/FinancialSheet';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { BalanceSheetRepository } from './BalanceSheetRepository';
|
||||
|
||||
export const BalanceSheetComparsionPreviousYear = <
|
||||
T extends GConstructor<FinancialSheet>,
|
||||
>(
|
||||
Base: T,
|
||||
) =>
|
||||
class BalanceSheetComparsionPreviousYear extends R.pipe(
|
||||
FinancialPreviousYear,
|
||||
)(Base) {
|
||||
query: BalanceSheetQuery;
|
||||
repository: BalanceSheetRepository;
|
||||
|
||||
// ------------------------------
|
||||
// # Account
|
||||
// ------------------------------
|
||||
/**
|
||||
* Associates the previous year to account node.
|
||||
* @param {IBalanceSheetDataNode} node
|
||||
* @returns {IBalanceSheetDataNode}
|
||||
*/
|
||||
protected assocPreviousYearAccountNode = (
|
||||
node: IBalanceSheetDataNode,
|
||||
): IBalanceSheetDataNode => {
|
||||
const closingBalance =
|
||||
this.repository.PYTotalAccountsLedger.whereAccountId(
|
||||
node.id,
|
||||
).getClosingBalance();
|
||||
|
||||
return R.assoc('previousYear', this.getAmountMeta(closingBalance), node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Assoc previous year attributes to account node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
protected previousYearAccountNodeComposer = (
|
||||
node: IBalanceSheetAccountNode,
|
||||
): IBalanceSheetAccountNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizontalTotals,
|
||||
this.assocPreviousYearAccountHorizNodeComposer,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
this.assocPreviousYearPercentageNode,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
this.assocPreviousYearChangetNode,
|
||||
),
|
||||
this.assocPreviousYearAccountNode,
|
||||
)(node);
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
// # Aggregate
|
||||
// ------------------------------
|
||||
/**
|
||||
* Assoc previous year on aggregate node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
protected assocPreviousYearAggregateNode = (
|
||||
node: IBalanceSheetAccountNode,
|
||||
): IBalanceSheetAccountNode => {
|
||||
const total = sumBy(node.children, 'previousYear.amount');
|
||||
|
||||
return R.assoc('previousYear', this.getTotalAmountMeta(total), node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Assoc previous year attributes to aggregate node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
protected previousYearAggregateNodeComposer = (
|
||||
node: IBalanceSheetAccountNode,
|
||||
): IBalanceSheetAccountNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
this.assocPreviousYearTotalPercentageNode,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
this.assocPreviousYearTotalChangeNode,
|
||||
),
|
||||
R.when(
|
||||
this.isNodeHasHorizontalTotals,
|
||||
this.assocPreviousYearAggregateHorizNode,
|
||||
),
|
||||
this.assocPreviousYearAggregateNode,
|
||||
)(node);
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
// # Horizontal Nodes - Aggregate
|
||||
// ------------------------------
|
||||
/**
|
||||
* Assoc previous year total to horizontal node.
|
||||
* @param node
|
||||
* @returns
|
||||
*/
|
||||
private assocPreviousYearAggregateHorizTotalNode = R.curry(
|
||||
(node, index, totalNode) => {
|
||||
const total = this.getPYHorizNodesTotalSumation(index, node);
|
||||
|
||||
return R.assoc(
|
||||
'previousYear',
|
||||
this.getTotalAmountMeta(total),
|
||||
totalNode,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Compose previous year to aggregate horizontal nodes.
|
||||
* @param {IBalanceSheetTotal} node
|
||||
* @returns {IBalanceSheetTotal}
|
||||
*/
|
||||
private previousYearAggregateHorizNodeComposer = R.curry(
|
||||
(
|
||||
node: IBalanceSheetCommonNode,
|
||||
horiontalTotalNode: IBalanceSheetTotal,
|
||||
index: number,
|
||||
): IBalanceSheetTotal => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
this.assocPreviousYearTotalPercentageNode,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
this.assocPreviousYearTotalChangeNode,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.assocPreviousYearAggregateHorizTotalNode(node, index),
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.assocPreviousYearHorizNodeFromToDates,
|
||||
),
|
||||
)(horiontalTotalNode);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Assoc
|
||||
* @param {IBalanceSheetCommonNode} node
|
||||
* @returns {IBalanceSheetCommonNode}
|
||||
*/
|
||||
public assocPreviousYearAggregateHorizNode = (
|
||||
node: IBalanceSheetCommonNode,
|
||||
): IBalanceSheetCommonNode => {
|
||||
const horizontalTotals = R.addIndex(R.map)(
|
||||
this.previousYearAggregateHorizNodeComposer(node),
|
||||
node.horizontalTotals,
|
||||
) as IBalanceSheetTotal[];
|
||||
|
||||
return R.assoc('horizontalTotals', horizontalTotals, node);
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
// # Horizontal Nodes - Account.
|
||||
// ------------------------------
|
||||
/**
|
||||
* Retrieve the given account total in the given period.
|
||||
* @param {number} accountId - Account id.
|
||||
* @param {Date} fromDate - From date.
|
||||
* @param {Date} toDate - To date.
|
||||
* @returns {number}
|
||||
*/
|
||||
private getAccountPYDatePeriodTotal = R.curry(
|
||||
(accountId: number, fromDate: Date, toDate: Date): number => {
|
||||
const PYPeriodsTotal =
|
||||
this.repository.PYPeriodsAccountsLedger.whereAccountId(accountId)
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
const PYPeriodsOpeningTotal =
|
||||
this.repository.PYPeriodsOpeningAccountLedger.whereAccountId(
|
||||
accountId,
|
||||
).getClosingBalance();
|
||||
|
||||
return PYPeriodsOpeningTotal + PYPeriodsTotal;
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Assoc preivous year to account horizontal total node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {}
|
||||
*/
|
||||
private assocPreviousYearAccountHorizTotal = R.curry(
|
||||
(node: IBalanceSheetAccountNode, totalNode) => {
|
||||
const total = this.getAccountPYDatePeriodTotal(
|
||||
node.id,
|
||||
totalNode.previousYearFromDate.date,
|
||||
totalNode.previousYearToDate.date,
|
||||
);
|
||||
return R.assoc('previousYear', this.getAmountMeta(total), totalNode);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Previous year account horizontal node composer.
|
||||
* @param {IBalanceSheetAccountNode} node -
|
||||
* @param {IBalanceSheetTotal}
|
||||
* @returns {IBalanceSheetTotal}
|
||||
*/
|
||||
private previousYearAccountHorizNodeCompose = R.curry(
|
||||
(
|
||||
node: IBalanceSheetAccountNode,
|
||||
horizontalTotalNode: IBalanceSheetTotal,
|
||||
): IBalanceSheetTotal => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
this.assocPreviousYearPercentageNode,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
this.assocPreviousYearChangetNode,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.assocPreviousYearAccountHorizTotal(node),
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.assocPreviousYearHorizNodeFromToDates,
|
||||
),
|
||||
)(horizontalTotalNode);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Assoc previous year horizontal nodes to account node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
private assocPreviousYearAccountHorizNodeComposer = (
|
||||
node: IBalanceSheetAccountNode,
|
||||
) => {
|
||||
const horizontalTotals = R.map(
|
||||
this.previousYearAccountHorizNodeCompose(node),
|
||||
node.horizontalTotals,
|
||||
);
|
||||
return R.assoc('horizontalTotals', horizontalTotals, node);
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
// # Horizontal Nodes - Aggregate.
|
||||
// ------------------------------
|
||||
/**
|
||||
* Detarmines whether the given node has horizontal totals.
|
||||
* @param {IBalanceSheetCommonNode} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isNodeHasHorizontalTotals = (node: IBalanceSheetCommonNode) =>
|
||||
!isEmpty(node.horizontalTotals);
|
||||
};
|
||||
@@ -0,0 +1,210 @@
|
||||
import * as R from 'ramda';
|
||||
import { sumBy } from 'lodash';
|
||||
import {
|
||||
IBalanceSheetQuery,
|
||||
IBalanceSheetAccountNode,
|
||||
IBalanceSheetTotalPeriod,
|
||||
IBalanceSheetCommonNode,
|
||||
} from './BalanceSheet.types';
|
||||
import { FinancialDatePeriods } from '../../common/FinancialDatePeriods';
|
||||
import { IDateRange, IFormatNumberSettings } from '../../types/Report.types';
|
||||
import { GConstructor } from '@/common/types/Constructor';
|
||||
import { FinancialSheet } from '../../common/FinancialSheet';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
|
||||
/**
|
||||
* Balance sheet date periods.
|
||||
*/
|
||||
export const BalanceSheetDatePeriods = <T extends GConstructor<FinancialSheet>>(
|
||||
Base: T,
|
||||
) =>
|
||||
class BalanceSheetDatePeriods extends R.pipe(FinancialDatePeriods)(Base) {
|
||||
/**
|
||||
* @param {IBalanceSheetQuery}
|
||||
*/
|
||||
public readonly query: BalanceSheetQuery;
|
||||
|
||||
/**
|
||||
* Retrieves the date periods based on the report query.
|
||||
* @returns {IDateRange[]}
|
||||
*/
|
||||
get datePeriods(): IDateRange[] {
|
||||
return this.getDateRanges(
|
||||
this.query.fromDate,
|
||||
this.query.toDate,
|
||||
this.query.displayColumnsBy,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the date periods of the given node based on the report query.
|
||||
* @param {IBalanceSheetCommonNode} node
|
||||
* @param {Function} callback
|
||||
* @returns {}
|
||||
*/
|
||||
public getReportNodeDatePeriods = (
|
||||
node: IBalanceSheetCommonNode,
|
||||
callback: (
|
||||
node: IBalanceSheetCommonNode,
|
||||
fromDate: Date,
|
||||
toDate: Date,
|
||||
index: number,
|
||||
) => any,
|
||||
) => {
|
||||
return this.getNodeDatePeriods(
|
||||
this.query.fromDate,
|
||||
this.query.toDate,
|
||||
this.query.displayColumnsBy,
|
||||
node,
|
||||
callback,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the date period meta.
|
||||
* @param {number} total - Total amount.
|
||||
* @param {Date} fromDate - From date.
|
||||
* @param {Date} toDate - To date.
|
||||
* @return {ICashFlowDatePeriod}
|
||||
*/
|
||||
public getDatePeriodTotalMeta = (
|
||||
total: number,
|
||||
fromDate: Date,
|
||||
toDate: Date,
|
||||
overrideSettings: IFormatNumberSettings = {},
|
||||
): IBalanceSheetTotalPeriod => {
|
||||
return this.getDatePeriodMeta(total, fromDate, toDate, {
|
||||
money: true,
|
||||
...overrideSettings,
|
||||
});
|
||||
};
|
||||
|
||||
// --------------------------------
|
||||
// # Account
|
||||
// --------------------------------
|
||||
/**
|
||||
* Retrieve the given account date period total.
|
||||
* @param {number} accountId
|
||||
* @param {Date} toDate
|
||||
* @returns {number}
|
||||
*/
|
||||
public getAccountDatePeriodTotal = (
|
||||
accountId: number,
|
||||
toDate: Date,
|
||||
): number => {
|
||||
const periodTotalBetween = this.repository.periodsAccountsLedger
|
||||
.whereAccountId(accountId)
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
const periodOpening = this.repository.periodsOpeningAccountLedger
|
||||
.whereAccountId(accountId)
|
||||
.getClosingBalance();
|
||||
|
||||
return periodOpening + periodTotalBetween;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @param {Date} fromDate
|
||||
* @param {Date} toDate
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
public getAccountNodeDatePeriod = (
|
||||
node: IBalanceSheetAccountNode,
|
||||
fromDate: Date,
|
||||
toDate: Date,
|
||||
): IBalanceSheetTotalPeriod => {
|
||||
const periodTotal = this.getAccountDatePeriodTotal(node.id, toDate);
|
||||
|
||||
return this.getDatePeriodTotalMeta(periodTotal, fromDate, toDate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve total date periods of the given account node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
public getAccountsNodeDatePeriods = (
|
||||
node: IBalanceSheetAccountNode,
|
||||
): IBalanceSheetTotalPeriod[] => {
|
||||
return this.getReportNodeDatePeriods(node, this.getAccountNodeDatePeriod);
|
||||
};
|
||||
|
||||
/**
|
||||
* Assoc total date periods to account node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
public assocAccountNodeDatePeriods = (
|
||||
node: IBalanceSheetAccountNode,
|
||||
): IBalanceSheetAccountNode => {
|
||||
const datePeriods = this.getAccountsNodeDatePeriods(node);
|
||||
|
||||
return R.assoc('horizontalTotals', datePeriods, node);
|
||||
};
|
||||
|
||||
// --------------------------------
|
||||
// # Aggregate
|
||||
// --------------------------------
|
||||
/**
|
||||
*
|
||||
* @param {} node
|
||||
* @param {number} index
|
||||
* @returns {number}
|
||||
*/
|
||||
public getAggregateDatePeriodIndexTotal = (node, index) => {
|
||||
return sumBy(node.children, `horizontalTotals[${index}].total.amount`);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @param {Date} fromDate
|
||||
* @param {Date} toDate
|
||||
* @returns
|
||||
*/
|
||||
public getAggregateNodeDatePeriod = (
|
||||
node: IBalanceSheetAccountNode,
|
||||
fromDate: Date,
|
||||
toDate: Date,
|
||||
index: number,
|
||||
) => {
|
||||
const periodTotal = this.getAggregateDatePeriodIndexTotal(node, index);
|
||||
|
||||
return this.getDatePeriodTotalMeta(periodTotal, fromDate, toDate);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* @returns
|
||||
*/
|
||||
public getAggregateNodeDatePeriods = (node) => {
|
||||
return this.getReportNodeDatePeriods(
|
||||
node,
|
||||
this.getAggregateNodeDatePeriod,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Assoc total date periods to aggregate node.
|
||||
* @param node
|
||||
* @returns {}
|
||||
*/
|
||||
public assocAggregateNodeDatePeriods = (node) => {
|
||||
const datePeriods = this.getAggregateNodeDatePeriods(node);
|
||||
|
||||
return R.assoc('horizontalTotals', datePeriods, node);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* @returns
|
||||
*/
|
||||
public assocAccountsNodeDatePeriods = (node) => {
|
||||
return this.assocAggregateNodeDatePeriods(node);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { BalanceSheetTableInjectable } from './BalanceSheetTableInjectable';
|
||||
import { BalanceSheetPdfInjectable } from './BalanceSheetPdfInjectable';
|
||||
import { IBalanceSheetQuery } from './BalanceSheet.types';
|
||||
import { TableSheet } from '../../common/TableSheet';
|
||||
|
||||
@Injectable()
|
||||
export class BalanceSheetExportInjectable {
|
||||
constructor(
|
||||
private readonly balanceSheetTable: BalanceSheetTableInjectable,
|
||||
private readonly balanceSheetPdf: BalanceSheetPdfInjectable,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieves the trial balance sheet in XLSX format.
|
||||
* @param {ITrialBalanceSheetQuery} query
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
public async xlsx(query: IBalanceSheetQuery) {
|
||||
const table = await this.balanceSheetTable.table(query);
|
||||
|
||||
const tableSheet = new TableSheet(table.table);
|
||||
const tableCsv = tableSheet.convertToXLSX();
|
||||
|
||||
return tableSheet.convertToBuffer(tableCsv, 'xlsx');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the trial balance sheet in CSV format.
|
||||
* @param {ITrialBalanceSheetQuery} query
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
public async csv(
|
||||
query: IBalanceSheetQuery,
|
||||
): Promise<string> {
|
||||
const table = await this.balanceSheetTable.table(query);
|
||||
|
||||
const tableSheet = new TableSheet(table.table);
|
||||
const tableCsv = tableSheet.convertToCSV();
|
||||
|
||||
return tableCsv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the balance sheet in pdf format.
|
||||
* @param {IBalanceSheetQuery} query
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
public async pdf(
|
||||
query: IBalanceSheetQuery,
|
||||
): Promise<Buffer> {
|
||||
return this.balanceSheetPdf.pdf(query);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
import * as R from 'ramda';
|
||||
import { get } from 'lodash';
|
||||
import {
|
||||
IBalanceSheetDataNode,
|
||||
BALANCE_SHEET_NODE_TYPE,
|
||||
} from './BalanceSheet.types';
|
||||
import { GConstructor } from '@/common/types/Constructor';
|
||||
import { FinancialFilter } from '../../common/FinancialFilter';
|
||||
import { BalanceSheetBase } from './BalanceSheetBase';
|
||||
import { BalanceSheetRepository } from './BalanceSheetRepository';
|
||||
import { FinancialSheet } from '../../common/FinancialSheet';
|
||||
import { FinancialSheetStructure } from '../../common/FinancialSheetStructure';
|
||||
|
||||
export const BalanceSheetFiltering = <T extends GConstructor<FinancialSheet>>(
|
||||
Base: T,
|
||||
) =>
|
||||
class extends R.pipe(
|
||||
FinancialFilter,
|
||||
FinancialSheetStructure,
|
||||
BalanceSheetBase,
|
||||
)(Base) {
|
||||
/**
|
||||
* @description Repository.
|
||||
*/
|
||||
readonly repository: BalanceSheetRepository;
|
||||
|
||||
// -----------------------
|
||||
// # Account
|
||||
// -----------------------
|
||||
/**
|
||||
* Filter report node detarmine.
|
||||
* @param {IBalanceSheetDataNode} node - Balance sheet node.
|
||||
* @return {boolean}
|
||||
*/
|
||||
private accountNoneZeroNodesFilterDetarminer = (
|
||||
node: IBalanceSheetDataNode,
|
||||
): boolean => {
|
||||
return R.ifElse(
|
||||
this.isNodeType(BALANCE_SHEET_NODE_TYPE.ACCOUNT),
|
||||
this.isNodeNoneZero,
|
||||
R.always(true),
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines account none-transactions node.
|
||||
* @param {IBalanceSheetDataNode} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private accountNoneTransFilterDetarminer = (
|
||||
node: IBalanceSheetDataNode,
|
||||
): boolean => {
|
||||
return R.ifElse(
|
||||
this.isNodeType(BALANCE_SHEET_NODE_TYPE.ACCOUNT),
|
||||
this.isNodeNoneZero,
|
||||
R.always(true),
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Report nodes filter.
|
||||
* @param {IBalanceSheetSection[]} nodes -
|
||||
* @return {IBalanceSheetSection[]}
|
||||
*/
|
||||
private accountsNoneZeroNodesFilter = (
|
||||
nodes: IBalanceSheetDataNode[],
|
||||
): IBalanceSheetDataNode[] => {
|
||||
return this.filterNodesDeep(
|
||||
nodes,
|
||||
this.accountNoneZeroNodesFilterDetarminer,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Filters the accounts none-transactions nodes.
|
||||
* @param {IBalanceSheetDataNode[]} nodes
|
||||
* @returns {IBalanceSheetDataNode[]}
|
||||
*/
|
||||
private accountsNoneTransactionsNodesFilter = (
|
||||
nodes: IBalanceSheetDataNode[],
|
||||
) => {
|
||||
return this.filterNodesDeep(nodes, this.accountNoneTransFilterDetarminer);
|
||||
};
|
||||
|
||||
// -----------------------
|
||||
// # Aggregate/Accounts.
|
||||
// -----------------------
|
||||
/**
|
||||
* Detearmines aggregate none-children filtering.
|
||||
* @param {IBalanceSheetDataNode} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private aggregateNoneChildrenFilterDetarminer = (
|
||||
node: IBalanceSheetDataNode,
|
||||
): boolean => {
|
||||
// Detarmines whether the given node is aggregate or accounts node.
|
||||
const isAggregateOrAccounts =
|
||||
this.isNodeType(BALANCE_SHEET_NODE_TYPE.AGGREGATE, node) ||
|
||||
this.isNodeType(BALANCE_SHEET_NODE_TYPE.ACCOUNTS, node);
|
||||
|
||||
// Retrieve the schema node of the given id.
|
||||
const schemaNode = this.getSchemaNodeById(node.id);
|
||||
|
||||
// Detarmines if the schema node is always should show.
|
||||
const isSchemaAlwaysShow = get(schemaNode, 'alwaysShow', false);
|
||||
|
||||
return isAggregateOrAccounts && !isSchemaAlwaysShow
|
||||
? this.isNodeHasChildren(node)
|
||||
: true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Filters aggregate none-children nodes.
|
||||
* @param {IBalanceSheetDataNode[]} nodes
|
||||
* @returns {IBalanceSheetDataNode[]}
|
||||
*/
|
||||
private aggregateNoneChildrenFilter = (
|
||||
nodes: IBalanceSheetDataNode[],
|
||||
): IBalanceSheetDataNode[] => {
|
||||
return this.filterNodesDeep2(
|
||||
this.aggregateNoneChildrenFilterDetarminer,
|
||||
nodes,
|
||||
);
|
||||
};
|
||||
|
||||
// -----------------------
|
||||
// # Composers.
|
||||
// -----------------------
|
||||
/**
|
||||
* Filters none-zero nodes.
|
||||
* @param {IBalanceSheetDataNode[]} nodes
|
||||
* @returns {IBalanceSheetDataNode[]}
|
||||
*/
|
||||
private filterNoneZeroNodesCompose = (
|
||||
nodes: IBalanceSheetDataNode[],
|
||||
): IBalanceSheetDataNode[] => {
|
||||
return R.compose(
|
||||
this.aggregateNoneChildrenFilter,
|
||||
this.accountsNoneZeroNodesFilter,
|
||||
)(nodes);
|
||||
};
|
||||
|
||||
/**
|
||||
* Filters none-transactions nodes.
|
||||
* @param {IBalanceSheetDataNode[]} nodes
|
||||
* @returns {IBalanceSheetDataNode[]}
|
||||
*/
|
||||
private filterNoneTransNodesCompose = (
|
||||
nodes: IBalanceSheetDataNode[],
|
||||
): IBalanceSheetDataNode[] => {
|
||||
return R.compose(
|
||||
this.aggregateNoneChildrenFilter,
|
||||
this.accountsNoneTransactionsNodesFilter,
|
||||
)(nodes);
|
||||
};
|
||||
|
||||
/**
|
||||
* Supress nodes when accounts transactions ledger is empty.
|
||||
* @param {IBalanceSheetDataNode[]} nodes
|
||||
* @returns {IBalanceSheetDataNode[]}
|
||||
*/
|
||||
private supressNodesWhenAccountsTransactionsEmpty = (
|
||||
nodes: IBalanceSheetDataNode[],
|
||||
): IBalanceSheetDataNode[] => {
|
||||
return this.repository.totalAccountsLedger.isEmpty() ? [] : nodes;
|
||||
};
|
||||
|
||||
/**
|
||||
* Compose report nodes filtering.
|
||||
* @param {IBalanceSheetDataNode[]} nodes
|
||||
* @returns {IBalanceSheetDataNode[]}
|
||||
*/
|
||||
protected reportFilterPlugin = (nodes: IBalanceSheetDataNode[]) => {
|
||||
return R.compose(
|
||||
this.supressNodesWhenAccountsTransactionsEmpty,
|
||||
R.when(R.always(this.query.noneZero), this.filterNoneZeroNodesCompose),
|
||||
R.when(
|
||||
R.always(this.query.noneTransactions),
|
||||
this.filterNoneTransNodesCompose,
|
||||
),
|
||||
)(nodes);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
import {
|
||||
IBalanceSheetDOO,
|
||||
IBalanceSheetQuery,
|
||||
} from './BalanceSheet.types';
|
||||
import { BalanceSheetRepository } from './BalanceSheetRepository';
|
||||
import { BalanceSheetMetaInjectable } from './BalanceSheetMeta';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { events } from '@/common/events/events';
|
||||
import { BalanceSheet } from './BalanceSheet';
|
||||
import { getBalanceSheetDefaultQuery } from './constants';
|
||||
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
||||
import { I18nService } from 'nestjs-i18n';
|
||||
|
||||
@Injectable()
|
||||
export class BalanceSheetInjectable {
|
||||
constructor(
|
||||
private readonly balanceSheetMeta: BalanceSheetMetaInjectable,
|
||||
private readonly eventPublisher: EventEmitter2,
|
||||
private readonly tenancyContext: TenancyContext,
|
||||
private readonly i18n: I18nService,
|
||||
|
||||
@Inject(BalanceSheetRepository.name)
|
||||
private readonly balanceSheetRepository: BalanceSheetRepository,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieve balance sheet statement.
|
||||
* @param {IBalanceSheetQuery} query - Balance sheet query.
|
||||
* @return {Promise<IBalanceSheetStatement>}
|
||||
*/
|
||||
public async balanceSheet(
|
||||
query: IBalanceSheetQuery,
|
||||
): Promise<IBalanceSheetDOO> {
|
||||
const filter = {
|
||||
...getBalanceSheetDefaultQuery(),
|
||||
...query,
|
||||
};
|
||||
const tenantMetadata = await this.tenancyContext.getTenantMetadata(true);
|
||||
|
||||
// Loads all resources.
|
||||
await this.balanceSheetRepository.asyncInitialize(filter);
|
||||
|
||||
// Balance sheet report instance.
|
||||
const balanceSheetInstanace = new BalanceSheet(
|
||||
filter,
|
||||
this.balanceSheetRepository,
|
||||
tenantMetadata.baseCurrency,
|
||||
this.i18n,
|
||||
);
|
||||
// Balance sheet data.
|
||||
const data = balanceSheetInstanace.reportData();
|
||||
|
||||
// Balance sheet meta.
|
||||
const meta = await this.balanceSheetMeta.meta(filter);
|
||||
|
||||
// Triggers `onBalanceSheetViewed` event.
|
||||
await this.eventPublisher.emitAsync(events.reports.onBalanceSheetViewed, {
|
||||
query,
|
||||
});
|
||||
return {
|
||||
query: filter,
|
||||
data,
|
||||
meta,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import * as moment from 'moment';
|
||||
import { FinancialSheetMeta } from '../../common/FinancialSheetMeta';
|
||||
import { IBalanceSheetMeta, IBalanceSheetQuery } from './BalanceSheet.types';
|
||||
|
||||
@Injectable()
|
||||
export class BalanceSheetMetaInjectable {
|
||||
constructor(private readonly financialSheetMeta: FinancialSheetMeta) {}
|
||||
|
||||
/**
|
||||
* Retrieves the balance sheet meta.
|
||||
* @returns {IBalanceSheetMeta}
|
||||
*/
|
||||
public async meta(query: IBalanceSheetQuery): Promise<IBalanceSheetMeta> {
|
||||
const commonMeta = await this.financialSheetMeta.meta();
|
||||
const formattedAsDate = moment(query.toDate).format('YYYY/MM/DD');
|
||||
const formattedDateRange = `As ${formattedAsDate}`;
|
||||
const sheetName = 'Balance Sheet Statement';
|
||||
|
||||
return {
|
||||
...commonMeta,
|
||||
sheetName,
|
||||
formattedAsDate,
|
||||
formattedDateRange,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
BALANCE_SHEET_SCHEMA_NODE_TYPE,
|
||||
IBalanceSheetDataNode,
|
||||
IBalanceSheetNetIncomeNode,
|
||||
IBalanceSheetSchemaNetIncomeNode,
|
||||
IBalanceSheetSchemaNode,
|
||||
IBalanceSheetTotalPeriod,
|
||||
} from './BalanceSheet.types';
|
||||
import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear';
|
||||
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
|
||||
import { FinancialPreviousPeriod } from '../../common/FinancialPreviousPeriod';
|
||||
import { FinancialHorizTotals } from '../../common/FinancialHorizTotals';
|
||||
import { BalanceSheetRepository } from './BalanceSheetRepository';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { BalanceSheetNetIncomePP } from './BalanceSheetNetIncomePP';
|
||||
import { BalanceSheetNetIncomePY } from './BalanceSheetNetIncomePY';
|
||||
import { FinancialSheet } from '../../common/FinancialSheet';
|
||||
import { GConstructor } from '@/common/types/Constructor';
|
||||
|
||||
export const BalanceSheetNetIncome = <T extends GConstructor<FinancialSheet>>(
|
||||
Base: T,
|
||||
) =>
|
||||
class extends R.pipe(
|
||||
BalanceSheetNetIncomePP,
|
||||
BalanceSheetNetIncomePY,
|
||||
BalanceSheetComparsionPreviousYear,
|
||||
BalanceSheetComparsionPreviousPeriod,
|
||||
FinancialPreviousPeriod,
|
||||
FinancialHorizTotals,
|
||||
)(Base) {
|
||||
public repository: BalanceSheetRepository;
|
||||
public query: BalanceSheetQuery;
|
||||
|
||||
/**
|
||||
* Retrieves the closing balance of income accounts.
|
||||
* @returns {number}
|
||||
*/
|
||||
public getIncomeTotal = () => {
|
||||
const closeingBalance = this.repository.incomeLedger.getClosingBalance();
|
||||
return closeingBalance;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the closing balance of expenses accounts.
|
||||
* @returns {number}
|
||||
*/
|
||||
public getExpensesTotal = () => {
|
||||
const closingBalance = this.repository.expensesLedger.getClosingBalance();
|
||||
return closingBalance;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the total net income.
|
||||
* @returns {number}
|
||||
*/
|
||||
public getNetIncomeTotal = () => {
|
||||
const income = this.getIncomeTotal();
|
||||
const expenses = this.getExpensesTotal();
|
||||
|
||||
return income - expenses;
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the aggregate schema node type.
|
||||
* @param {IBalanceSheetSchemaNetIncomeNode} node - Schema node.
|
||||
* @return {IBalanceSheetAggregateNode}
|
||||
*/
|
||||
public schemaNetIncomeNodeMapper = (
|
||||
node: IBalanceSheetSchemaNetIncomeNode,
|
||||
): IBalanceSheetNetIncomeNode => {
|
||||
const total = this.getNetIncomeTotal();
|
||||
|
||||
return {
|
||||
id: node.id,
|
||||
name: this.i18n.__(node.name),
|
||||
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.NET_INCOME,
|
||||
total: this.getTotalAmountMeta(total),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Mapps the net income shcema node to report node.
|
||||
* @param {IBalanceSheetSchemaNetIncomeNode} node
|
||||
* @returns {IBalanceSheetNetIncomeNode}
|
||||
*/
|
||||
public schemaNetIncomeNodeCompose = (
|
||||
node: IBalanceSheetSchemaNetIncomeNode,
|
||||
): IBalanceSheetNetIncomeNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.previousYearNetIncomeNodeCompose,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.previousPeriodNetIncomeNodeCompose,
|
||||
),
|
||||
R.when(
|
||||
this.query.isDatePeriodsColumnsType,
|
||||
this.assocNetIncomeDatePeriodsNode,
|
||||
),
|
||||
this.schemaNetIncomeNodeMapper,
|
||||
)(node);
|
||||
};
|
||||
|
||||
// --------------------------------
|
||||
// # Date Periods
|
||||
// --------------------------------
|
||||
/**
|
||||
* Retreives total income of the given date period.
|
||||
* @param {number} accountId -
|
||||
* @param {Date} toDate -
|
||||
* @returns {number}
|
||||
*/
|
||||
public getIncomeDatePeriodTotal = (toDate: Date): number => {
|
||||
const periodTotalBetween = this.repository.incomePeriodsAccountsLedger
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
const periodOpening =
|
||||
this.repository.incomePeriodsOpeningAccountsLedger.getClosingBalance();
|
||||
|
||||
return periodOpening + periodTotalBetween;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves total expense of the given date period.
|
||||
* @param {number} accountId -
|
||||
* @param {Date} toDate -
|
||||
* @returns {number}
|
||||
*/
|
||||
public getExpensesDatePeriodTotal = (toDate: Date): number => {
|
||||
const periodTotalBetween = this.repository.expensesPeriodsAccountsLedger
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
const periodOpening =
|
||||
this.repository.expensesOpeningAccountLedger.getClosingBalance();
|
||||
|
||||
return periodOpening + periodTotalBetween;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the given net income date period total.
|
||||
* @param {number} accountId
|
||||
* @param {Date} toDate
|
||||
* @returns {number}
|
||||
*/
|
||||
public getNetIncomeDatePeriodTotal = (toDate: Date): number => {
|
||||
const income = this.getIncomeDatePeriodTotal(toDate);
|
||||
const expense = this.getExpensesDatePeriodTotal(toDate);
|
||||
|
||||
return income - expense;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the net income date period node.
|
||||
* @param {IBalanceSheetNetIncomeNode} node
|
||||
* @param {Date} fromDate
|
||||
* @param {Date} toDate
|
||||
* @returns {IBalanceSheetNetIncomeNode}
|
||||
*/
|
||||
public getNetIncomeDatePeriodNode = (
|
||||
node: IBalanceSheetNetIncomeNode,
|
||||
fromDate: Date,
|
||||
toDate: Date,
|
||||
): IBalanceSheetTotalPeriod => {
|
||||
const periodTotal = this.getNetIncomeDatePeriodTotal(toDate);
|
||||
|
||||
return this.getDatePeriodTotalMeta(periodTotal, fromDate, toDate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve total date periods of the given net income node.
|
||||
* @param {IBalanceSheetNetIncomeNode} node
|
||||
* @returns {IBalanceSheetNetIncomeNode}
|
||||
*/
|
||||
public getNetIncomeDatePeriodsNode = (
|
||||
node: IBalanceSheetNetIncomeNode,
|
||||
): IBalanceSheetTotalPeriod[] => {
|
||||
return this.getReportNodeDatePeriods(
|
||||
node,
|
||||
this.getNetIncomeDatePeriodNode,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Assoc total date periods to net income node.
|
||||
* @param {IBalanceSheetNetIncomeNode} node
|
||||
* @returns {IBalanceSheetNetIncomeNode}
|
||||
*/
|
||||
public assocNetIncomeDatePeriodsNode = (
|
||||
node: IBalanceSheetNetIncomeNode,
|
||||
): IBalanceSheetNetIncomeNode => {
|
||||
const datePeriods = this.getNetIncomeDatePeriodsNode(node);
|
||||
|
||||
return R.assoc('horizontalTotals', datePeriods, node);
|
||||
};
|
||||
|
||||
// -----------------------------
|
||||
// - Net Income Nodes Praser
|
||||
// -----------------------------
|
||||
/**
|
||||
* Mappes the given report schema node.
|
||||
* @param {IBalanceSheetSchemaNode} node - Schema node.
|
||||
* @return {IBalanceSheetDataNode}
|
||||
*/
|
||||
public reportNetIncomeNodeSchemaParser = (
|
||||
schemaNode: IBalanceSheetSchemaNode,
|
||||
): IBalanceSheetDataNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.NET_INCOME),
|
||||
this.schemaNetIncomeNodeCompose,
|
||||
),
|
||||
)(schemaNode);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses the report net income schema nodes.
|
||||
* @param {(IBalanceSheetSchemaNode | IBalanceSheetDataNode)[]} nodes -
|
||||
* @return {IBalanceSheetDataNode[]}
|
||||
*/
|
||||
public netIncomeSchemaParser = (
|
||||
nodes: (IBalanceSheetSchemaNode | IBalanceSheetDataNode)[],
|
||||
): IBalanceSheetDataNode[] => {
|
||||
return this.mapNodesDeep(nodes, this.reportNetIncomeNodeSchemaParser);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,124 @@
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
IBalanceSheetNetIncomeNode,
|
||||
IBalanceSheetTotalPeriod,
|
||||
} from '@/interfaces';
|
||||
import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear';
|
||||
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
|
||||
import { FinancialPreviousPeriod } from '../../common/FinancialPreviousPeriod';
|
||||
import { FinancialHorizTotals } from '../../common/FinancialHorizTotals';
|
||||
import { BalanceSheetRepository } from './BalanceSheetRepository';
|
||||
import { BalanceSheetNetIncomePP } from './BalanceSheetNetIncomePP';
|
||||
import { BalanceSheetNetIncomePY } from './BalanceSheetNetIncomePY';
|
||||
import { FinancialSheet } from '../../common/FinancialSheet';
|
||||
import { GConstructor } from '@/common/types/Constructor';
|
||||
|
||||
export const BalanceSheetNetIncomeDatePeriods = <
|
||||
T extends GConstructor<FinancialSheet>,
|
||||
>(
|
||||
Base: T,
|
||||
) =>
|
||||
class extends R.pipe(
|
||||
BalanceSheetNetIncomePP,
|
||||
BalanceSheetNetIncomePY,
|
||||
BalanceSheetComparsionPreviousYear,
|
||||
BalanceSheetComparsionPreviousPeriod,
|
||||
FinancialPreviousPeriod,
|
||||
FinancialHorizTotals,
|
||||
)(Base) {
|
||||
repository: BalanceSheetRepository;
|
||||
|
||||
// --------------------------------
|
||||
// # Date Periods
|
||||
// --------------------------------
|
||||
/**
|
||||
* Retreives total income of the given date period.
|
||||
* @param {number} accountId -
|
||||
* @param {Date} toDate -
|
||||
* @returns {number}
|
||||
*/
|
||||
private getIncomeDatePeriodTotal = (toDate: Date): number => {
|
||||
const periodTotalBetween = this.repository.incomePeriodsAccountsLedger
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
const periodOpening =
|
||||
this.repository.incomePeriodsOpeningAccountsLedger.getClosingBalance();
|
||||
|
||||
return periodOpening + periodTotalBetween;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves total expense of the given date period.
|
||||
* @param {number} accountId -
|
||||
* @param {Date} toDate -
|
||||
* @returns {number}
|
||||
*/
|
||||
private getExpensesDatePeriodTotal = (toDate: Date): number => {
|
||||
const periodTotalBetween = this.repository.expensesPeriodsAccountsLedger
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
const periodOpening =
|
||||
this.repository.expensesOpeningAccountLedger.getClosingBalance();
|
||||
|
||||
return periodOpening + periodTotalBetween;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the given net income date period total.
|
||||
* @param {number} accountId
|
||||
* @param {Date} toDate
|
||||
* @returns {number}
|
||||
*/
|
||||
private getNetIncomeDatePeriodTotal = (toDate: Date): number => {
|
||||
const income = this.getIncomeDatePeriodTotal(toDate);
|
||||
const expense = this.getExpensesDatePeriodTotal(toDate);
|
||||
|
||||
return income - expense;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the net income date period node.
|
||||
* @param {IBalanceSheetNetIncomeNode} node
|
||||
* @param {Date} fromDate
|
||||
* @param {Date} toDate
|
||||
* @returns {IBalanceSheetNetIncomeNode}
|
||||
*/
|
||||
private getNetIncomeDatePeriodNode = (
|
||||
node: IBalanceSheetNetIncomeNode,
|
||||
fromDate: Date,
|
||||
toDate: Date,
|
||||
): IBalanceSheetTotalPeriod => {
|
||||
const periodTotal = this.getNetIncomeDatePeriodTotal(toDate);
|
||||
|
||||
return this.getDatePeriodTotalMeta(periodTotal, fromDate, toDate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve total date periods of the given net income node.
|
||||
* @param {IBalanceSheetNetIncomeNode} node
|
||||
* @returns {IBalanceSheetNetIncomeNode}
|
||||
*/
|
||||
private getNetIncomeDatePeriodsNode = (
|
||||
node: IBalanceSheetNetIncomeNode,
|
||||
): IBalanceSheetTotalPeriod[] => {
|
||||
return this.getReportNodeDatePeriods(
|
||||
node,
|
||||
this.getNetIncomeDatePeriodNode,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Assoc total date periods to net income node.
|
||||
* @param {IBalanceSheetNetIncomeNode} node
|
||||
* @returns {IBalanceSheetNetIncomeNode}
|
||||
*/
|
||||
public assocNetIncomeDatePeriodsNode = (
|
||||
node: IBalanceSheetNetIncomeNode,
|
||||
): IBalanceSheetNetIncomeNode => {
|
||||
const datePeriods = this.getNetIncomeDatePeriodsNode(node);
|
||||
|
||||
return R.assoc('horizontalTotals', datePeriods, node);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,136 @@
|
||||
import * as R from 'ramda';
|
||||
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
|
||||
import { FinancialPreviousPeriod } from '../../common/FinancialPreviousPeriod';
|
||||
import { FinancialHorizTotals } from '../../common/FinancialHorizTotals';
|
||||
import {
|
||||
IBalanceSheetNetIncomeNode,
|
||||
IBalanceSheetTotal,
|
||||
} from './BalanceSheet.types';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { BalanceSheetRepository } from './BalanceSheetRepository';
|
||||
import { GConstructor } from '@/common/types/Constructor';
|
||||
import { FinancialSheet } from '../../common/FinancialSheet';
|
||||
|
||||
export const BalanceSheetNetIncomeDatePeriodsPP = <
|
||||
T extends GConstructor<FinancialSheet>,
|
||||
>(
|
||||
Base: T,
|
||||
) =>
|
||||
class extends R.pipe(
|
||||
BalanceSheetComparsionPreviousPeriod,
|
||||
FinancialPreviousPeriod,
|
||||
FinancialHorizTotals,
|
||||
)(Base) {
|
||||
query: BalanceSheetQuery;
|
||||
repository: BalanceSheetRepository;
|
||||
|
||||
/**
|
||||
* Retrieves the PY total income of the given date period.
|
||||
* @param {number} accountId -
|
||||
* @param {Date} toDate -
|
||||
* @return {number}
|
||||
*/
|
||||
public getPPIncomeDatePeriodTotal = R.curry((toDate: Date) => {
|
||||
const PYPeriodsTotal = this.repository.incomePPPeriodsAccountsLedger
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
const PYPeriodsOpeningTotal =
|
||||
this.repository.incomePPPeriodsOpeningAccountLedger.getClosingBalance();
|
||||
|
||||
return PYPeriodsOpeningTotal + PYPeriodsTotal;
|
||||
});
|
||||
|
||||
/**
|
||||
* Retrieves the PY total expense of the given date period.
|
||||
* @param {number} accountId -
|
||||
* @param {Date} toDate -
|
||||
* @returns {number}
|
||||
*/
|
||||
public getPPExpenseDatePeriodTotal = R.curry((toDate: Date) => {
|
||||
const PYPeriodsTotal = this.repository.expensePPPeriodsAccountsLedger
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
const PYPeriodsOpeningTotal =
|
||||
this.repository.expensePPPeriodsOpeningAccountLedger.getClosingBalance();
|
||||
|
||||
return PYPeriodsOpeningTotal + PYPeriodsTotal;
|
||||
});
|
||||
|
||||
/**
|
||||
* Retrieve the given net income total of the given period.
|
||||
* @param {number} accountId - Account id.
|
||||
* @param {Date} toDate - To date.
|
||||
* @returns {number}
|
||||
*/
|
||||
public getPPNetIncomeDatePeriodTotal = R.curry((toDate: Date) => {
|
||||
const income = this.getPPIncomeDatePeriodTotal(toDate);
|
||||
const expense = this.getPPExpenseDatePeriodTotal(toDate);
|
||||
|
||||
return income - expense;
|
||||
});
|
||||
|
||||
/**
|
||||
* Assoc preivous period to account horizontal total node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {}
|
||||
*/
|
||||
public assocPreviousPeriodNetIncomeHorizTotal = R.curry(
|
||||
(node: IBalanceSheetNetIncomeNode, totalNode) => {
|
||||
const total = this.getPPNetIncomeDatePeriodTotal(
|
||||
totalNode.previousPeriodToDate.date,
|
||||
);
|
||||
return R.assoc('previousPeriod', this.getAmountMeta(total), totalNode);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Compose previous period to aggregate horizontal nodes.
|
||||
* @param {IBalanceSheetTotal} node
|
||||
* @returns {IBalanceSheetTotal}
|
||||
*/
|
||||
public previousPeriodNetIncomeHorizNodeComposer = R.curry(
|
||||
(
|
||||
node: IBalanceSheetNetIncomeNode,
|
||||
horiontalTotalNode: IBalanceSheetTotal,
|
||||
): IBalanceSheetTotal => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
this.assocPreviousPeriodTotalPercentageNode,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
this.assocPreviousPeriodTotalChangeNode,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.assocPreviousPeriodNetIncomeHorizTotal(node),
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.assocPreviousPeriodHorizNodeFromToDates(
|
||||
this.query.displayColumnsBy,
|
||||
),
|
||||
),
|
||||
)(horiontalTotalNode);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Associate the PP to net income horizontal nodes.
|
||||
* @param {IBalanceSheetCommonNode} node
|
||||
* @returns {IBalanceSheetCommonNode}
|
||||
*/
|
||||
public assocPreviousPeriodNetIncomeHorizNode = (
|
||||
node: IBalanceSheetNetIncomeNode,
|
||||
): IBalanceSheetNetIncomeNode => {
|
||||
const horizontalTotals = R.addIndex(R.map)(
|
||||
this.previousPeriodNetIncomeHorizNodeComposer(node),
|
||||
node.horizontalTotals,
|
||||
) as IBalanceSheetTotal[];
|
||||
|
||||
return R.assoc('horizontalTotals', horizontalTotals, node);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,131 @@
|
||||
import * as R from 'ramda';
|
||||
import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear';
|
||||
import { FinancialPreviousPeriod } from '../../common/FinancialPreviousPeriod';
|
||||
import { FinancialHorizTotals } from '../../common/FinancialHorizTotals';
|
||||
import {
|
||||
IBalanceSheetNetIncomeNode,
|
||||
IBalanceSheetTotal,
|
||||
} from './BalanceSheet.types';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { BalanceSheetRepository } from './BalanceSheetRepository';
|
||||
import { GConstructor } from '@/common/types/Constructor';
|
||||
import { FinancialSheet } from '../../common/FinancialSheet';
|
||||
|
||||
export const BalanceSheetNetIncomeDatePeriodsPY = <
|
||||
T extends GConstructor<FinancialSheet>,
|
||||
>(
|
||||
Base: T,
|
||||
) =>
|
||||
class extends R.pipe(
|
||||
BalanceSheetComparsionPreviousYear,
|
||||
FinancialPreviousPeriod,
|
||||
FinancialHorizTotals,
|
||||
)(Base) {
|
||||
query: BalanceSheetQuery;
|
||||
repository: BalanceSheetRepository;
|
||||
|
||||
/**
|
||||
* Retrieves the PY total income of the given date period.
|
||||
* @param {Date} toDate -
|
||||
* @return {number}
|
||||
*/
|
||||
public getPYIncomeDatePeriodTotal = R.curry((toDate: Date) => {
|
||||
const PYPeriodsTotal = this.repository.incomePYPeriodsAccountsLedger
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
const PYPeriodsOpeningTotal =
|
||||
this.repository.incomePYPeriodsOpeningAccountLedger.getClosingBalance();
|
||||
|
||||
return PYPeriodsOpeningTotal + PYPeriodsTotal;
|
||||
});
|
||||
|
||||
/**
|
||||
* Retrieves the PY total expense of the given date period.
|
||||
* @param {Date} toDate -
|
||||
* @returns {number}
|
||||
*/
|
||||
public getPYExpenseDatePeriodTotal = R.curry((toDate: Date) => {
|
||||
const PYPeriodsTotal = this.repository.expensePYPeriodsAccountsLedger
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
const PYPeriodsOpeningTotal =
|
||||
this.repository.expensePYPeriodsOpeningAccountLedger.getClosingBalance();
|
||||
|
||||
return PYPeriodsOpeningTotal + PYPeriodsTotal;
|
||||
});
|
||||
|
||||
/**
|
||||
* Retrieve the given net income total of the given period.
|
||||
* @param {Date} toDate - To date.
|
||||
* @returns {number}
|
||||
*/
|
||||
public getPYNetIncomeDatePeriodTotal = R.curry((toDate: Date) => {
|
||||
const income = this.getPYIncomeDatePeriodTotal(toDate);
|
||||
const expense = this.getPYExpenseDatePeriodTotal(toDate);
|
||||
|
||||
return income - expense;
|
||||
});
|
||||
|
||||
/**
|
||||
* Assoc preivous year to account horizontal total node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {}
|
||||
*/
|
||||
public assocPreviousYearNetIncomeHorizTotal = R.curry(
|
||||
(node: IBalanceSheetNetIncomeNode, totalNode) => {
|
||||
const total = this.getPYNetIncomeDatePeriodTotal(
|
||||
totalNode.previousYearToDate.date,
|
||||
);
|
||||
return R.assoc('previousYear', this.getAmountMeta(total), totalNode);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Compose PY to net income horizontal nodes.
|
||||
* @param {IBalanceSheetTotal} node
|
||||
* @returns {IBalanceSheetTotal}
|
||||
*/
|
||||
public previousYearNetIncomeHorizNodeComposer = R.curry(
|
||||
(
|
||||
node: IBalanceSheetNetIncomeNode,
|
||||
horiontalTotalNode: IBalanceSheetTotal,
|
||||
): IBalanceSheetTotal => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
this.assocPreviousYearTotalPercentageNode,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
this.assocPreviousYearTotalChangeNode,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.assocPreviousYearNetIncomeHorizTotal(node),
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.assocPreviousYearHorizNodeFromToDates,
|
||||
),
|
||||
)(horiontalTotalNode);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Associate the PY to net income horizontal nodes.
|
||||
* @param {IBalanceSheetCommonNode} node
|
||||
* @returns {IBalanceSheetCommonNode}
|
||||
*/
|
||||
public assocPreviousYearNetIncomeHorizNode = (
|
||||
node: IBalanceSheetNetIncomeNode,
|
||||
): IBalanceSheetNetIncomeNode => {
|
||||
const horizontalTotals = R.addIndex(R.map)(
|
||||
this.previousYearNetIncomeHorizNodeComposer(node),
|
||||
node.horizontalTotals,
|
||||
) as IBalanceSheetTotal[];
|
||||
|
||||
return R.assoc('horizontalTotals', horizontalTotals, node);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
IBalanceSheetDataNode,
|
||||
IBalanceSheetNetIncomeNode,
|
||||
} from './BalanceSheet.types';
|
||||
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
|
||||
import { FinancialPreviousPeriod } from '../../common/FinancialPreviousPeriod';
|
||||
import { FinancialHorizTotals } from '../../common/FinancialHorizTotals';
|
||||
import { BalanceSheetRepository } from './BalanceSheetRepository';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { BalanceSheetNetIncomeDatePeriodsPP } from './BalanceSheetNetIncomeDatePeriodsPP';
|
||||
import { FinancialSheet } from '../../common/FinancialSheet';
|
||||
import { GConstructor } from '@/common/types/Constructor';
|
||||
|
||||
export const BalanceSheetNetIncomePP = <T extends GConstructor<FinancialSheet>>(
|
||||
Base: T,
|
||||
) =>
|
||||
class extends R.pipe(
|
||||
BalanceSheetNetIncomeDatePeriodsPP,
|
||||
BalanceSheetComparsionPreviousPeriod,
|
||||
FinancialPreviousPeriod,
|
||||
FinancialHorizTotals,
|
||||
)(Base) {
|
||||
public repository: BalanceSheetRepository;
|
||||
public query: BalanceSheetQuery;
|
||||
|
||||
// -------------------------------
|
||||
// # Previous Period (PP)
|
||||
// -------------------------------
|
||||
/**
|
||||
* Retrieves the PP net income.
|
||||
* @returns {}
|
||||
*/
|
||||
public getPreviousPeriodNetIncome = () => {
|
||||
const income = this.repository.incomePPAccountsLedger.getClosingBalance();
|
||||
const expense =
|
||||
this.repository.expensePPAccountsLedger.getClosingBalance();
|
||||
|
||||
return income - expense;
|
||||
};
|
||||
|
||||
/**
|
||||
* Associates the previous period to account node.
|
||||
* @param {IBalanceSheetDataNode} node
|
||||
* @returns {IBalanceSheetDataNode}
|
||||
*/
|
||||
public assocPreviousPeriodNetIncomeNode = (
|
||||
node: IBalanceSheetDataNode,
|
||||
): IBalanceSheetDataNode => {
|
||||
const total = this.getPreviousPeriodNetIncome();
|
||||
|
||||
return R.assoc('previousPeriod', this.getAmountMeta(total), node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Previous period account node composer.
|
||||
* @param {IBalanceSheetNetIncomeNode} node
|
||||
* @returns {IBalanceSheetNetIncomeNode}
|
||||
*/
|
||||
public previousPeriodNetIncomeNodeCompose = (
|
||||
node: IBalanceSheetNetIncomeNode,
|
||||
): IBalanceSheetNetIncomeNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizTotals,
|
||||
this.assocPreviousPeriodNetIncomeHorizNode,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
this.assocPreviousPeriodPercentageNode,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
this.assocPreviousPeriodChangeNode,
|
||||
),
|
||||
this.assocPreviousPeriodNetIncomeNode,
|
||||
)(node);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,80 @@
|
||||
import * as R from 'ramda';
|
||||
import { IBalanceSheetNetIncomeNode } from './BalanceSheet.types';
|
||||
import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear';
|
||||
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
|
||||
import { FinancialPreviousPeriod } from '../../common/FinancialPreviousPeriod';
|
||||
import { FinancialHorizTotals } from '../../common/FinancialHorizTotals';
|
||||
import { BalanceSheetRepository } from './BalanceSheetRepository';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { BalanceSheetNetIncomeDatePeriodsPY } from './BalanceSheetNetIncomeDatePeriodsPY';
|
||||
import { FinancialSheet } from '../../common/FinancialSheet';
|
||||
import { GConstructor } from '@/common/types/Constructor';
|
||||
|
||||
export const BalanceSheetNetIncomePY = <T extends GConstructor<FinancialSheet>>(
|
||||
Base: T,
|
||||
) =>
|
||||
class extends R.pipe(
|
||||
BalanceSheetNetIncomeDatePeriodsPY,
|
||||
BalanceSheetComparsionPreviousYear,
|
||||
BalanceSheetComparsionPreviousPeriod,
|
||||
FinancialPreviousPeriod,
|
||||
FinancialHorizTotals,
|
||||
)(Base) {
|
||||
// public repository: BalanceSheetRepository;
|
||||
// public query: BalanceSheetQuery;
|
||||
|
||||
// ------------------------------
|
||||
// # Previous Year (PY)
|
||||
// ------------------------------
|
||||
/**
|
||||
* Retrieves the previous year (PY) net income.
|
||||
* @returns {number}
|
||||
*/
|
||||
public getPreviousYearNetIncome = () => {
|
||||
const income =
|
||||
this.repository.incomePYTotalAccountsLedger.getClosingBalance();
|
||||
const expense =
|
||||
this.repository.expensePYTotalAccountsLedger.getClosingBalance();
|
||||
|
||||
return income - expense;
|
||||
};
|
||||
|
||||
/**
|
||||
* Assoc previous year on aggregate node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
public assocPreviousYearNetIncomeNode = (
|
||||
node: IBalanceSheetNetIncomeNode,
|
||||
): IBalanceSheetNetIncomeNode => {
|
||||
const total = this.getPreviousYearNetIncome();
|
||||
|
||||
return R.assoc('previousYear', this.getTotalAmountMeta(total), node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Assoc previous year attributes to aggregate node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
public previousYearNetIncomeNodeCompose = (
|
||||
node: IBalanceSheetNetIncomeNode,
|
||||
): IBalanceSheetNetIncomeNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
this.assocPreviousYearTotalPercentageNode,
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
this.assocPreviousYearTotalChangeNode,
|
||||
),
|
||||
// Associate the PY to date periods horizontal nodes.
|
||||
R.when(
|
||||
this.isNodeHasHorizontalTotals,
|
||||
this.assocPreviousYearNetIncomeHorizNode,
|
||||
),
|
||||
this.assocPreviousYearNetIncomeNode,
|
||||
)(node);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import { TableSheetPdf } from '../../common/TableSheetPdf';
|
||||
import { IBalanceSheetQuery } from './BalanceSheet.types';
|
||||
import { BalanceSheetTableInjectable } from './BalanceSheetTableInjectable';
|
||||
import { HtmlTableCustomCss } from './constants';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class BalanceSheetPdfInjectable {
|
||||
constructor(
|
||||
private readonly balanceSheetTable: BalanceSheetTableInjectable,
|
||||
private readonly tableSheetPdf: TableSheetPdf,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Converts the given balance sheet table to pdf.
|
||||
* @param {IBalanceSheetQuery} query - Balance sheet query.
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
public async pdf(query: IBalanceSheetQuery): Promise<Buffer> {
|
||||
const table = await this.balanceSheetTable.table(query);
|
||||
|
||||
return this.tableSheetPdf.convertToPdf(
|
||||
table.table,
|
||||
table.meta.sheetName,
|
||||
table.meta.formattedDateRange,
|
||||
HtmlTableCustomCss,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
import * as R from 'ramda';
|
||||
import { get } from 'lodash';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { IBalanceSheetDataNode } from './BalanceSheet.types';
|
||||
import { GConstructor } from '@/common/types/Constructor';
|
||||
import { FinancialSheet } from '../../common/FinancialSheet';
|
||||
|
||||
export const BalanceSheetPercentage = <T extends GConstructor<FinancialSheet>>(
|
||||
Base: T,
|
||||
) =>
|
||||
class extends Base {
|
||||
readonly query: BalanceSheetQuery;
|
||||
|
||||
/**
|
||||
* Assoc percentage of column to report node.
|
||||
* @param {IBalanceSheetDataNode} node
|
||||
* @returns {IBalanceSheetDataNode}
|
||||
*/
|
||||
public assocReportNodeColumnPercentage = R.curry(
|
||||
(
|
||||
parentTotal: number,
|
||||
node: IBalanceSheetDataNode,
|
||||
): IBalanceSheetDataNode => {
|
||||
const percentage = this.getPercentageBasis(
|
||||
parentTotal,
|
||||
node.total.amount,
|
||||
);
|
||||
return R.assoc(
|
||||
'percentageColumn',
|
||||
this.getPercentageAmountMeta(percentage),
|
||||
node,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Assoc percentage of row to report node.
|
||||
* @param {IBalanceSheetDataNode} node
|
||||
* @returns {IBalanceSheetDataNode}
|
||||
*/
|
||||
public assocReportNodeRowPercentage = R.curry(
|
||||
(
|
||||
parentTotal: number,
|
||||
node: IBalanceSheetDataNode,
|
||||
): IBalanceSheetDataNode => {
|
||||
const percenatage = this.getPercentageBasis(
|
||||
parentTotal,
|
||||
node.total.amount,
|
||||
);
|
||||
return R.assoc(
|
||||
'percentageRow',
|
||||
this.getPercentageAmountMeta(percenatage),
|
||||
node,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Assoc percentage of row to horizontal total.
|
||||
* @param {number} parentTotal -
|
||||
* @param {IBalanceSheetDataNode} node
|
||||
* @returns {IBalanceSheetDataNode}
|
||||
*/
|
||||
public assocRowPercentageHorizTotals = R.curry(
|
||||
(
|
||||
parentTotal: number,
|
||||
node: IBalanceSheetDataNode,
|
||||
): IBalanceSheetDataNode => {
|
||||
const assocRowPercen = this.assocReportNodeRowPercentage(parentTotal);
|
||||
const horTotals = R.map(assocRowPercen)(node.horizontalTotals);
|
||||
|
||||
return R.assoc('horizontalTotals', horTotals, node);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {} parentNode -
|
||||
* @param {} horTotalNode -
|
||||
* @param {number} index -
|
||||
*/
|
||||
private assocColumnPercentageHorizTotal = R.curry(
|
||||
(parentNode, horTotalNode, index) => {
|
||||
const parentTotal = get(
|
||||
parentNode,
|
||||
`horizontalTotals[${index}].total.amount`,
|
||||
0,
|
||||
);
|
||||
return this.assocReportNodeColumnPercentage(parentTotal, horTotalNode);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Assoc column percentage to horizontal totals nodes.
|
||||
* @param {IBalanceSheetDataNode} node
|
||||
* @returns {IBalanceSheetDataNode}
|
||||
*/
|
||||
public assocColumnPercentageHorizTotals = R.curry(
|
||||
(
|
||||
parentNode: IBalanceSheetDataNode,
|
||||
node: IBalanceSheetDataNode,
|
||||
): IBalanceSheetDataNode => {
|
||||
// Horizontal totals.
|
||||
const assocColPerc = this.assocColumnPercentageHorizTotal(parentNode);
|
||||
const horTotals = R.addIndex(R.map)(assocColPerc)(
|
||||
node.horizontalTotals,
|
||||
);
|
||||
return R.assoc('horizontalTotals', horTotals, node);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} parentTotal -
|
||||
* @param {} node
|
||||
* @returns
|
||||
*/
|
||||
public reportNodeColumnPercentageComposer = R.curry((parentNode, node) => {
|
||||
const parentTotal = parentNode.total.amount;
|
||||
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizoTotals,
|
||||
this.assocColumnPercentageHorizTotals(parentNode),
|
||||
),
|
||||
this.assocReportNodeColumnPercentage(parentTotal),
|
||||
)(node);
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* @returns
|
||||
*/
|
||||
private reportNodeRowPercentageComposer = (node) => {
|
||||
const total = node.total.amount;
|
||||
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizoTotals,
|
||||
this.assocRowPercentageHorizTotals(total),
|
||||
),
|
||||
this.assocReportNodeRowPercentage(total),
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private assocNodeColumnPercentageChildren = (node) => {
|
||||
const children = this.mapNodesDeep(
|
||||
node.children,
|
||||
this.reportNodeColumnPercentageComposer(node),
|
||||
);
|
||||
return R.assoc('children', children, node);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* @returns
|
||||
*/
|
||||
private reportNodeColumnPercentageDeepMap = (node) => {
|
||||
const parentTotal = node.total.amount;
|
||||
const parentNode = node;
|
||||
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizoTotals,
|
||||
this.assocColumnPercentageHorizTotals(parentNode),
|
||||
),
|
||||
this.assocReportNodeColumnPercentage(parentTotal),
|
||||
this.assocNodeColumnPercentageChildren,
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IBalanceSheetDataNode[]} node
|
||||
* @returns {IBalanceSheetDataNode[]}
|
||||
*/
|
||||
private reportColumnsPercentageMapper = (
|
||||
nodes: IBalanceSheetDataNode[],
|
||||
): IBalanceSheetDataNode[] => {
|
||||
return R.map(this.reportNodeColumnPercentageDeepMap, nodes);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param nodes
|
||||
* @returns
|
||||
*/
|
||||
private reportRowsPercentageMapper = (nodes) => {
|
||||
return this.mapNodesDeep(nodes, this.reportNodeRowPercentageComposer);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param nodes
|
||||
* @returns
|
||||
*/
|
||||
public reportPercentageCompose = (nodes) => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isColumnsPercentageActive,
|
||||
this.reportColumnsPercentageMapper,
|
||||
),
|
||||
R.when(
|
||||
this.query.isRowsPercentageActive,
|
||||
this.reportRowsPercentageMapper,
|
||||
),
|
||||
)(nodes);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines whether the given node has horizontal total.
|
||||
* @param {IBalanceSheetDataNode} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isNodeHasHorizoTotals = (node: IBalanceSheetDataNode): boolean => {
|
||||
return (
|
||||
!R.isEmpty(node.horizontalTotals) && !R.isNil(node.horizontalTotals)
|
||||
);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,185 @@
|
||||
import { merge } from 'lodash';
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
IBalanceSheetQuery,
|
||||
IFinancialDatePeriodsUnit,
|
||||
} from './BalanceSheet.types';
|
||||
import { FinancialDateRanges } from '../../common/FinancialDateRanges';
|
||||
import { DISPLAY_COLUMNS_BY } from './constants';
|
||||
|
||||
export class BalanceSheetQuery extends R.compose(FinancialDateRanges)(
|
||||
class {},
|
||||
) {
|
||||
/**
|
||||
* Balance sheet query.
|
||||
* @param {IBalanceSheetQuery}
|
||||
*/
|
||||
public readonly query: IBalanceSheetQuery;
|
||||
|
||||
/**
|
||||
* Previous year to date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public readonly PYToDate: Date;
|
||||
|
||||
/**
|
||||
* Previous year from date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public readonly PYFromDate: Date;
|
||||
|
||||
/**
|
||||
* Previous period to date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public readonly PPToDate: Date;
|
||||
|
||||
/**
|
||||
* Previous period from date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public readonly PPFromDate: Date;
|
||||
|
||||
/**
|
||||
* Constructor method
|
||||
* @param {IBalanceSheetQuery} query
|
||||
*/
|
||||
constructor(query: IBalanceSheetQuery) {
|
||||
super();
|
||||
this.query = query;
|
||||
|
||||
// Pervious Year (PY) Dates.
|
||||
this.PYToDate = this.getPreviousYearDate(this.query.toDate);
|
||||
this.PYFromDate = this.getPreviousYearDate(this.query.fromDate);
|
||||
|
||||
// Previous Period (PP) Dates for Total column.
|
||||
if (this.isTotalColumnType()) {
|
||||
const { fromDate, toDate } = this.getPPTotalDateRange(
|
||||
this.query.fromDate,
|
||||
this.query.toDate,
|
||||
);
|
||||
this.PPToDate = toDate;
|
||||
this.PPFromDate = fromDate;
|
||||
// Previous Period (PP) Dates for Date period columns type.
|
||||
} else if (this.isDatePeriodsColumnsType()) {
|
||||
const { fromDate, toDate } = this.getPPDatePeriodDateRange(
|
||||
this.query.fromDate,
|
||||
this.query.toDate,
|
||||
this.query.displayColumnsBy as IFinancialDatePeriodsUnit,
|
||||
);
|
||||
this.PPToDate = toDate;
|
||||
this.PPFromDate = fromDate;
|
||||
}
|
||||
return merge(this, query);
|
||||
}
|
||||
|
||||
// ---------------------------
|
||||
// # Columns Type/By.
|
||||
// ---------------------------
|
||||
/**
|
||||
* Detarmines the given display columns type.
|
||||
* @param {string} displayColumnsBy
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isDisplayColumnsBy = (displayColumnsBy: string): boolean => {
|
||||
return this.query.displayColumnsBy === displayColumnsBy;
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines the given display columns by type.
|
||||
* @param {string} displayColumnsBy
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isDisplayColumnsType = (displayColumnsType: string): boolean => {
|
||||
return this.query.displayColumnsType === displayColumnsType;
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines whether the columns type is date periods.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isDatePeriodsColumnsType = (): boolean => {
|
||||
return this.isDisplayColumnsType(DISPLAY_COLUMNS_BY.DATE_PERIODS);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines whether the columns type is total.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isTotalColumnType = (): boolean => {
|
||||
return this.isDisplayColumnsType(DISPLAY_COLUMNS_BY.TOTAL);
|
||||
};
|
||||
|
||||
// ---------------------------
|
||||
// # Percentage column/row.
|
||||
// ---------------------------
|
||||
/**
|
||||
* Detarmines whether the percentage of column active.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isColumnsPercentageActive = (): boolean => {
|
||||
return this.query.percentageOfColumn;
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines whether the percentage of row active.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isRowsPercentageActive = (): boolean => {
|
||||
return this.query.percentageOfRow;
|
||||
};
|
||||
|
||||
// ---------------------------
|
||||
// # Previous Year (PY)
|
||||
// ---------------------------
|
||||
/**
|
||||
* Detarmines the report query has previous year enabled.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isPreviousYearActive = (): boolean => {
|
||||
return this.query.previousYear;
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines the report query has previous year percentage change active.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isPreviousYearPercentageActive = (): boolean => {
|
||||
return this.query.previousYearPercentageChange;
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines the report query has previous year change active.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isPreviousYearChangeActive = (): boolean => {
|
||||
return this.query.previousYearAmountChange;
|
||||
};
|
||||
|
||||
// ---------------------------
|
||||
// # Previous Period (PP).
|
||||
// ---------------------------
|
||||
/**
|
||||
* Detarmines the report query has previous period enabled.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isPreviousPeriodActive = (): boolean => {
|
||||
return this.query.previousPeriod;
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines wether the preivous period percentage is active.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isPreviousPeriodPercentageActive = (): boolean => {
|
||||
return this.query.previousPeriodPercentageChange;
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines wether the previous period change is active.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isPreviousPeriodChangeActive = (): boolean => {
|
||||
return this.query.previousPeriodAmountChange;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
import { Inject, Injectable, Scope } from '@nestjs/common';
|
||||
import * as R from 'ramda';
|
||||
import { Knex } from 'knex';
|
||||
import { isEmpty } from 'lodash';
|
||||
import {
|
||||
IAccountTransactionsGroupBy,
|
||||
IBalanceSheetQuery,
|
||||
} from './BalanceSheet.types';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { FinancialDatePeriods } from '../../common/FinancialDatePeriods';
|
||||
import { BalanceSheetRepositoryNetIncome } from './BalanceSheetRepositoryNetIncome';
|
||||
import { ILedger } from '@/modules/Ledger/types/Ledger.types';
|
||||
import { Ledger } from '@/modules/Ledger/Ledger';
|
||||
import { transformToMapBy } from '@/utils/transform-to-map-by';
|
||||
import { Account } from '@/modules/Accounts/models/Account.model';
|
||||
import { AccountTransaction } from '@/modules/Accounts/models/AccountTransaction.model';
|
||||
|
||||
@Injectable({ scope: Scope.TRANSIENT })
|
||||
export class BalanceSheetRepository extends R.compose(
|
||||
BalanceSheetRepositoryNetIncome,
|
||||
FinancialDatePeriods,
|
||||
)(class {}) {
|
||||
/**
|
||||
* Account model.
|
||||
*/
|
||||
@Inject(Account.name)
|
||||
public readonly accountModel: typeof Account;
|
||||
|
||||
/**
|
||||
* Account transaction model.
|
||||
*/
|
||||
@Inject(AccountTransaction.name)
|
||||
public readonly accountTransactionModel: typeof AccountTransaction;
|
||||
|
||||
/**
|
||||
* @description Balance sheet query.
|
||||
* @param {BalanceSheetQuery}
|
||||
*/
|
||||
public query: BalanceSheetQuery;
|
||||
|
||||
/**
|
||||
* @param {}
|
||||
*/
|
||||
public accounts: any;
|
||||
|
||||
/**
|
||||
* @param {}
|
||||
*/
|
||||
public accountsGraph: any;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public accountsByType: any;
|
||||
|
||||
/**
|
||||
* PY from date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public readonly PYFromDate: Date;
|
||||
|
||||
/**
|
||||
* PY to date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public readonly PYToDate: Date;
|
||||
|
||||
/**
|
||||
* PP to date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public readonly PPToDate: Date;
|
||||
|
||||
/**
|
||||
* PP from date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public readonly PPFromDate: Date;
|
||||
|
||||
/**
|
||||
* Total closing accounts ledger.
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public totalAccountsLedger: Ledger;
|
||||
|
||||
/**
|
||||
* Total income accounts ledger.
|
||||
*/
|
||||
public incomeLedger: Ledger;
|
||||
|
||||
/**
|
||||
* Total expense accounts ledger.
|
||||
*/
|
||||
public expensesLedger: Ledger;
|
||||
|
||||
/**
|
||||
* Transactions group type.
|
||||
* @param {IAccountTransactionsGroupBy}
|
||||
*/
|
||||
public transactionsGroupType: IAccountTransactionsGroupBy =
|
||||
IAccountTransactionsGroupBy.Month;
|
||||
|
||||
// -----------------------
|
||||
// # Date Periods
|
||||
// -----------------------
|
||||
/**
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public periodsAccountsLedger: Ledger;
|
||||
|
||||
/**
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public periodsOpeningAccountLedger: Ledger;
|
||||
|
||||
// -----------------------
|
||||
// # Previous Year (PY).
|
||||
// -----------------------
|
||||
/**
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public PYPeriodsOpeningAccountLedger: Ledger;
|
||||
|
||||
/**
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public PYPeriodsAccountsLedger: Ledger;
|
||||
|
||||
/**
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public PYTotalAccountsLedger: ILedger;
|
||||
|
||||
// -----------------------
|
||||
// # Previous Period (PP).
|
||||
// -----------------------
|
||||
/**
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public PPTotalAccountsLedger: Ledger;
|
||||
|
||||
/**
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public PPPeriodsAccountsLedger: ILedger;
|
||||
|
||||
/**
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public PPPeriodsOpeningAccountLedger: ILedger;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {IBalanceSheetQuery} query
|
||||
*/
|
||||
public setQuery(query: IBalanceSheetQuery) {
|
||||
this.query = new BalanceSheetQuery(query);
|
||||
|
||||
this.transactionsGroupType = this.getGroupByFromDisplayColumnsBy(
|
||||
this.query.displayColumnsBy,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Async initialize.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public asyncInitialize = async (query: IBalanceSheetQuery) => {
|
||||
this.setQuery(query);
|
||||
|
||||
await this.initAccounts();
|
||||
await this.initAccountsGraph();
|
||||
|
||||
await this.initAccountsTotalLedger();
|
||||
|
||||
// Date periods.
|
||||
if (this.query.isDatePeriodsColumnsType()) {
|
||||
await this.initTotalDatePeriods();
|
||||
}
|
||||
// Previous Year (PY).
|
||||
if (this.query.isPreviousYearActive()) {
|
||||
await this.initTotalPreviousYear();
|
||||
}
|
||||
if (
|
||||
this.query.isPreviousYearActive() &&
|
||||
this.query.isDatePeriodsColumnsType()
|
||||
) {
|
||||
await this.initPeriodsPreviousYear();
|
||||
}
|
||||
// Previous Period (PP).
|
||||
if (this.query.isPreviousPeriodActive()) {
|
||||
await this.initTotalPreviousPeriod();
|
||||
}
|
||||
if (
|
||||
this.query.isPreviousPeriodActive() &&
|
||||
this.query.isDatePeriodsColumnsType()
|
||||
) {
|
||||
await this.initPeriodsPreviousPeriod();
|
||||
}
|
||||
//
|
||||
await this.asyncInitializeNetIncome();
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Accounts
|
||||
// ----------------------------
|
||||
public initAccounts = async () => {
|
||||
const accounts = await this.getAccounts();
|
||||
|
||||
this.accounts = accounts;
|
||||
this.accountsByType = transformToMapBy(accounts, 'accountType');
|
||||
this.accountsByParentType = transformToMapBy(accounts, 'accountParentType');
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize accounts graph.
|
||||
*/
|
||||
public initAccountsGraph = async () => {
|
||||
this.accountsGraph = this.accountModel.toDependencyGraph(this.accounts);
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Closing Total
|
||||
// ----------------------------
|
||||
/**
|
||||
* Initialize accounts closing total based on the given query.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public initAccountsTotalLedger = async (): Promise<void> => {
|
||||
const totalByAccount = await this.closingAccountsTotal(this.query.toDate);
|
||||
|
||||
// Inject to the repository.
|
||||
this.totalAccountsLedger = Ledger.fromTransactions(totalByAccount);
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Date periods.
|
||||
// ----------------------------
|
||||
/**
|
||||
* Initialize date periods total.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public initTotalDatePeriods = async (): Promise<void> => {
|
||||
// Retrieves grouped transactions by given date group.
|
||||
const periodsByAccount = await this.accountsDatePeriods(
|
||||
this.query.fromDate,
|
||||
this.query.toDate,
|
||||
this.transactionsGroupType,
|
||||
);
|
||||
// Retrieves opening balance of grouped transactions.
|
||||
const periodsOpeningByAccount = await this.closingAccountsTotal(
|
||||
this.query.fromDate,
|
||||
);
|
||||
// Inject to the repository.
|
||||
this.periodsAccountsLedger = Ledger.fromTransactions(periodsByAccount);
|
||||
this.periodsOpeningAccountLedger = Ledger.fromTransactions(
|
||||
periodsOpeningByAccount,
|
||||
);
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Previous Year (PY).
|
||||
// ----------------------------
|
||||
/**
|
||||
* Initialize total of previous year.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public initTotalPreviousYear = async (): Promise<void> => {
|
||||
const PYTotalsByAccounts = await this.closingAccountsTotal(
|
||||
this.query.PYToDate,
|
||||
);
|
||||
// Inject to the repository.
|
||||
this.PYTotalAccountsLedger = Ledger.fromTransactions(PYTotalsByAccounts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize date periods of previous year.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public initPeriodsPreviousYear = async (): Promise<void> => {
|
||||
const PYPeriodsBYAccounts = await this.accountsDatePeriods(
|
||||
this.query.PYFromDate,
|
||||
this.query.PYToDate,
|
||||
this.transactionsGroupType,
|
||||
);
|
||||
// Retrieves opening balance of grouped transactions.
|
||||
const periodsOpeningByAccount = await this.closingAccountsTotal(
|
||||
this.query.PYFromDate,
|
||||
);
|
||||
// Inject to the repository.
|
||||
this.PYPeriodsAccountsLedger = Ledger.fromTransactions(PYPeriodsBYAccounts);
|
||||
this.PYPeriodsOpeningAccountLedger = Ledger.fromTransactions(
|
||||
periodsOpeningByAccount,
|
||||
);
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Previous Year (PP).
|
||||
// ----------------------------
|
||||
/**
|
||||
* Initialize total of previous year.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public initTotalPreviousPeriod = async (): Promise<void> => {
|
||||
const PPTotalsByAccounts = await this.closingAccountsTotal(
|
||||
this.query.PPToDate,
|
||||
);
|
||||
// Inject to the repository.
|
||||
this.PPTotalAccountsLedger = Ledger.fromTransactions(PPTotalsByAccounts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize date periods of previous year.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public initPeriodsPreviousPeriod = async (): Promise<void> => {
|
||||
const PPPeriodsBYAccounts = await this.accountsDatePeriods(
|
||||
this.query.PPFromDate,
|
||||
this.query.PPToDate,
|
||||
this.transactionsGroupType,
|
||||
);
|
||||
// Retrieves opening balance of grouped transactions.
|
||||
const periodsOpeningByAccount = await this.closingAccountsTotal(
|
||||
this.query.PPFromDate,
|
||||
);
|
||||
// Inject to the repository.
|
||||
this.PPPeriodsAccountsLedger = Ledger.fromTransactions(PPPeriodsBYAccounts);
|
||||
this.PPPeriodsOpeningAccountLedger = Ledger.fromTransactions(
|
||||
periodsOpeningByAccount,
|
||||
);
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Utils
|
||||
// ----------------------------
|
||||
/**
|
||||
* Retrieve accounts of the report.
|
||||
* @return {Promise<IAccount[]>}
|
||||
*/
|
||||
public getAccounts = () => {
|
||||
return this.accountModel.query();
|
||||
};
|
||||
|
||||
/**
|
||||
* Closing accounts date periods.
|
||||
* @param {Date} fromDate
|
||||
* @param {Date} toDate
|
||||
* @param {string} datePeriodsType
|
||||
* @returns
|
||||
*/
|
||||
public accountsDatePeriods = async (
|
||||
fromDate: Date,
|
||||
toDate: Date,
|
||||
datePeriodsType: string,
|
||||
) => {
|
||||
return this.accountTransactionModel.query().onBuild((query) => {
|
||||
query.sum('credit as credit');
|
||||
query.sum('debit as debit');
|
||||
query.groupBy('accountId');
|
||||
query.select(['accountId']);
|
||||
|
||||
query.modify('groupByDateFormat', datePeriodsType);
|
||||
query.modify('filterDateRange', fromDate, toDate);
|
||||
query.withGraphFetched('account');
|
||||
|
||||
this.commonFilterBranchesQuery(query);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the opening balance transactions of the report.
|
||||
* @param {Date|string} openingDate -
|
||||
*/
|
||||
public closingAccountsTotal = async (openingDate: Date | string) => {
|
||||
return this.accountTransactionModel.query().onBuild((query) => {
|
||||
query.sum('credit as credit');
|
||||
query.sum('debit as debit');
|
||||
query.groupBy('accountId');
|
||||
query.select(['accountId']);
|
||||
|
||||
query.modify('filterDateRange', null, openingDate);
|
||||
query.withGraphFetched('account');
|
||||
|
||||
this.commonFilterBranchesQuery(query);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Common branches filter query.
|
||||
* @param {Knex.QueryBuilder} query
|
||||
*/
|
||||
public commonFilterBranchesQuery = (query: Knex.QueryBuilder) => {
|
||||
if (!isEmpty(this.query.branchesIds)) {
|
||||
query.modify('filterByBranches', this.query.branchesIds);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
import * as R from 'ramda';
|
||||
import { FinancialDatePeriods } from '../../common/FinancialDatePeriods';
|
||||
import { ModelObject } from 'objection';
|
||||
import { Account } from '@/modules/Accounts/models/Account.model';
|
||||
import { ILedger } from '@/modules/Ledger/types/Ledger.types';
|
||||
import { ACCOUNT_PARENT_TYPE } from '@/constants/accounts';
|
||||
import { GConstructor } from '@/common/types/Constructor';
|
||||
import { FinancialSheet } from '../../common/FinancialSheet';
|
||||
|
||||
export const BalanceSheetRepositoryNetIncome = <
|
||||
T extends GConstructor<FinancialSheet>,
|
||||
>(
|
||||
Base: T,
|
||||
) =>
|
||||
class extends R.pipe(FinancialDatePeriods)(Base) {
|
||||
// -----------------------
|
||||
// # Net Income
|
||||
// -----------------------
|
||||
public incomeAccounts: ModelObject<Account>[];
|
||||
public incomeAccountsIds: number[];
|
||||
|
||||
public expenseAccounts: ModelObject<Account>[];
|
||||
public expenseAccountsIds: number[];
|
||||
|
||||
public incomePeriodsAccountsLedger: ILedger;
|
||||
public incomePeriodsOpeningAccountsLedger: ILedger;
|
||||
public expensesPeriodsAccountsLedger: ILedger;
|
||||
public expensesOpeningAccountLedger: ILedger;
|
||||
|
||||
public incomePPAccountsLedger: ILedger;
|
||||
public expensePPAccountsLedger: ILedger;
|
||||
|
||||
public incomePPPeriodsAccountsLedger: ILedger;
|
||||
public incomePPPeriodsOpeningAccountLedger: ILedger;
|
||||
public expensePPPeriodsAccountsLedger: ILedger;
|
||||
public expensePPPeriodsOpeningAccountLedger: ILedger;
|
||||
|
||||
public incomePYTotalAccountsLedger: ILedger;
|
||||
public expensePYTotalAccountsLedger: ILedger;
|
||||
|
||||
public incomePYPeriodsAccountsLedger: ILedger;
|
||||
public incomePYPeriodsOpeningAccountLedger: ILedger;
|
||||
public expensePYPeriodsAccountsLedger: ILedger;
|
||||
public expensePYPeriodsOpeningAccountLedger: ILedger;
|
||||
|
||||
/**
|
||||
* Async initialize.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public asyncInitializeNetIncome = async () => {
|
||||
await this.initAccounts();
|
||||
await this.initAccountsTotalLedger();
|
||||
|
||||
// Net Income
|
||||
this.initIncomeAccounts();
|
||||
this.initExpenseAccounts();
|
||||
|
||||
this.initIncomeTotalLedger();
|
||||
this.initExpensesTotalLedger();
|
||||
|
||||
// Date periods
|
||||
if (this.query.isDatePeriodsColumnsType()) {
|
||||
this.initNetIncomeDatePeriods();
|
||||
}
|
||||
// Previous Year (PY).
|
||||
if (this.query.isPreviousYearActive()) {
|
||||
this.initNetIncomePreviousYear();
|
||||
}
|
||||
// Previous Period (PP).
|
||||
if (this.query.isPreviousPeriodActive()) {
|
||||
this.initNetIncomePreviousPeriod();
|
||||
}
|
||||
// Previous Year (PY) / Date Periods.
|
||||
if (
|
||||
this.query.isPreviousYearActive() &&
|
||||
this.query.isDatePeriodsColumnsType()
|
||||
) {
|
||||
this.initNetIncomePeriodsPreviewYear();
|
||||
}
|
||||
// Previous Period (PP) / Date Periods.
|
||||
if (
|
||||
this.query.isPreviousPeriodActive() &&
|
||||
this.query.isDatePeriodsColumnsType()
|
||||
) {
|
||||
this.initNetIncomePeriodsPreviousPeriod();
|
||||
}
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Net Income
|
||||
// ----------------------------
|
||||
/**
|
||||
* Initialize income accounts.
|
||||
*/
|
||||
public initIncomeAccounts = () => {
|
||||
const incomeAccounts = this.accountsByParentType.get(
|
||||
ACCOUNT_PARENT_TYPE.INCOME,
|
||||
);
|
||||
const incomeAccountsIds = incomeAccounts.map((a) => a.id);
|
||||
|
||||
this.incomeAccounts = incomeAccounts;
|
||||
this.incomeAccountsIds = incomeAccountsIds;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize expense accounts.
|
||||
*/
|
||||
public initExpenseAccounts = () => {
|
||||
const expensesAccounts = this.accountsByParentType.get(
|
||||
ACCOUNT_PARENT_TYPE.EXPENSE,
|
||||
);
|
||||
const expensesAccountsIds = expensesAccounts.map((a) => a.id);
|
||||
|
||||
this.expenseAccounts = expensesAccounts;
|
||||
this.expenseAccountsIds = expensesAccountsIds;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the income total ledger.
|
||||
*/
|
||||
public initIncomeTotalLedger = (): void => {
|
||||
// Inject to the repository.
|
||||
this.incomeLedger = this.totalAccountsLedger.whereAccountsIds(
|
||||
this.incomeAccountsIds,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the expenses total ledger.
|
||||
*/
|
||||
public initExpensesTotalLedger = (): void => {
|
||||
this.expensesLedger = this.totalAccountsLedger.whereAccountsIds(
|
||||
this.expenseAccountsIds,
|
||||
);
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Net Income - Date Periods
|
||||
// ----------------------------
|
||||
/**
|
||||
* Initialize the net income date periods.
|
||||
*/
|
||||
public initNetIncomeDatePeriods = () => {
|
||||
this.incomePeriodsAccountsLedger =
|
||||
this.periodsAccountsLedger.whereAccountsIds(this.incomeAccountsIds);
|
||||
|
||||
this.incomePeriodsOpeningAccountsLedger =
|
||||
this.periodsOpeningAccountLedger.whereAccountsIds(
|
||||
this.incomeAccountsIds,
|
||||
);
|
||||
|
||||
this.expensesPeriodsAccountsLedger =
|
||||
this.periodsAccountsLedger.whereAccountsIds(this.expenseAccountsIds);
|
||||
|
||||
this.expensesOpeningAccountLedger =
|
||||
this.periodsOpeningAccountLedger.whereAccountsIds(
|
||||
this.expenseAccountsIds,
|
||||
);
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Net Income - Previous Period
|
||||
// ----------------------------
|
||||
/**
|
||||
* Initialize the total net income PP.
|
||||
*/
|
||||
public initNetIncomePreviousPeriod = () => {
|
||||
this.incomePPAccountsLedger = this.PPTotalAccountsLedger.whereAccountsIds(
|
||||
this.incomeAccountsIds,
|
||||
);
|
||||
this.expensePPAccountsLedger =
|
||||
this.PPTotalAccountsLedger.whereAccountsIds(this.expenseAccountsIds);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the net income periods of previous period.
|
||||
*/
|
||||
public initNetIncomePeriodsPreviousPeriod = () => {
|
||||
this.incomePPPeriodsAccountsLedger =
|
||||
this.PPPeriodsAccountsLedger.whereAccountsIds(this.incomeAccountsIds);
|
||||
|
||||
this.incomePPPeriodsOpeningAccountLedger =
|
||||
this.PPPeriodsOpeningAccountLedger.whereAccountsIds(
|
||||
this.incomeAccountsIds,
|
||||
);
|
||||
|
||||
this.expensePPPeriodsAccountsLedger =
|
||||
this.PPPeriodsAccountsLedger.whereAccountsIds(this.expenseAccountsIds);
|
||||
|
||||
this.expensePPPeriodsOpeningAccountLedger =
|
||||
this.PPPeriodsOpeningAccountLedger.whereAccountsIds(
|
||||
this.expenseAccountsIds,
|
||||
);
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Net Income - Previous Year
|
||||
// ----------------------------
|
||||
/**
|
||||
* Initialize the net income PY total.
|
||||
*/
|
||||
public initNetIncomePreviousYear = () => {
|
||||
this.incomePYTotalAccountsLedger =
|
||||
this.PYTotalAccountsLedger.whereAccountsIds(this.incomeAccountsIds);
|
||||
|
||||
this.expensePYTotalAccountsLedger =
|
||||
this.PYTotalAccountsLedger.whereAccountsIds(this.expenseAccountsIds);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the net income PY periods.
|
||||
*/
|
||||
public initNetIncomePeriodsPreviewYear = () => {
|
||||
this.incomePYPeriodsAccountsLedger =
|
||||
this.PYPeriodsAccountsLedger.whereAccountsIds(this.incomeAccountsIds);
|
||||
|
||||
this.incomePYPeriodsOpeningAccountLedger =
|
||||
this.PYPeriodsOpeningAccountLedger.whereAccountsIds(
|
||||
this.incomeAccountsIds,
|
||||
);
|
||||
|
||||
this.expensePYPeriodsAccountsLedger =
|
||||
this.PYPeriodsAccountsLedger.whereAccountsIds(this.expenseAccountsIds);
|
||||
|
||||
this.expensePYPeriodsOpeningAccountLedger =
|
||||
this.PYPeriodsOpeningAccountLedger.whereAccountsIds(
|
||||
this.expenseAccountsIds,
|
||||
);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,132 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
BALANCE_SHEET_SCHEMA_NODE_ID,
|
||||
BALANCE_SHEET_SCHEMA_NODE_TYPE,
|
||||
} from './BalanceSheet.types';
|
||||
import { FinancialSchema } from '../../common/FinancialSchema';
|
||||
import { GConstructor } from '@/common/types/Constructor';
|
||||
import { ACCOUNT_TYPE } from '@/constants/accounts';
|
||||
import { FinancialSheet } from '../../common/FinancialSheet';
|
||||
|
||||
export const BalanceSheetSchema = <T extends GConstructor<FinancialSheet>>(
|
||||
Base: T,
|
||||
) =>
|
||||
class extends R.pipe(FinancialSchema)(Base) {
|
||||
/**
|
||||
* Retrieves the balance sheet schema.
|
||||
* @returns
|
||||
*/
|
||||
getSchema = () => {
|
||||
return getBalanceSheetSchema();
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the balance sheet report schema.
|
||||
*/
|
||||
export const getBalanceSheetSchema = () => [
|
||||
{
|
||||
name: 'balance_sheet.assets',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.ASSETS,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE,
|
||||
children: [
|
||||
{
|
||||
name: 'balance_sheet.current_asset',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.CURRENT_ASSETS,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE,
|
||||
children: [
|
||||
{
|
||||
name: 'balance_sheet.cash_and_cash_equivalents',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.CASH_EQUIVALENTS,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.CASH, ACCOUNT_TYPE.BANK],
|
||||
},
|
||||
{
|
||||
name: 'balance_sheet.accounts_receivable',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.ACCOUNTS_RECEIVABLE,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.ACCOUNTS_RECEIVABLE],
|
||||
},
|
||||
{
|
||||
name: 'balance_sheet.inventory',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.INVENTORY,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.INVENTORY],
|
||||
},
|
||||
{
|
||||
name: 'balance_sheet.other_current_assets',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.OTHER_CURRENT_ASSET,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.OTHER_CURRENT_ASSET],
|
||||
},
|
||||
],
|
||||
alwaysShow: true,
|
||||
},
|
||||
{
|
||||
name: 'balance_sheet.fixed_asset',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.FIXED_ASSET,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.FIXED_ASSET],
|
||||
},
|
||||
{
|
||||
name: 'balance_sheet.non_current_assets',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.NON_CURRENT_ASSET,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.NON_CURRENT_ASSET],
|
||||
},
|
||||
],
|
||||
alwaysShow: true,
|
||||
},
|
||||
{
|
||||
name: 'balance_sheet.liabilities_and_equity',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.LIABILITY_EQUITY,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE,
|
||||
children: [
|
||||
{
|
||||
name: 'balance_sheet.liabilities',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.LIABILITY,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE,
|
||||
children: [
|
||||
{
|
||||
name: 'balance_sheet.current_liabilties',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.CURRENT_LIABILITY,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
accountsTypes: [
|
||||
ACCOUNT_TYPE.ACCOUNTS_PAYABLE,
|
||||
ACCOUNT_TYPE.TAX_PAYABLE,
|
||||
ACCOUNT_TYPE.CREDIT_CARD,
|
||||
ACCOUNT_TYPE.OTHER_CURRENT_LIABILITY,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'balance_sheet.long_term_liabilities',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.LOGN_TERM_LIABILITY,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.LOGN_TERM_LIABILITY],
|
||||
},
|
||||
{
|
||||
name: 'balance_sheet.non_current_liabilities',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.NON_CURRENT_LIABILITY,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.NON_CURRENT_LIABILITY],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'balance_sheet.equity',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.EQUITY,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.EQUITY],
|
||||
children: [
|
||||
{
|
||||
name: 'balance_sheet.net_income',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.NET_INCOME,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.NET_INCOME,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
alwaysShow: true,
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,282 @@
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
IBalanceSheetStatementData,
|
||||
IBalanceSheetQuery,
|
||||
BALANCE_SHEET_SCHEMA_NODE_TYPE,
|
||||
IBalanceSheetDataNode,
|
||||
IBalanceSheetSchemaNode,
|
||||
IBalanceSheetNetIncomeNode,
|
||||
IBalanceSheetAccountNode,
|
||||
IBalanceSheetAccountsNode,
|
||||
IBalanceSheetAggregateNode,
|
||||
} from './BalanceSheet.types';
|
||||
import {
|
||||
ITableColumnAccessor,
|
||||
ITableColumn,
|
||||
ITableRow,
|
||||
} from '../../types/Table.types';
|
||||
import { tableRowMapper } from '../../utils/Table.utils';
|
||||
import { FinancialSheet } from '../../common/FinancialSheet';
|
||||
import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear';
|
||||
import { IROW_TYPE, DISPLAY_COLUMNS_BY } from './constants';
|
||||
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
|
||||
import { BalanceSheetPercentage } from './BalanceSheetPercentage';
|
||||
import { FinancialSheetStructure } from '../../common/FinancialSheetStructure';
|
||||
import { BalanceSheetBase } from './BalanceSheetBase';
|
||||
import { BalanceSheetTablePercentage } from './BalanceSheetTablePercentage';
|
||||
import { BalanceSheetTablePreviousYear } from './BalanceSheetTablePreviousYear';
|
||||
import { BalanceSheetTablePreviousPeriod } from './BalanceSheetTablePreviousPeriod';
|
||||
import { FinancialTable } from '../../common/FinancialTable';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { BalanceSheetTableDatePeriods } from './BalanceSheetTableDatePeriods';
|
||||
|
||||
export class BalanceSheetTable extends R.pipe(
|
||||
BalanceSheetBase,
|
||||
FinancialTable,
|
||||
FinancialSheetStructure,
|
||||
BalanceSheetPercentage,
|
||||
BalanceSheetComparsionPreviousPeriod,
|
||||
BalanceSheetComparsionPreviousYear,
|
||||
BalanceSheetTablePercentage,
|
||||
BalanceSheetTableDatePeriods,
|
||||
BalanceSheetTablePreviousYear,
|
||||
BalanceSheetTablePreviousPeriod,
|
||||
)(FinancialSheet) {
|
||||
/**
|
||||
* Balance sheet data.
|
||||
* @param {IBalanceSheetStatementData}
|
||||
*/
|
||||
public reportData: IBalanceSheetStatementData;
|
||||
|
||||
/**
|
||||
* Balance sheet query.
|
||||
* @parma {BalanceSheetQuery}
|
||||
*/
|
||||
public query: BalanceSheetQuery;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {IBalanceSheetStatementData} reportData -
|
||||
* @param {IBalanceSheetQuery} query -
|
||||
*/
|
||||
constructor(
|
||||
reportData: IBalanceSheetStatementData,
|
||||
query: IBalanceSheetQuery,
|
||||
i18n: any,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.reportData = reportData;
|
||||
this.query = new BalanceSheetQuery(query);
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detarmines the node type of the given schema node.
|
||||
* @param {IBalanceSheetStructureSection} node -
|
||||
* @param {string} type -
|
||||
* @return {boolean}
|
||||
*/
|
||||
public isNodeType = R.curry(
|
||||
(type: string, node: IBalanceSheetSchemaNode): boolean => {
|
||||
return node.nodeType === type;
|
||||
},
|
||||
);
|
||||
|
||||
// -------------------------
|
||||
// # Accessors.
|
||||
// -------------------------
|
||||
/**
|
||||
* Retrieve the common columns for all report nodes.
|
||||
* @param {ITableColumnAccessor[]}
|
||||
*/
|
||||
public commonColumnsAccessors = (): ITableColumnAccessor[] => {
|
||||
return R.compose(
|
||||
R.concat([{ key: 'name', accessor: 'name' }]),
|
||||
R.ifElse(
|
||||
R.always(this.isDisplayColumnsBy(DISPLAY_COLUMNS_BY.DATE_PERIODS)),
|
||||
R.concat(this.datePeriodsColumnsAccessors()),
|
||||
R.concat(this.totalColumnAccessor()),
|
||||
),
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the total column accessor.
|
||||
* @return {ITableColumnAccessor[]}
|
||||
*/
|
||||
public totalColumnAccessor = (): ITableColumnAccessor[] => {
|
||||
return R.pipe(
|
||||
R.concat(this.previousPeriodColumnAccessor()),
|
||||
R.concat(this.previousYearColumnAccessor()),
|
||||
R.concat(this.percentageColumnsAccessor()),
|
||||
R.concat([{ key: 'total', accessor: 'total.formattedAmount' }]),
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the table row from the given report aggregate node.
|
||||
* @param {IBalanceSheetAggregateNode} node
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
public aggregateNodeTableRowsMapper = (
|
||||
node: IBalanceSheetAggregateNode,
|
||||
): ITableRow => {
|
||||
const columns = this.commonColumnsAccessors();
|
||||
const meta = {
|
||||
rowTypes: [IROW_TYPE.AGGREGATE],
|
||||
id: node.id,
|
||||
};
|
||||
return tableRowMapper(node, columns, meta);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the table row from the given report accounts node.
|
||||
* @param {IBalanceSheetAccountsNode} node
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
public accountsNodeTableRowsMapper = (
|
||||
node: IBalanceSheetAccountsNode,
|
||||
): ITableRow => {
|
||||
const columns = this.commonColumnsAccessors();
|
||||
const meta = {
|
||||
rowTypes: [IROW_TYPE.ACCOUNTS],
|
||||
id: node.id,
|
||||
};
|
||||
return tableRowMapper(node, columns, meta);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the table row from the given report account node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
public accountNodeTableRowsMapper = (
|
||||
node: IBalanceSheetAccountNode,
|
||||
): ITableRow => {
|
||||
const columns = this.commonColumnsAccessors();
|
||||
|
||||
const meta = {
|
||||
rowTypes: [IROW_TYPE.ACCOUNT],
|
||||
id: node.id,
|
||||
};
|
||||
return tableRowMapper(node, columns, meta);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the table row from the given report net income node.
|
||||
* @param {IBalanceSheetNetIncomeNode} node
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
public netIncomeNodeTableRowsMapper = (
|
||||
node: IBalanceSheetNetIncomeNode,
|
||||
): ITableRow => {
|
||||
const columns = this.commonColumnsAccessors();
|
||||
const meta = {
|
||||
rowTypes: [IROW_TYPE.NET_INCOME],
|
||||
id: node.id,
|
||||
};
|
||||
return tableRowMapper(node, columns, meta);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the given report node to table rows.
|
||||
* @param {IBalanceSheetDataNode} node -
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
public nodeToTableRowsMapper = (node: IBalanceSheetDataNode): ITableRow => {
|
||||
return R.cond([
|
||||
[
|
||||
this.isNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE),
|
||||
this.aggregateNodeTableRowsMapper,
|
||||
],
|
||||
[
|
||||
this.isNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS),
|
||||
this.accountsNodeTableRowsMapper,
|
||||
],
|
||||
[
|
||||
this.isNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNT),
|
||||
this.accountNodeTableRowsMapper,
|
||||
],
|
||||
[
|
||||
this.isNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.NET_INCOME),
|
||||
this.netIncomeNodeTableRowsMapper,
|
||||
],
|
||||
])(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the given report sections to table rows.
|
||||
* @param {IBalanceSheetDataNode[]} nodes -
|
||||
* @return {ITableRow}
|
||||
*/
|
||||
public nodesToTableRowsMapper = (
|
||||
nodes: IBalanceSheetDataNode[],
|
||||
): ITableRow[] => {
|
||||
return this.mapNodesDeep(nodes, this.nodeToTableRowsMapper);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the total children columns.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
public totalColumnChildren = (): ITableColumn[] => {
|
||||
return R.compose(
|
||||
R.unless(
|
||||
R.isEmpty,
|
||||
R.concat([
|
||||
{ key: 'total', label: this.i18n.__('balance_sheet.total') },
|
||||
]),
|
||||
),
|
||||
R.concat(this.percentageColumns()),
|
||||
R.concat(this.getPreviousYearColumns()),
|
||||
R.concat(this.previousPeriodColumns()),
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the total column.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
public totalColumn = (): ITableColumn[] => {
|
||||
return [
|
||||
{
|
||||
key: 'total',
|
||||
label: this.i18n.__('balance_sheet.total'),
|
||||
children: this.totalColumnChildren(),
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the report table rows.
|
||||
* @returns {ITableRow[]}
|
||||
*/
|
||||
public tableRows = (): ITableRow[] => {
|
||||
return R.compose(
|
||||
this.addTotalRows,
|
||||
this.nodesToTableRowsMapper,
|
||||
)(this.reportData);
|
||||
};
|
||||
|
||||
// -------------------------
|
||||
// # Columns.
|
||||
// -------------------------
|
||||
/**
|
||||
* Retrieve the report table columns.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
public tableColumns = (): ITableColumn[] => {
|
||||
return R.compose(
|
||||
this.tableColumnsCellIndexing,
|
||||
R.concat([
|
||||
{ key: 'name', label: this.i18n.__('balance_sheet.account_name') },
|
||||
]),
|
||||
R.ifElse(
|
||||
this.query.isDatePeriodsColumnsType,
|
||||
R.concat(this.datePeriodsColumns()),
|
||||
R.concat(this.totalColumn()),
|
||||
),
|
||||
)([]);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
import * as R from 'ramda';
|
||||
import * as moment from 'moment';
|
||||
import { ITableColumn, ITableColumnAccessor } from '../../types/Table.types';
|
||||
import { FinancialDatePeriods } from '../../common/FinancialDatePeriods';
|
||||
import { IDateRange } from '../CashFlow/Cashflow.types';
|
||||
import { GConstructor } from '@/common/types/Constructor';
|
||||
import { FinancialSheet } from '../../common/FinancialSheet';
|
||||
|
||||
export const BalanceSheetTableDatePeriods = <
|
||||
T extends GConstructor<FinancialSheet>,
|
||||
>(
|
||||
Base: T,
|
||||
) =>
|
||||
class extends R.pipe(FinancialDatePeriods)(Base) {
|
||||
/**
|
||||
* Retrieves the date periods based on the report query.
|
||||
* @returns {IDateRange[]}
|
||||
*/
|
||||
get datePeriods() {
|
||||
return this.getDateRanges(
|
||||
this.query.fromDate,
|
||||
this.query.toDate,
|
||||
this.query.displayColumnsBy,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the formatted column label from the given date range.
|
||||
* @param {ICashFlowDateRange} dateRange -
|
||||
* @return {string}
|
||||
*/
|
||||
public formatColumnLabel = (dateRange: ICashFlowDateRange) => {
|
||||
const monthFormat = (range) => moment(range.toDate).format('YYYY-MM');
|
||||
const yearFormat = (range) => moment(range.toDate).format('YYYY');
|
||||
const dayFormat = (range) => moment(range.toDate).format('YYYY-MM-DD');
|
||||
|
||||
const conditions = [
|
||||
['month', monthFormat],
|
||||
['year', yearFormat],
|
||||
['day', dayFormat],
|
||||
['quarter', monthFormat],
|
||||
['week', dayFormat],
|
||||
];
|
||||
const conditionsPairs = R.map(
|
||||
([type, formatFn]) => [
|
||||
R.always(this.query.isDisplayColumnsBy(type)),
|
||||
formatFn,
|
||||
],
|
||||
conditions,
|
||||
);
|
||||
return R.compose(R.cond(conditionsPairs))(dateRange);
|
||||
};
|
||||
|
||||
// -------------------------
|
||||
// # Accessors.
|
||||
// -------------------------
|
||||
/**
|
||||
* Date period columns accessor.
|
||||
* @param {IDateRange} dateRange -
|
||||
* @param {number} index -
|
||||
*/
|
||||
public datePeriodColumnsAccessor = R.curry(
|
||||
(dateRange: IDateRange, index: number) => {
|
||||
return R.pipe(
|
||||
R.concat(this.previousPeriodHorizColumnAccessors(index)),
|
||||
R.concat(this.previousYearHorizontalColumnAccessors(index)),
|
||||
R.concat(this.percetangeDatePeriodColumnsAccessor(index)),
|
||||
R.concat([
|
||||
{
|
||||
key: `date-range-${index}`,
|
||||
accessor: `horizontalTotals[${index}].total.formattedAmount`,
|
||||
},
|
||||
]),
|
||||
)([]);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Retrieve the date periods columns accessors.
|
||||
* @returns {ITableColumnAccessor[]}
|
||||
*/
|
||||
public datePeriodsColumnsAccessors = (): ITableColumnAccessor[] => {
|
||||
return R.compose(
|
||||
R.flatten,
|
||||
R.addIndex(R.map)(this.datePeriodColumnsAccessor),
|
||||
)(this.datePeriods);
|
||||
};
|
||||
|
||||
// -------------------------
|
||||
// # Columns.
|
||||
// -------------------------
|
||||
/**
|
||||
*
|
||||
* @param {number} index
|
||||
* @param {} dateRange
|
||||
* @returns {}
|
||||
*/
|
||||
public datePeriodChildrenColumns = (
|
||||
index: number,
|
||||
dateRange: IDateRange,
|
||||
) => {
|
||||
return R.compose(
|
||||
R.unless(
|
||||
R.isEmpty,
|
||||
R.concat([
|
||||
{ key: `total`, label: this.i18n.__('balance_sheet.total') },
|
||||
]),
|
||||
),
|
||||
R.concat(this.percentageColumns()),
|
||||
R.concat(this.getPreviousYearHorizontalColumns(dateRange)),
|
||||
R.concat(this.previousPeriodHorizontalColumns(dateRange)),
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param dateRange
|
||||
* @param index
|
||||
* @returns
|
||||
*/
|
||||
public datePeriodColumn = (
|
||||
dateRange: IDateRange,
|
||||
index: number,
|
||||
): ITableColumn => {
|
||||
return {
|
||||
key: `date-range-${index}`,
|
||||
label: this.formatColumnLabel(dateRange),
|
||||
children: this.datePeriodChildrenColumns(index, dateRange),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Date periods columns.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
public datePeriodsColumns = (): ITableColumn[] => {
|
||||
return this.datePeriods.map(this.datePeriodColumn);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { I18nService } from 'nestjs-i18n';
|
||||
import { BalanceSheetInjectable } from './BalanceSheetInjectable';
|
||||
import { BalanceSheetTable } from './BalanceSheetTable';
|
||||
import { IBalanceSheetQuery, IBalanceSheetTable } from './BalanceSheet.types';
|
||||
|
||||
@Injectable()
|
||||
export class BalanceSheetTableInjectable {
|
||||
constructor(
|
||||
private readonly balanceSheetService: BalanceSheetInjectable,
|
||||
private readonly i18nService: I18nService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieves the balance sheet in table format.
|
||||
* @param {number} query -
|
||||
* @returns {Promise<IBalanceSheetTable>}
|
||||
*/
|
||||
public async table(filter: IBalanceSheetQuery): Promise<IBalanceSheetTable> {
|
||||
const { data, query, meta } =
|
||||
await this.balanceSheetService.balanceSheet(filter);
|
||||
|
||||
const table = new BalanceSheetTable(data, query, this.i18nService);
|
||||
|
||||
return {
|
||||
table: {
|
||||
columns: table.tableColumns(),
|
||||
rows: table.tableRows(),
|
||||
},
|
||||
query,
|
||||
meta,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import * as R from 'ramda';
|
||||
import { ITableColumn } from '../../types/Table.types';
|
||||
import { Constructor } from '@/common/types/Constructor';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { I18nService } from 'nestjs-i18n';
|
||||
|
||||
export const BalanceSheetTablePercentage = <T extends Constructor>(Base: T) =>
|
||||
class BalanceSheetComparsionPreviousYear extends Base {
|
||||
public readonly query: BalanceSheetQuery;
|
||||
public readonly i18n: I18nService;
|
||||
|
||||
// --------------------
|
||||
// # Columns
|
||||
// --------------------
|
||||
/**
|
||||
* Retrieve percentage of column/row columns.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
public percentageColumns = (): ITableColumn[] => {
|
||||
return R.pipe(
|
||||
R.when(
|
||||
this.query.isColumnsPercentageActive,
|
||||
R.append({
|
||||
key: 'percentage_of_column',
|
||||
label: this.i18n.t('balance_sheet.percentage_of_column'),
|
||||
})
|
||||
),
|
||||
R.when(
|
||||
this.query.isRowsPercentageActive,
|
||||
R.append({
|
||||
key: 'percentage_of_row',
|
||||
label: this.i18n.t('balance_sheet.percentage_of_row'),
|
||||
})
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
|
||||
// --------------------
|
||||
// # Accessors
|
||||
// --------------------
|
||||
/**
|
||||
* Retrieves percentage of column/row accessors.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
public percentageColumnsAccessor = (): ITableColumn[] => {
|
||||
return R.pipe(
|
||||
R.when(
|
||||
this.query.isColumnsPercentageActive,
|
||||
R.append({
|
||||
key: 'percentage_of_column',
|
||||
accessor: 'percentageColumn.formattedAmount',
|
||||
})
|
||||
),
|
||||
R.when(
|
||||
this.query.isRowsPercentageActive,
|
||||
R.append({
|
||||
key: 'percentage_of_row',
|
||||
accessor: 'percentageRow.formattedAmount',
|
||||
})
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Percentage columns accessors for date period columns.
|
||||
* @param {number} index
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
public percetangeDatePeriodColumnsAccessor = (
|
||||
index: number
|
||||
): ITableColumn[] => {
|
||||
return R.pipe(
|
||||
R.when(
|
||||
this.query.isColumnsPercentageActive,
|
||||
R.append({
|
||||
key: `percentage_of_column-${index}`,
|
||||
accessor: `horizontalTotals[${index}].percentageColumn.formattedAmount`,
|
||||
})
|
||||
),
|
||||
R.when(
|
||||
this.query.isRowsPercentageActive,
|
||||
R.append({
|
||||
key: `percentage_of_row-${index}`,
|
||||
accessor: `horizontalTotals[${index}].percentageRow.formattedAmount`,
|
||||
})
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,114 @@
|
||||
import * as R from 'ramda';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { FinancialTablePreviousPeriod } from '../../common/FinancialTablePreviousPeriod';
|
||||
import { FinancialDateRanges } from '../../common/FinancialDateRanges';
|
||||
import { IDateRange } from '../../types/Report.types';
|
||||
import { ITableColumn } from '../../types/Table.types';
|
||||
import { GConstructor } from '@/common/types/Constructor';
|
||||
import { FinancialSheet } from '../../common/FinancialSheet';
|
||||
|
||||
export const BalanceSheetTablePreviousPeriod = <
|
||||
T extends GConstructor<FinancialSheet>,
|
||||
>(
|
||||
Base: T,
|
||||
) =>
|
||||
class BalanceSheetTablePreviousPeriod extends R.pipe(
|
||||
FinancialTablePreviousPeriod,
|
||||
FinancialDateRanges,
|
||||
)(Base) {
|
||||
readonly query: BalanceSheetQuery;
|
||||
|
||||
// --------------------
|
||||
// # Columns
|
||||
// --------------------
|
||||
/**
|
||||
* Retrieves the previous period columns.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
public previousPeriodColumns = (dateRange?: IDateRange): ITableColumn[] => {
|
||||
return R.pipe(
|
||||
// Previous period columns.
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
R.append(this.getPreviousPeriodTotalColumn(dateRange)),
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
R.append(this.getPreviousPeriodChangeColumn()),
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
R.append(this.getPreviousPeriodPercentageColumn()),
|
||||
),
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Previous period for date periods
|
||||
* @param {IDateRange} dateRange
|
||||
* @returns {ITableColumn}
|
||||
*/
|
||||
public previousPeriodHorizontalColumns = (
|
||||
dateRange: IDateRange,
|
||||
): ITableColumn[] => {
|
||||
const PPDateRange = this.getPPDatePeriodDateRange(
|
||||
dateRange.fromDate,
|
||||
dateRange.toDate,
|
||||
this.query.displayColumnsBy,
|
||||
);
|
||||
return this.previousPeriodColumns({
|
||||
fromDate: PPDateRange.fromDate,
|
||||
toDate: PPDateRange.toDate,
|
||||
});
|
||||
};
|
||||
|
||||
// --------------------
|
||||
// # Accessors
|
||||
// --------------------
|
||||
/**
|
||||
* Retrieves previous period columns accessors.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
public previousPeriodColumnAccessor = (): ITableColumn[] => {
|
||||
return R.pipe(
|
||||
// Previous period columns.
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
R.append(this.getPreviousPeriodTotalAccessor()),
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
R.append(this.getPreviousPeriodChangeAccessor()),
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
R.append(this.getPreviousPeriodPercentageAccessor()),
|
||||
),
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} index
|
||||
* @returns
|
||||
*/
|
||||
public previousPeriodHorizColumnAccessors = (
|
||||
index: number,
|
||||
): ITableColumn[] => {
|
||||
return R.pipe(
|
||||
// Previous period columns.
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
R.append(this.getPreviousPeriodTotalHorizAccessor(index)),
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
R.append(this.getPreviousPeriodChangeHorizAccessor(index)),
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
R.append(this.getPreviousPeriodPercentageHorizAccessor(index)),
|
||||
),
|
||||
)([]);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,99 @@
|
||||
import * as R from 'ramda';
|
||||
import { IDateRange } from '../../types/Report.types';
|
||||
import { ITableColumn } from '../../types/Table.types';
|
||||
import { FinancialTablePreviousYear } from '../../common/FinancialTablePreviousYear';
|
||||
import { FinancialDateRanges } from '../../common/FinancialDateRanges';
|
||||
import { Constructor } from '@/common/types/Constructor';
|
||||
|
||||
export const BalanceSheetTablePreviousYear = <T extends Constructor>(Base: T) =>
|
||||
class extends R.pipe(FinancialTablePreviousYear, FinancialDateRanges)(Base) {
|
||||
// --------------------
|
||||
// # Columns.
|
||||
// --------------------
|
||||
/**
|
||||
* Retrieves pervious year comparison columns.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
public getPreviousYearColumns = (
|
||||
dateRange?: IDateRange,
|
||||
): ITableColumn[] => {
|
||||
return R.pipe(
|
||||
// Previous year columns.
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
R.append(this.getPreviousYearTotalColumn(dateRange)),
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
R.append(this.getPreviousYearChangeColumn()),
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
R.append(this.getPreviousYearPercentageColumn()),
|
||||
),
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IDateRange} dateRange
|
||||
* @returns
|
||||
*/
|
||||
public getPreviousYearHorizontalColumns = (dateRange: IDateRange) => {
|
||||
const PYDateRange = this.getPreviousYearDateRange(
|
||||
dateRange.fromDate,
|
||||
dateRange.toDate,
|
||||
);
|
||||
return this.getPreviousYearColumns(PYDateRange);
|
||||
};
|
||||
|
||||
// --------------------
|
||||
// # Accessors.
|
||||
// --------------------
|
||||
/**
|
||||
* Retrieves previous year columns accessors.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
public previousYearColumnAccessor = (): ITableColumn[] => {
|
||||
return R.pipe(
|
||||
// Previous year columns.
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
R.append(this.getPreviousYearTotalAccessor()),
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
R.append(this.getPreviousYearChangeAccessor()),
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
R.append(this.getPreviousYearPercentageAccessor()),
|
||||
),
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Previous year period column accessor.
|
||||
* @param {number} index
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
public previousYearHorizontalColumnAccessors = (
|
||||
index: number,
|
||||
): ITableColumn[] => {
|
||||
return R.pipe(
|
||||
// Previous year columns.
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
R.append(this.getPreviousYearTotalHorizAccessor(index)),
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
R.append(this.getPreviousYearChangeHorizAccessor(index)),
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
R.append(this.getPreviousYearPercentageHorizAccessor(index)),
|
||||
),
|
||||
)([]);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
import * as R from 'ramda';
|
||||
|
||||
export const BalanceSheetTotal = (Base: any) => class extends Base {};
|
||||
@@ -0,0 +1,100 @@
|
||||
import { IBalanceSheetQuery } from "./BalanceSheet.types";
|
||||
|
||||
export const MAP_CONFIG = { childrenPath: 'children', pathFormat: 'array' };
|
||||
|
||||
export const DISPLAY_COLUMNS_BY = {
|
||||
DATE_PERIODS: 'date_periods',
|
||||
TOTAL: 'total',
|
||||
};
|
||||
|
||||
export enum IROW_TYPE {
|
||||
AGGREGATE = 'AGGREGATE',
|
||||
ACCOUNTS = 'ACCOUNTS',
|
||||
ACCOUNT = 'ACCOUNT',
|
||||
NET_INCOME = 'NET_INCOME',
|
||||
TOTAL = 'TOTAL',
|
||||
}
|
||||
|
||||
export const HtmlTableCustomCss = `
|
||||
table tr.row-type--total td {
|
||||
font-weight: 600;
|
||||
border-top: 1px solid #bbb;
|
||||
color: #000;
|
||||
}
|
||||
table tr.row-type--total.row-id--assets td,
|
||||
table tr.row-type--total.row-id--liability-equity td {
|
||||
border-bottom: 3px double #000;
|
||||
}
|
||||
table .column--name,
|
||||
table .cell--name {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
table .column--total {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
table td.cell--total,
|
||||
table td.cell--previous_year,
|
||||
table td.cell--previous_year_change,
|
||||
table td.cell--previous_year_percentage,
|
||||
|
||||
table td.cell--previous_period,
|
||||
table td.cell--previous_period_change,
|
||||
table td.cell--previous_period_percentage,
|
||||
|
||||
table td.cell--percentage_of_row,
|
||||
table td.cell--percentage_of_column,
|
||||
table td[class*="cell--date-range"] {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
table .column--total,
|
||||
table .column--previous_year,
|
||||
table .column--previous_year_change,
|
||||
table .column--previous_year_percentage,
|
||||
|
||||
table .column--previous_period,
|
||||
table .column--previous_period_change,
|
||||
table .column--previous_period_percentage,
|
||||
|
||||
table .column--percentage_of_row,
|
||||
table .column--percentage_of_column,
|
||||
table [class*="column--date-range"] {
|
||||
text-align: right;
|
||||
}
|
||||
`;
|
||||
|
||||
export const getBalanceSheetDefaultQuery = (): IBalanceSheetQuery => {
|
||||
return {
|
||||
displayColumnsType: 'total',
|
||||
displayColumnsBy: 'month',
|
||||
|
||||
fromDate: moment().startOf('year').format('YYYY-MM-DD'),
|
||||
toDate: moment().format('YYYY-MM-DD'),
|
||||
|
||||
numberFormat: {
|
||||
precision: 2,
|
||||
divideOn1000: false,
|
||||
showZero: false,
|
||||
formatMoney: 'total',
|
||||
negativeFormat: 'mines',
|
||||
},
|
||||
noneZero: false,
|
||||
noneTransactions: false,
|
||||
|
||||
basis: 'cash',
|
||||
accountIds: [],
|
||||
|
||||
percentageOfColumn: false,
|
||||
percentageOfRow: false,
|
||||
|
||||
previousPeriod: false,
|
||||
previousPeriodAmountChange: false,
|
||||
previousPeriodPercentageChange: false,
|
||||
|
||||
previousYear: false,
|
||||
previousYearAmountChange: false,
|
||||
previousYearPercentageChange: false,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user