mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 13:20:31 +00:00
add server to monorepo.
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
import * as R from 'ramda';
|
||||
import { ACCOUNT_TYPE } from '@/data/AccountTypes';
|
||||
import {
|
||||
ProfitLossAggregateNodeId,
|
||||
ProfitLossNodeType,
|
||||
IProfitLossSchemaNode,
|
||||
} from '@/interfaces';
|
||||
import { FinancialSchema } from '../FinancialSchema';
|
||||
|
||||
export const ProfitLossShema = (Base) =>
|
||||
class extends R.compose(FinancialSchema)(Base) {
|
||||
/**
|
||||
* Retrieves the report schema.
|
||||
* @returns {IProfitLossSchemaNode[]}
|
||||
*/
|
||||
getSchema = (): IProfitLossSchemaNode[] => {
|
||||
return getProfitLossSheetSchema();
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves P&L sheet schema.
|
||||
* @returns {IProfitLossSchemaNode}
|
||||
*/
|
||||
export const getProfitLossSheetSchema = (): IProfitLossSchemaNode[] => [
|
||||
{
|
||||
id: ProfitLossAggregateNodeId.INCOME,
|
||||
name: 'profit_loss_sheet.income',
|
||||
nodeType: ProfitLossNodeType.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.INCOME],
|
||||
alwaysShow: true,
|
||||
},
|
||||
{
|
||||
id: ProfitLossAggregateNodeId.COS,
|
||||
name: 'profit_loss_sheet.cost_of_sales',
|
||||
nodeType: ProfitLossNodeType.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.COST_OF_GOODS_SOLD],
|
||||
},
|
||||
{
|
||||
id: ProfitLossAggregateNodeId.GROSS_PROFIT,
|
||||
name: 'profit_loss_sheet.gross_profit',
|
||||
nodeType: ProfitLossNodeType.EQUATION,
|
||||
equation: `${ProfitLossAggregateNodeId.INCOME} - ${ProfitLossAggregateNodeId.COS}`,
|
||||
},
|
||||
{
|
||||
id: ProfitLossAggregateNodeId.EXPENSES,
|
||||
name: 'profit_loss_sheet.expenses',
|
||||
nodeType: ProfitLossNodeType.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.EXPENSE],
|
||||
alwaysShow: true,
|
||||
},
|
||||
{
|
||||
id: ProfitLossAggregateNodeId.NET_OPERATING_INCOME,
|
||||
name: 'profit_loss_sheet.net_operating_income',
|
||||
nodeType: ProfitLossNodeType.EQUATION,
|
||||
equation: `${ProfitLossAggregateNodeId.GROSS_PROFIT} - ${ProfitLossAggregateNodeId.EXPENSES}`,
|
||||
},
|
||||
{
|
||||
id: ProfitLossAggregateNodeId.OTHER_INCOME,
|
||||
name: 'profit_loss_sheet.other_income',
|
||||
nodeType: ProfitLossNodeType.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.OTHER_INCOME],
|
||||
},
|
||||
{
|
||||
id: ProfitLossAggregateNodeId.OTHER_EXPENSES,
|
||||
name: 'profit_loss_sheet.other_expenses',
|
||||
nodeType: ProfitLossNodeType.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.OTHER_EXPENSE],
|
||||
},
|
||||
{
|
||||
id: ProfitLossAggregateNodeId.NET_INCOME,
|
||||
name: 'profit_loss_sheet.net_income',
|
||||
nodeType: ProfitLossNodeType.EQUATION,
|
||||
equation: `${ProfitLossAggregateNodeId.NET_OPERATING_INCOME} + ${ProfitLossAggregateNodeId.OTHER_INCOME} - ${ProfitLossAggregateNodeId.OTHER_EXPENSES}`,
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,324 @@
|
||||
import * as R from 'ramda';
|
||||
import { IProfitLossSheetQuery } from '@/interfaces/ProfitLossSheet';
|
||||
import FinancialSheet from '../FinancialSheet';
|
||||
import { FinancialSheetStructure } from '../FinancialSheetStructure';
|
||||
import {
|
||||
ProfitLossNodeType,
|
||||
IProfitLossSheetEquationNode,
|
||||
IProfitLossEquationSchemaNode,
|
||||
IProfitLossSheetAccountsNode,
|
||||
IProfitLossAccountsSchemaNode,
|
||||
IProfitLossSchemaNode,
|
||||
IProfitLossSheetNode,
|
||||
IAccount,
|
||||
IProfitLossSheetAccountNode,
|
||||
} from '@/interfaces';
|
||||
import { ProfitLossShema } from './ProfitLossSchema';
|
||||
import { ProfitLossSheetPercentage } from './ProfitLossSheetPercentage';
|
||||
import { ProfitLossSheetQuery } from './ProfitLossSheetQuery';
|
||||
import { ProfitLossSheetRepository } from './ProfitLossSheetRepository';
|
||||
import { ProfitLossSheetBase } from './ProfitLossSheetBase';
|
||||
import { ProfitLossSheetDatePeriods } from './ProfitLossSheetDatePeriods';
|
||||
import { FinancialEvaluateEquation } from '../FinancialEvaluateEquation';
|
||||
import { ProfitLossSheetPreviousYear } from './ProfitLossSheetPreviousYear';
|
||||
import { ProfitLossSheetPreviousPeriod } from './ProfitLossSheetPreviousPeriod';
|
||||
import { FinancialDateRanges } from '../FinancialDateRanges';
|
||||
import { ProfitLossSheetFilter } from './ProfitLossSheetFilter';
|
||||
|
||||
export default class ProfitLossSheet extends R.compose(
|
||||
ProfitLossSheetPreviousYear,
|
||||
ProfitLossSheetPreviousPeriod,
|
||||
ProfitLossSheetPercentage,
|
||||
ProfitLossSheetDatePeriods,
|
||||
ProfitLossSheetFilter,
|
||||
ProfitLossShema,
|
||||
ProfitLossSheetBase,
|
||||
FinancialDateRanges,
|
||||
FinancialEvaluateEquation,
|
||||
FinancialSheetStructure
|
||||
)(FinancialSheet) {
|
||||
/**
|
||||
* Profit/Loss sheet query.
|
||||
* @param {ProfitLossSheetQuery}
|
||||
*/
|
||||
readonly query: ProfitLossSheetQuery;
|
||||
/**
|
||||
* @param {string}
|
||||
*/
|
||||
readonly comparatorDateType: string;
|
||||
|
||||
/**
|
||||
* Organization's base currency.
|
||||
* @param {string}
|
||||
*/
|
||||
readonly baseCurrency: string;
|
||||
|
||||
/**
|
||||
* Profit/Loss repository.
|
||||
* @param {ProfitLossSheetRepository}
|
||||
*/
|
||||
readonly repository: ProfitLossSheetRepository;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {IProfitLossSheetQuery} query -
|
||||
* @param {IAccount[]} accounts -
|
||||
* @param {IJournalPoster} transactionsJournal -
|
||||
*/
|
||||
constructor(
|
||||
repository: ProfitLossSheetRepository,
|
||||
query: IProfitLossSheetQuery,
|
||||
baseCurrency: string,
|
||||
i18n: any
|
||||
) {
|
||||
super();
|
||||
|
||||
this.query = new ProfitLossSheetQuery(query);
|
||||
this.repository = repository;
|
||||
this.numberFormat = this.query.query.numberFormat;
|
||||
this.baseCurrency = baseCurrency;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the sheet account node from the given account.
|
||||
* @param {IAccount} account
|
||||
* @returns {IProfitLossSheetAccountNode}
|
||||
*/
|
||||
private accountNodeMapper = (
|
||||
account: IAccount
|
||||
): IProfitLossSheetAccountNode => {
|
||||
const total = this.repository.totalAccountsLedger
|
||||
.whereAccountId(account.id)
|
||||
.getClosingBalance();
|
||||
|
||||
return {
|
||||
id: account.id,
|
||||
name: account.name,
|
||||
nodeType: ProfitLossNodeType.ACCOUNT,
|
||||
total: this.getAmountMeta(total),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Compose account node.
|
||||
* @param {IAccount} node
|
||||
* @returns {IProfitLossSheetAccountNode}
|
||||
*/
|
||||
private accountNodeCompose = (
|
||||
account: IAccount
|
||||
): IProfitLossSheetAccountNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.previousPeriodAccountNodeCompose
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.previousYearAccountNodeCompose
|
||||
),
|
||||
R.when(
|
||||
this.query.isDatePeriodsColumnsType,
|
||||
this.assocAccountNodeDatePeriod
|
||||
),
|
||||
this.accountNodeMapper
|
||||
)(account);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve report accounts nodes by the given accounts types.
|
||||
* @param {string[]} types
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
private getAccountsNodesByTypes = (
|
||||
types: string[]
|
||||
): IProfitLossSheetAccountNode[] => {
|
||||
return R.compose(
|
||||
R.map(this.accountNodeCompose),
|
||||
R.flatten,
|
||||
R.map(this.repository.getAccountsByType)
|
||||
)(types);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mapps the accounts schema node to report node.
|
||||
* @param {IProfitLossSchemaNode} node
|
||||
* @returns {IProfitLossSheetNode}
|
||||
*/
|
||||
private accountsSchemaNodeMapper = (
|
||||
node: IProfitLossAccountsSchemaNode
|
||||
): IProfitLossSheetNode => {
|
||||
// Retrieve accounts node by the given types.
|
||||
const children = this.getAccountsNodesByTypes(node.accountsTypes);
|
||||
|
||||
// Retrieve the total of the given nodes.
|
||||
const total = this.getTotalOfNodes(children);
|
||||
|
||||
return {
|
||||
id: node.id,
|
||||
name: this.i18n.__(node.name),
|
||||
nodeType: ProfitLossNodeType.ACCOUNTS,
|
||||
total: this.getTotalAmountMeta(total),
|
||||
children,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Accounts schema node composer.
|
||||
* @param {IProfitLossSchemaNode} node
|
||||
* @returns {IProfitLossSheetAccountsNode}
|
||||
*/
|
||||
private accountsSchemaNodeCompose = (
|
||||
node: IProfitLossSchemaNode
|
||||
): IProfitLossSheetAccountsNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.previousPeriodAggregateNodeCompose
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.previousYearAggregateNodeCompose
|
||||
),
|
||||
R.when(
|
||||
this.query.isDatePeriodsColumnsType,
|
||||
this.assocAggregateDatePeriod
|
||||
),
|
||||
this.accountsSchemaNodeMapper
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Equation schema node parser.
|
||||
* @param {(IProfitLossSchemaNode | IProfitLossSheetNode)[]} accNodes -
|
||||
* @param {IProfitLossEquationSchemaNode} node -
|
||||
* @param {IProfitLossSheetEquationNode}
|
||||
*/
|
||||
private equationSchemaNodeParser = R.curry(
|
||||
(
|
||||
accNodes: (IProfitLossSchemaNode | IProfitLossSheetNode)[],
|
||||
node: IProfitLossEquationSchemaNode
|
||||
): IProfitLossSheetEquationNode => {
|
||||
const tableNodes = this.getNodesTableForEvaluating(
|
||||
'total.amount',
|
||||
accNodes
|
||||
);
|
||||
// Evaluate the given equation.
|
||||
const total = this.evaluateEquation(node.equation, tableNodes);
|
||||
|
||||
return {
|
||||
id: node.id,
|
||||
name: this.i18n.__(node.name),
|
||||
nodeType: ProfitLossNodeType.EQUATION,
|
||||
total: this.getTotalAmountMeta(total),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Equation schema node composer.
|
||||
* @param {(IProfitLossSchemaNode | IProfitLossSheetNode)[]} accNodes -
|
||||
* @param {IProfitLossSchemaNode} node -
|
||||
* @returns {IProfitLossSheetEquationNode}
|
||||
*/
|
||||
private equationSchemaNodeCompose = R.curry(
|
||||
(
|
||||
accNodes: (IProfitLossSchemaNode | IProfitLossSheetNode)[],
|
||||
node: IProfitLossEquationSchemaNode
|
||||
): IProfitLossSheetEquationNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.previousPeriodEquationNodeCompose(accNodes, node.equation)
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.previousYearEquationNodeCompose(accNodes, node.equation)
|
||||
),
|
||||
R.when(
|
||||
this.query.isDatePeriodsColumnsType,
|
||||
this.assocEquationNodeDatePeriod(accNodes, node.equation)
|
||||
),
|
||||
this.equationSchemaNodeParser(accNodes)
|
||||
)(node);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Parses accounts schema node to report node.
|
||||
* @param {IProfitLossSchemaNode} schemaNode
|
||||
* @returns {IProfitLossSheetNode | IProfitLossSchemaNode}
|
||||
*/
|
||||
private accountsSchemaNodeMap = (
|
||||
schemaNode: IProfitLossSchemaNode
|
||||
): IProfitLossSheetNode | IProfitLossSchemaNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeType(ProfitLossNodeType.ACCOUNTS),
|
||||
this.accountsSchemaNodeCompose
|
||||
)
|
||||
)(schemaNode);
|
||||
};
|
||||
|
||||
/**
|
||||
* Composes schema equation node to report node.
|
||||
* @param {IProfitLossSheetNode | IProfitLossSchemaNode} node
|
||||
* @param {number} key
|
||||
* @param {IProfitLossSheetNode | IProfitLossSchemaNode} parentValue
|
||||
* @param {(IProfitLossSheetNode | IProfitLossSchemaNode)[]} accNodes
|
||||
* @param context
|
||||
* @returns {IProfitLossSheetEquationNode}
|
||||
*/
|
||||
private reportSchemaEquationNodeCompose = (
|
||||
node: IProfitLossSheetNode | IProfitLossSchemaNode,
|
||||
key: number,
|
||||
parentValue: IProfitLossSheetNode | IProfitLossSchemaNode,
|
||||
accNodes: (IProfitLossSheetNode | IProfitLossSchemaNode)[],
|
||||
context
|
||||
): IProfitLossSheetEquationNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeType(ProfitLossNodeType.EQUATION),
|
||||
this.equationSchemaNodeCompose(accNodes)
|
||||
)
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses schema accounts nodes.
|
||||
* @param {IProfitLossSchemaNode[]}
|
||||
* @returns {(IProfitLossSheetNode | IProfitLossSchemaNode)[]}
|
||||
*/
|
||||
private reportSchemaAccountsNodesCompose = (
|
||||
schemaNodes: IProfitLossSchemaNode[]
|
||||
): (IProfitLossSheetNode | IProfitLossSchemaNode)[] => {
|
||||
return this.mapNodesDeep(schemaNodes, this.accountsSchemaNodeMap);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses schema equation nodes.
|
||||
* @param {(IProfitLossSheetNode | IProfitLossSchemaNode)[]} nodes
|
||||
* @returns {(IProfitLossSheetNode | IProfitLossSchemaNode)[]}
|
||||
*/
|
||||
private reportSchemaEquationNodesCompose = (
|
||||
nodes: (IProfitLossSheetNode | IProfitLossSchemaNode)[]
|
||||
): (IProfitLossSheetNode | IProfitLossSchemaNode)[] => {
|
||||
return this.mapAccNodesDeep(nodes, this.reportSchemaEquationNodeCompose);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve profit/loss report data.
|
||||
* @return {IProfitLossSheetStatement}
|
||||
*/
|
||||
public reportData = (): IProfitLossSheetNode => {
|
||||
const schema = this.getSchema();
|
||||
|
||||
return R.compose(
|
||||
this.reportFilterPlugin,
|
||||
this.reportRowsPercentageCompose,
|
||||
this.reportColumnsPerentageCompose,
|
||||
this.reportSchemaEquationNodesCompose,
|
||||
this.reportSchemaAccountsNodesCompose
|
||||
)(schema);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import * as R from 'ramda';
|
||||
import { TOTAL_NODE_TYPES } from './constants';
|
||||
|
||||
export const ProfitLossSheetBase = (Base) =>
|
||||
class extends Base {
|
||||
/**
|
||||
*
|
||||
* @param type
|
||||
* @param node
|
||||
* @returns
|
||||
*/
|
||||
public isNodeType = R.curry((type: string, node) => {
|
||||
return node.nodeType === type;
|
||||
});
|
||||
|
||||
protected isNodeTypeIn = R.curry((types: string[], node) => {
|
||||
return types.indexOf(node.nodeType) !== -1;
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
protected findNodeById = R.curry((id, nodes) => {
|
||||
return this.findNodeDeep(nodes, (node) => node.id === id);
|
||||
});
|
||||
|
||||
isNodeTotal = (node) => {
|
||||
return this.isNodeTypeIn(TOTAL_NODE_TYPES, node);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,236 @@
|
||||
import * as R from 'ramda';
|
||||
import { sumBy } from 'lodash';
|
||||
import { FinancialDatePeriods } from '../FinancialDatePeriods';
|
||||
import {
|
||||
IDateRange,
|
||||
IProfitLossHorizontalDatePeriodNode,
|
||||
IProfitLossSheetAccountNode,
|
||||
IProfitLossSheetAccountsNode,
|
||||
IProfitLossSheetCommonNode,
|
||||
IProfitLossSheetNode,
|
||||
} from '@/interfaces';
|
||||
|
||||
export const ProfitLossSheetDatePeriods = (Base) =>
|
||||
class extends R.compose(FinancialDatePeriods)(Base) {
|
||||
/**
|
||||
* 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 {IProfitLossSheetCommonNode} node
|
||||
* @param {Function} callback
|
||||
* @returns {}
|
||||
*/
|
||||
protected getReportNodeDatePeriods = (
|
||||
node: IProfitLossSheetCommonNode,
|
||||
callback: (
|
||||
node: IProfitLossSheetCommonNode,
|
||||
fromDate: Date,
|
||||
toDate: Date,
|
||||
index: number
|
||||
) => any
|
||||
) => {
|
||||
return this.getNodeDatePeriods(
|
||||
this.query.fromDate,
|
||||
this.query.toDate,
|
||||
this.query.displayColumnsBy,
|
||||
node,
|
||||
callback
|
||||
);
|
||||
};
|
||||
|
||||
// --------------------------
|
||||
// # Account Nodes.
|
||||
// --------------------------
|
||||
/**
|
||||
* Retrieve account node date period total.
|
||||
* @param {IProfitLossSheetAccount} node
|
||||
* @param {Date} fromDate
|
||||
* @param {Date} toDate
|
||||
* @returns {}
|
||||
*/
|
||||
private getAccountNodeDatePeriodTotal = (
|
||||
node: IProfitLossSheetAccountNode,
|
||||
fromDate: Date,
|
||||
toDate: Date
|
||||
) => {
|
||||
const periodTotal = this.repository.periodsAccountsLedger
|
||||
.whereAccountId(node.id)
|
||||
.whereFromDate(fromDate)
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
return this.getDatePeriodTotalMeta(periodTotal, fromDate, toDate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve account node date period.
|
||||
* @param {IProfitLossSheetAccountNode} node
|
||||
* @returns {IProfitLossSheetAccountNode}
|
||||
*/
|
||||
public getAccountNodeDatePeriod = (node: IProfitLossSheetAccountNode) => {
|
||||
return this.getReportNodeDatePeriods(
|
||||
node,
|
||||
this.getAccountNodeDatePeriodTotal
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Account date periods to the given account node.
|
||||
* @param {IProfitLossSheetAccountNode} node
|
||||
* @returns {IProfitLossSheetAccountNode}
|
||||
*/
|
||||
public assocAccountNodeDatePeriod = (
|
||||
node: IProfitLossSheetAccountNode
|
||||
): IProfitLossSheetAccountNode => {
|
||||
const datePeriods = this.getAccountNodeDatePeriod(node);
|
||||
|
||||
return R.assoc('horizontalTotals', datePeriods, node);
|
||||
};
|
||||
|
||||
// --------------------------
|
||||
// # Aggregate nodes.
|
||||
// --------------------------
|
||||
/**
|
||||
* Retrieves sumation of the given aggregate node children totals.
|
||||
* @param {IProfitLossSheetAccountsNode} node
|
||||
* @param {number} index
|
||||
* @returns {number}
|
||||
*/
|
||||
private getAggregateDatePeriodIndexTotal = (
|
||||
node: IProfitLossSheetAccountsNode,
|
||||
index: number
|
||||
): number => {
|
||||
return sumBy(node.children, `horizontalTotals[${index}].total.amount`);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IProfitLossSheetAccount} node
|
||||
* @param {Date} fromDate
|
||||
* @param {Date} toDate
|
||||
* @param {number} index
|
||||
* @returns {IProfitLossSheetAccount}
|
||||
*/
|
||||
private getAggregateNodeDatePeriodTotal = R.curry(
|
||||
(
|
||||
node: IProfitLossSheetAccountsNode,
|
||||
fromDate: Date,
|
||||
toDate: Date,
|
||||
index: number
|
||||
): IProfitLossHorizontalDatePeriodNode => {
|
||||
const periodTotal = this.getAggregateDatePeriodIndexTotal(node, index);
|
||||
|
||||
return this.getDatePeriodTotalMeta(periodTotal, fromDate, toDate);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Retrieves aggregate horizontal date periods.
|
||||
* @param {IProfitLossSheetAccountsNode} node
|
||||
* @returns {IProfitLossSheetAccountsNode}
|
||||
*/
|
||||
private getAggregateNodeDatePeriod = (
|
||||
node: IProfitLossSheetAccountsNode
|
||||
): IProfitLossHorizontalDatePeriodNode[] => {
|
||||
return this.getReportNodeDatePeriods(
|
||||
node,
|
||||
this.getAggregateNodeDatePeriodTotal
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Assoc horizontal date periods to aggregate node.
|
||||
* @param {IProfitLossSheetAccountsNode} node
|
||||
* @returns {IProfitLossSheetAccountsNode}
|
||||
*/
|
||||
protected assocAggregateDatePeriod = (
|
||||
node: IProfitLossSheetAccountsNode
|
||||
): IProfitLossSheetAccountsNode => {
|
||||
const datePeriods = this.getAggregateNodeDatePeriod(node);
|
||||
|
||||
return R.assoc('horizontalTotals', datePeriods, node);
|
||||
};
|
||||
|
||||
// --------------------------
|
||||
// # Equation nodes.
|
||||
// --------------------------
|
||||
/**
|
||||
* Retrieves equation date period node.
|
||||
* @param {IProfitLossSheetNode[]} accNodes
|
||||
* @param {IProfitLossSheetNode} node
|
||||
* @param {Date} fromDate
|
||||
* @param {Date} toDate
|
||||
* @param {number} index
|
||||
* @returns {IProfitLossHorizontalDatePeriodNode}
|
||||
*/
|
||||
private getEquationNodeDatePeriod = R.curry(
|
||||
(
|
||||
accNodes: IProfitLossSheetNode[],
|
||||
equation: string,
|
||||
node: IProfitLossSheetNode,
|
||||
fromDate: Date,
|
||||
toDate: Date,
|
||||
index: number
|
||||
): IProfitLossHorizontalDatePeriodNode => {
|
||||
const tableNodes = this.getNodesTableForEvaluating(
|
||||
`horizontalTotals[${index}].total.amount`,
|
||||
accNodes
|
||||
);
|
||||
// Evaluate the given equation.
|
||||
const total = this.evaluateEquation(equation, tableNodes);
|
||||
|
||||
return this.getDatePeriodTotalMeta(total, fromDate, toDate);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Retrieves the equation node date periods.
|
||||
* @param {IProfitLossSheetNode[]} node
|
||||
* @param {string} equation
|
||||
* @param {IProfitLossSheetNode} node
|
||||
* @returns {IProfitLossHorizontalDatePeriodNode[]}
|
||||
*/
|
||||
private getEquationNodeDatePeriods = R.curry(
|
||||
(
|
||||
accNodes: IProfitLossSheetNode[],
|
||||
equation: string,
|
||||
node: IProfitLossSheetNode
|
||||
): IProfitLossHorizontalDatePeriodNode[] => {
|
||||
return this.getReportNodeDatePeriods(
|
||||
node,
|
||||
this.getEquationNodeDatePeriod(accNodes, equation)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Assoc equation node date period.
|
||||
* @param {IProfitLossSheetNode[]}
|
||||
* @param {IProfitLossSheetNode} node
|
||||
* @returns {IProfitLossSheetNode}
|
||||
*/
|
||||
protected assocEquationNodeDatePeriod = R.curry(
|
||||
(
|
||||
accNodes: IProfitLossSheetNode[],
|
||||
equation: string,
|
||||
node: IProfitLossSheetNode
|
||||
): IProfitLossSheetNode => {
|
||||
const periods = this.getEquationNodeDatePeriods(
|
||||
accNodes,
|
||||
equation,
|
||||
node
|
||||
);
|
||||
return R.assoc('horizontalTotals', periods, node);
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,170 @@
|
||||
import * as R from 'ramda';
|
||||
import { get } from 'lodash';
|
||||
import { IProfitLossSheetNode, ProfitLossNodeType } from '@/interfaces';
|
||||
import { FinancialFilter } from '../FinancialFilter';
|
||||
import { ProfitLossSheetBase } from './ProfitLossSheetBase';
|
||||
import { ProfitLossSheetQuery } from './ProfitLossSheetQuery';
|
||||
|
||||
export const ProfitLossSheetFilter = (Base) =>
|
||||
class extends R.compose(FinancialFilter, ProfitLossSheetBase)(Base) {
|
||||
query: ProfitLossSheetQuery;
|
||||
|
||||
// ----------------
|
||||
// # Account.
|
||||
// ----------------
|
||||
/**
|
||||
* Filter report node detarmine.
|
||||
* @param {IProfitLossSheetNode} node - Balance sheet node.
|
||||
* @return {boolean}
|
||||
*/
|
||||
private accountNoneZeroNodesFilterDetarminer = (
|
||||
node: IProfitLossSheetNode
|
||||
): boolean => {
|
||||
return R.ifElse(
|
||||
this.isNodeType(ProfitLossNodeType.ACCOUNT),
|
||||
this.isNodeNoneZero,
|
||||
R.always(true)
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines account none-transactions node.
|
||||
* @param {IBalanceSheetDataNode} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private accountNoneTransFilterDetarminer = (
|
||||
node: IProfitLossSheetNode
|
||||
): boolean => {
|
||||
return R.ifElse(
|
||||
this.isNodeType(ProfitLossNodeType.ACCOUNT),
|
||||
this.isNodeNoneZero,
|
||||
R.always(true)
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Report nodes filter.
|
||||
* @param {IProfitLossSheetNode[]} nodes -
|
||||
* @return {IProfitLossSheetNode[]}
|
||||
*/
|
||||
private accountsNoneZeroNodesFilter = (
|
||||
nodes: IProfitLossSheetNode[]
|
||||
): IProfitLossSheetNode[] => {
|
||||
return this.filterNodesDeep(
|
||||
nodes,
|
||||
this.accountNoneZeroNodesFilterDetarminer
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Filters the accounts none-transactions nodes.
|
||||
* @param {IProfitLossSheetNode[]} nodes
|
||||
* @returns {IProfitLossSheetNode[]}
|
||||
*/
|
||||
private accountsNoneTransactionsNodesFilter = (
|
||||
nodes: IProfitLossSheetNode[]
|
||||
) => {
|
||||
return this.filterNodesDeep(nodes, this.accountNoneTransFilterDetarminer);
|
||||
};
|
||||
|
||||
// ----------------
|
||||
// # Aggregate.
|
||||
// ----------------
|
||||
/**
|
||||
* Detearmines aggregate none-children filtering.
|
||||
* @param {IProfitLossSheetNode} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private aggregateNoneChildrenFilterDetarminer = (
|
||||
node: IProfitLossSheetNode
|
||||
): boolean => {
|
||||
const schemaNode = this.getSchemaNodeById(node.id);
|
||||
|
||||
// Detarmines whether the given node is aggregate node.
|
||||
const isAggregateNode = this.isNodeType(
|
||||
ProfitLossNodeType.ACCOUNTS,
|
||||
node
|
||||
);
|
||||
// Detarmines if the schema node is always should show.
|
||||
const isSchemaAlwaysShow = get(schemaNode, 'alwaysShow', false);
|
||||
|
||||
// Should node has children if aggregate node or not always show.
|
||||
return isAggregateNode && !isSchemaAlwaysShow
|
||||
? this.isNodeHasChildren(node)
|
||||
: true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Filters aggregate none-children nodes.
|
||||
* @param {IProfitLossSheetNode[]} nodes
|
||||
* @returns {IProfitLossSheetNode[]}
|
||||
*/
|
||||
private aggregateNoneChildrenFilter = (
|
||||
nodes: IProfitLossSheetNode[]
|
||||
): IProfitLossSheetNode[] => {
|
||||
return this.filterNodesDeep2(
|
||||
this.aggregateNoneChildrenFilterDetarminer,
|
||||
nodes
|
||||
);
|
||||
};
|
||||
|
||||
// ----------------
|
||||
// # Composers.
|
||||
// ----------------
|
||||
/**
|
||||
* Filters none-zero nodes.
|
||||
* @param {IProfitLossSheetNode[]} nodes
|
||||
* @returns {IProfitLossSheetNode[]}
|
||||
*/
|
||||
private filterNoneZeroNodesCompose = (
|
||||
nodes: IProfitLossSheetNode[]
|
||||
): IProfitLossSheetNode[] => {
|
||||
return R.compose(
|
||||
this.aggregateNoneChildrenFilter,
|
||||
this.accountsNoneZeroNodesFilter
|
||||
)(nodes);
|
||||
};
|
||||
|
||||
/**
|
||||
* Filters none-transactions nodes.
|
||||
* @param {IProfitLossSheetNode[]} nodes
|
||||
* @returns {IProfitLossSheetNode[]}
|
||||
*/
|
||||
private filterNoneTransNodesCompose = (
|
||||
nodes: IProfitLossSheetNode[]
|
||||
): IProfitLossSheetNode[] => {
|
||||
return R.compose(
|
||||
this.aggregateNoneChildrenFilter,
|
||||
this.accountsNoneTransactionsNodesFilter
|
||||
)(nodes);
|
||||
};
|
||||
|
||||
/**
|
||||
* Supress nodes when total accounts range transactions is empty.
|
||||
* @param {IProfitLossSheetNode[]} nodes
|
||||
* @returns {IProfitLossSheetNode[]}
|
||||
*/
|
||||
private supressNodesWhenRangeTransactionsEmpty = (
|
||||
nodes: IProfitLossSheetNode[]
|
||||
) => {
|
||||
return this.repository.totalAccountsLedger.isEmpty() ? [] : nodes;
|
||||
};
|
||||
|
||||
/**
|
||||
* Compose report nodes filtering.
|
||||
* @param {IProfitLossSheetNode[]} nodes
|
||||
* @returns {IProfitLossSheetNode[]}
|
||||
*/
|
||||
protected reportFilterPlugin = (
|
||||
nodes: IProfitLossSheetNode[]
|
||||
): IProfitLossSheetNode[] => {
|
||||
return R.compose(
|
||||
this.supressNodesWhenRangeTransactionsEmpty,
|
||||
R.when(() => this.query.noneZero, this.filterNoneZeroNodesCompose),
|
||||
R.when(
|
||||
() => this.query.noneTransactions,
|
||||
this.filterNoneTransNodesCompose
|
||||
)
|
||||
)(nodes);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,301 @@
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
IProfitLossSheetNode,
|
||||
IProfitLossSheetTotal,
|
||||
ProfitLossAggregateNodeId,
|
||||
} from '@/interfaces';
|
||||
import { FinancialHorizTotals } from '../FinancialHorizTotals';
|
||||
|
||||
export const ProfitLossSheetPercentage = (Base) =>
|
||||
class extends R.compose(FinancialHorizTotals)(Base) {
|
||||
/**
|
||||
* Assoc column of percentage attribute to the given node.
|
||||
* @param {IProfitLossSheetNode} netIncomeNode -
|
||||
* @param {IProfitLossSheetNode} node -
|
||||
* @return {IProfitLossSheetNode}
|
||||
*/
|
||||
private assocColumnPercentage = R.curry(
|
||||
(
|
||||
propertyPath: string,
|
||||
parentNode: IProfitLossSheetNode,
|
||||
node: IProfitLossSheetNode
|
||||
) => {
|
||||
const percentage = this.getPercentageBasis(
|
||||
parentNode.total.amount,
|
||||
node.total.amount
|
||||
);
|
||||
return R.assoc(
|
||||
propertyPath,
|
||||
this.getPercentageAmountMeta(percentage),
|
||||
node
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Assoc column of percentage attribute to the given node.
|
||||
* @param {IProfitLossSheetNode} netIncomeNode -
|
||||
* @param {IProfitLossSheetNode} node -
|
||||
* @return {IProfitLossSheetNode}
|
||||
*/
|
||||
private assocColumnTotalPercentage = R.curry(
|
||||
(
|
||||
propertyPath: string,
|
||||
parentNode: IProfitLossSheetNode,
|
||||
node: IProfitLossSheetNode
|
||||
) => {
|
||||
const percentage = this.getPercentageBasis(
|
||||
parentNode.total.amount,
|
||||
node.total.amount
|
||||
);
|
||||
return R.assoc(
|
||||
propertyPath,
|
||||
this.getPercentageTotalAmountMeta(percentage),
|
||||
node
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Compose percentage of columns.
|
||||
* @param {IProfitLossSheetNode[]} nodes
|
||||
* @returns {IProfitLossSheetNode[]}
|
||||
*/
|
||||
private columnPercentageCompose = (
|
||||
nodes: IProfitLossSheetNode[]
|
||||
): IProfitLossSheetNode[] => {
|
||||
const netIncomeNode = this.findNodeById(
|
||||
ProfitLossAggregateNodeId.NET_INCOME,
|
||||
nodes
|
||||
);
|
||||
return this.mapNodesDeep(
|
||||
nodes,
|
||||
this.columnPercentageMapper(netIncomeNode)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Compose percentage of income.
|
||||
* @param {IProfitLossSheetNode[]} nodes
|
||||
* @returns {IProfitLossSheetNode[]}
|
||||
*/
|
||||
private incomePercetageCompose = (
|
||||
nodes: IProfitLossSheetNode[]
|
||||
): IProfitLossSheetNode[] => {
|
||||
const incomeNode = this.findNodeById(
|
||||
ProfitLossAggregateNodeId.INCOME,
|
||||
nodes
|
||||
);
|
||||
return this.mapNodesDeep(nodes, this.incomePercentageMapper(incomeNode));
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IProfitLossSheetNode[]} nodes
|
||||
* @returns {IProfitLossSheetNode[]}
|
||||
*/
|
||||
private rowPercentageCompose = (
|
||||
nodes: IProfitLossSheetNode[]
|
||||
): IProfitLossSheetNode[] => {
|
||||
return this.mapNodesDeep(nodes, this.rowPercentageMap);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IProfitLossSheetNode} netIncomeNode -
|
||||
* @param {IProfitLossSheetNode} node -
|
||||
* @return {IProfitLossSheetNode}
|
||||
*/
|
||||
private columnPercentageMapper = R.curry(
|
||||
(netIncomeNode: IProfitLossSheetNode, node: IProfitLossSheetNode) => {
|
||||
const path = 'percentageColumn';
|
||||
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizTotals,
|
||||
this.assocColumnPercentageHorizTotals(netIncomeNode)
|
||||
),
|
||||
R.ifElse(
|
||||
this.isNodeTotal,
|
||||
this.assocColumnTotalPercentage(path, netIncomeNode),
|
||||
this.assocColumnPercentage(path, netIncomeNode)
|
||||
)
|
||||
)(node);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IProfitLossSheetNode} node
|
||||
* @returns {IProfitLossSheetNode}
|
||||
*/
|
||||
private rowPercentageMap = (
|
||||
node: IProfitLossSheetNode
|
||||
): IProfitLossSheetNode => {
|
||||
const path = 'percentageRow';
|
||||
|
||||
return R.compose(
|
||||
R.when(this.isNodeHasHorizTotals, this.assocRowPercentageHorizTotals),
|
||||
R.ifElse(
|
||||
this.isNodeTotal,
|
||||
this.assocColumnTotalPercentage(path, node),
|
||||
this.assocColumnPercentage(path, node)
|
||||
)
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IProfitLossSheetNode} incomeNode -
|
||||
* @param {IProfitLossSheetNode} node -
|
||||
* @returns {IProfitLossSheetNode}
|
||||
*/
|
||||
private incomePercentageMapper = R.curry(
|
||||
(incomeNode: IProfitLossSheetNode, node: IProfitLossSheetNode) => {
|
||||
const path = 'percentageIncome';
|
||||
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizTotals,
|
||||
this.assocIncomePercentageHorizTotals(incomeNode)
|
||||
),
|
||||
R.ifElse(
|
||||
this.isNodeTotal,
|
||||
this.assocColumnTotalPercentage(path, incomeNode),
|
||||
this.assocColumnPercentage(path, incomeNode)
|
||||
)
|
||||
)(node);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IProfitLossSheetNode} expenseNode -
|
||||
* @param {IProfitLossSheetNode} node -
|
||||
*/
|
||||
private expensePercentageMapper = R.curry(
|
||||
(expenseNode: IProfitLossSheetNode, node: IProfitLossSheetNode) => {
|
||||
const path = 'percentageExpense';
|
||||
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizTotals,
|
||||
this.assocExpensePercentageHorizTotals(expenseNode)
|
||||
),
|
||||
R.ifElse(
|
||||
this.isNodeTotal,
|
||||
this.assocColumnTotalPercentage(path, expenseNode),
|
||||
this.assocColumnPercentage(path, expenseNode)
|
||||
)
|
||||
)(node);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Compose percentage of expense.
|
||||
* @param {IProfitLossSheetNode[]} nodes
|
||||
* @returns {IProfitLossSheetNode[]}
|
||||
*/
|
||||
private expensesPercentageCompose = (
|
||||
nodes: IProfitLossSheetNode[]
|
||||
): IProfitLossSheetNode[] => {
|
||||
const expenseNode = this.findNodeById(
|
||||
ProfitLossAggregateNodeId.EXPENSES,
|
||||
nodes
|
||||
);
|
||||
return this.mapNodesDeep(
|
||||
nodes,
|
||||
this.expensePercentageMapper(expenseNode)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Compose percentage attributes.
|
||||
* @param {IProfitLossSheetNode[]} nodes
|
||||
* @returns {IProfitLossSheetNode[]}
|
||||
*/
|
||||
protected reportColumnsPerentageCompose = (
|
||||
nodes: IProfitLossSheetNode[]
|
||||
): IProfitLossSheetNode[] => {
|
||||
return R.compose(
|
||||
R.when(this.query.isIncomePercentage, this.incomePercetageCompose),
|
||||
R.when(this.query.isColumnPercentage, this.columnPercentageCompose),
|
||||
R.when(this.query.isExpensesPercentage, this.expensesPercentageCompose),
|
||||
R.when(this.query.isRowPercentage, this.rowPercentageCompose)
|
||||
)(nodes);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {} nodes
|
||||
* @returns {}
|
||||
*/
|
||||
protected reportRowsPercentageCompose = (nodes) => {
|
||||
return nodes;
|
||||
};
|
||||
|
||||
// ----------------------------------
|
||||
// # Horizontal Nodes
|
||||
// ----------------------------------
|
||||
/**
|
||||
* Assoc incomer percentage to horizontal totals nodes.
|
||||
* @param {IProfitLossSheetNode} incomeNode -
|
||||
* @param {IProfitLossSheetNode} node -
|
||||
* @returns {IProfitLossSheetNode}
|
||||
*/
|
||||
private assocIncomePercentageHorizTotals = R.curry(
|
||||
(incomeNode: IProfitLossSheetNode, node: IProfitLossSheetNode) => {
|
||||
const horTotalsWithIncomePerc = this.assocPercentageHorizTotals(
|
||||
'percentageIncome',
|
||||
incomeNode,
|
||||
node
|
||||
);
|
||||
return R.assoc('horizontalTotals', horTotalsWithIncomePerc, node);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Assoc expense percentage to horizontal totals nodes.
|
||||
* @param {IProfitLossSheetNode} expenseNode -
|
||||
* @param {IProfitLossSheetNode} node -
|
||||
* @returns {IProfitLossSheetNode}
|
||||
*/
|
||||
private assocExpensePercentageHorizTotals = R.curry(
|
||||
(expenseNode: IProfitLossSheetNode, node: IProfitLossSheetNode) => {
|
||||
const horTotalsWithExpensePerc = this.assocPercentageHorizTotals(
|
||||
'percentageExpense',
|
||||
expenseNode,
|
||||
node
|
||||
);
|
||||
return R.assoc('horizontalTotals', horTotalsWithExpensePerc, node);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Assoc net income percentage to horizontal totals nodes.
|
||||
* @param {IProfitLossSheetNode} expenseNode -
|
||||
* @param {IProfitLossSheetNode} node -
|
||||
* @returns {IProfitLossSheetNode}
|
||||
*/
|
||||
private assocColumnPercentageHorizTotals = R.curry(
|
||||
(netIncomeNode: IProfitLossSheetNode, node: IProfitLossSheetNode) => {
|
||||
const horTotalsWithExpensePerc = this.assocPercentageHorizTotals(
|
||||
'percentageColumn',
|
||||
netIncomeNode,
|
||||
node
|
||||
);
|
||||
return R.assoc('horizontalTotals', horTotalsWithExpensePerc, node);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private assocRowPercentageHorizTotals = R.curry((node) => {
|
||||
const horTotalsWithExpensePerc = this.assocHorizontalPercentageTotals(
|
||||
'percentageRow',
|
||||
node
|
||||
);
|
||||
return R.assoc('horizontalTotals', horTotalsWithExpensePerc, node);
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,395 @@
|
||||
import * as R from 'ramda';
|
||||
import { sumBy } from 'lodash';
|
||||
import {
|
||||
IProfitLossHorizontalDatePeriodNode,
|
||||
IProfitLossSchemaNode,
|
||||
IProfitLossSheetAccountNode,
|
||||
IProfitLossSheetAccountsNode,
|
||||
IProfitLossSheetEquationNode,
|
||||
IProfitLossSheetNode,
|
||||
} from '@/interfaces';
|
||||
import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod';
|
||||
import { ProfitLossSheetQuery } from './ProfitLossSheetQuery';
|
||||
|
||||
export const ProfitLossSheetPreviousPeriod = (Base) =>
|
||||
class extends R.compose(FinancialPreviousPeriod)(Base) {
|
||||
query: ProfitLossSheetQuery;
|
||||
|
||||
// ---------------------------
|
||||
// # Account
|
||||
// ---------------------------
|
||||
/**
|
||||
* Assoc previous period change attribute to account node.
|
||||
* @param {IProfitLossSheetAccountNode} accountNode
|
||||
* @returns {IProfitLossSheetAccountNode}
|
||||
*/
|
||||
protected assocPreviousPeriodTotalAccountNode = (
|
||||
node: IProfitLossSheetAccountNode
|
||||
): IProfitLossSheetAccountNode => {
|
||||
const total = this.repository.PPTotalAccountsLedger.whereAccountId(
|
||||
node.id
|
||||
).getClosingBalance();
|
||||
|
||||
return R.assoc('previousPeriod', this.getAmountMeta(total), node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Compose previous period account node.
|
||||
* @param {IProfitLossSheetAccountNode} accountNode
|
||||
* @returns {IProfitLossSheetAccountNode}
|
||||
*/
|
||||
protected previousPeriodAccountNodeCompose = (
|
||||
accountNode: IProfitLossSheetAccountNode
|
||||
): IProfitLossSheetAccountNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizTotals,
|
||||
this.assocPreviousPeriodAccountHorizNodeCompose
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
this.assocPreviousPeriodPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
this.assocPreviousPeriodChangeNode
|
||||
),
|
||||
this.assocPreviousPeriodTotalAccountNode
|
||||
)(accountNode);
|
||||
};
|
||||
|
||||
// ---------------------------
|
||||
// # Aggregate
|
||||
// ---------------------------
|
||||
/**
|
||||
* Assoc previous period total attribute to aggregate node.
|
||||
* @param {IProfitLossSheetAccountNode} accountNode
|
||||
* @returns {IProfitLossSheetAccountNode}
|
||||
*/
|
||||
private assocPreviousPeriodTotalAggregateNode = (
|
||||
node: IProfitLossSheetAccountNode
|
||||
) => {
|
||||
const total = sumBy(node.children, 'previousPeriod.amount');
|
||||
|
||||
return R.assoc('previousPeriod', this.getTotalAmountMeta(total), node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Compose previous period to aggregate node.
|
||||
* @param {IProfitLossSheetAccountNode} accountNode
|
||||
* @returns {IProfitLossSheetAccountNode}
|
||||
*/
|
||||
protected previousPeriodAggregateNodeCompose = (
|
||||
accountNode: IProfitLossSheetAccountNode
|
||||
): IProfitLossSheetAccountNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizTotals,
|
||||
this.assocPreviousPeriodAggregateHorizNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
this.assocPreviousPeriodTotalPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
this.assocPreviousPeriodTotalChangeNode
|
||||
),
|
||||
this.assocPreviousPeriodTotalAggregateNode
|
||||
)(accountNode);
|
||||
};
|
||||
|
||||
// ---------------------------
|
||||
// # Equation
|
||||
// --------------------------
|
||||
/**
|
||||
*
|
||||
* @param {(IProfitLossSchemaNode | IProfitLossSheetNode)[]} accNodes
|
||||
* @param {string} equation
|
||||
* @param {IProfitLossSheetNode} node
|
||||
* @returns {IProfitLossSheetEquationNode}
|
||||
*/
|
||||
private assocPreviousPeriodTotalEquationNode = R.curry(
|
||||
(
|
||||
accNodes: (IProfitLossSchemaNode | IProfitLossSheetNode)[],
|
||||
equation: string,
|
||||
node: IProfitLossSheetEquationNode
|
||||
): IProfitLossSheetEquationNode => {
|
||||
const previousPeriodNodePath = 'previousPeriod.amount';
|
||||
const tableNodes = this.getNodesTableForEvaluating(
|
||||
previousPeriodNodePath,
|
||||
accNodes
|
||||
);
|
||||
// Evaluate the given equation.
|
||||
const total = this.evaluateEquation(equation, tableNodes);
|
||||
|
||||
return R.assoc('previousPeriod', this.getTotalAmountMeta(total), node);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {(IProfitLossSchemaNode | IProfitLossSheetNode)[]} accNodes -
|
||||
* @param {string} node
|
||||
* @param {IProfitLossSheetEquationNode} node
|
||||
* @returns {IProfitLossSheetEquationNode}
|
||||
*/
|
||||
protected previousPeriodEquationNodeCompose = R.curry(
|
||||
(
|
||||
accNodes: (IProfitLossSchemaNode | IProfitLossSheetNode)[],
|
||||
equation: string,
|
||||
node: IProfitLossSheetEquationNode
|
||||
): IProfitLossSheetEquationNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizTotals,
|
||||
this.assocPreviousPeriodEquationHorizNode(accNodes, equation)
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
this.assocPreviousPeriodTotalPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
this.assocPreviousPeriodTotalChangeNode
|
||||
),
|
||||
this.assocPreviousPeriodTotalEquationNode(accNodes, equation)
|
||||
)(node);
|
||||
}
|
||||
);
|
||||
|
||||
// ---------------------------
|
||||
// # Horizontal Nodes - Account
|
||||
// --------------------------
|
||||
/**
|
||||
* Assoc previous period to account horizontal node.
|
||||
* @param {IProfitLossSheetAccountNode} node
|
||||
* @param {IProfitLossHorizontalDatePeriodNode} totalNode
|
||||
* @returns {IProfitLossHorizontalDatePeriodNode}
|
||||
*/
|
||||
private assocPerviousPeriodAccountHorizTotal = R.curry(
|
||||
(
|
||||
node: IProfitLossSheetAccountNode,
|
||||
totalNode: IProfitLossHorizontalDatePeriodNode
|
||||
): IProfitLossHorizontalDatePeriodNode => {
|
||||
const total = this.repository.PPPeriodsAccountsLedger.whereAccountId(
|
||||
node.id
|
||||
)
|
||||
.whereFromDate(totalNode.previousPeriodFromDate.date)
|
||||
.whereToDate(totalNode.previousPeriodToDate.date)
|
||||
.getClosingBalance();
|
||||
|
||||
return R.assoc('previousPeriod', this.getAmountMeta(total), totalNode);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* @param {IProfitLossSheetAccountNode} node
|
||||
* @param {IProfitLossSheetTotal}
|
||||
*/
|
||||
private previousPeriodAccountHorizNodeCompose = R.curry(
|
||||
(
|
||||
node: IProfitLossSheetAccountNode,
|
||||
horizontalTotalNode: IProfitLossHorizontalDatePeriodNode,
|
||||
index: number
|
||||
): IProfitLossHorizontalDatePeriodNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
this.assocPreviousPeriodPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
this.assocPreviousPeriodChangeNode
|
||||
),
|
||||
this.assocPerviousPeriodAccountHorizTotal(node),
|
||||
this.assocPreviousPeriodHorizNodeFromToDates(
|
||||
this.query.displayColumnsBy
|
||||
)
|
||||
)(horizontalTotalNode);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IProfitLossSheetAccountNode} node
|
||||
* @returns {IProfitLossSheetAccountNode}
|
||||
*/
|
||||
private assocPreviousPeriodAccountHorizNodeCompose = (
|
||||
node: IProfitLossSheetAccountNode
|
||||
): IProfitLossSheetAccountNode => {
|
||||
const horizontalTotals = R.addIndex(R.map)(
|
||||
this.previousPeriodAccountHorizNodeCompose(node),
|
||||
node.horizontalTotals
|
||||
);
|
||||
return R.assoc('horizontalTotals', horizontalTotals, node);
|
||||
};
|
||||
|
||||
// ----------------------------------
|
||||
// # Horizontal Nodes - Aggregate
|
||||
// ----------------------------------
|
||||
/**
|
||||
* Assoc previous period total to aggregate horizontal nodes.
|
||||
* @param {IProfitLossSheetAccountsNode} node
|
||||
* @param {number} index
|
||||
* @param {any} totalNode
|
||||
* @return {}
|
||||
*/
|
||||
private assocPreviousPeriodAggregateHorizTotal = R.curry(
|
||||
(
|
||||
node: IProfitLossSheetAccountsNode,
|
||||
index: number,
|
||||
totalNode: IProfitLossHorizontalDatePeriodNode
|
||||
) => {
|
||||
const total = this.getPPHorizNodesTotalSumation(index, node);
|
||||
|
||||
return R.assoc(
|
||||
'previousPeriod',
|
||||
this.getTotalAmountMeta(total),
|
||||
totalNode
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IProfitLossSheetAccountsNode} node
|
||||
* @param {IProfitLossHorizontalDatePeriodNode} horizontalTotalNode -
|
||||
* @param {number} index
|
||||
* @returns {IProfitLossHorizontalDatePeriodNode}
|
||||
*/
|
||||
private previousPeriodAggregateHorizNodeCompose = R.curry(
|
||||
(
|
||||
node: IProfitLossSheetAccountsNode,
|
||||
horizontalTotalNode: IProfitLossHorizontalDatePeriodNode,
|
||||
index: number
|
||||
): IProfitLossHorizontalDatePeriodNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
this.assocPreviousPeriodTotalPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
this.assocPreviousPeriodTotalChangeNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.assocPreviousPeriodAggregateHorizTotal(node, index)
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.assocPreviousPeriodHorizNodeFromToDates(
|
||||
this.query.displayColumnsBy
|
||||
)
|
||||
)
|
||||
)(horizontalTotalNode);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Assoc previous period to aggregate horizontal nodes.
|
||||
* @param {IProfitLossSheetAccountsNode} node
|
||||
* @returns
|
||||
*/
|
||||
private assocPreviousPeriodAggregateHorizNode = (
|
||||
node: IProfitLossSheetAccountsNode
|
||||
): IProfitLossSheetAccountsNode => {
|
||||
const horizontalTotals = R.addIndex(R.map)(
|
||||
this.previousPeriodAggregateHorizNodeCompose(node),
|
||||
node.horizontalTotals
|
||||
);
|
||||
return R.assoc('horizontalTotals', horizontalTotals, node);
|
||||
};
|
||||
|
||||
// ----------------------------------
|
||||
// # Horizontal Nodes - Equation
|
||||
// ----------------------------------
|
||||
/**
|
||||
*
|
||||
* @param {IProfitLossSheetNode[]} accNodes -
|
||||
* @param {string} equation
|
||||
* @param {index} number
|
||||
* @param {} totalNode
|
||||
*/
|
||||
private assocPreviousPeriodEquationHorizTotal = R.curry(
|
||||
(
|
||||
accNodes: IProfitLossSheetNode[],
|
||||
equation: string,
|
||||
index: number,
|
||||
totalNode
|
||||
): IProfitLossSheetNode => {
|
||||
const scopes = this.getNodesTableForEvaluating(
|
||||
`horizontalTotals[${index}].previousPeriod.amount`,
|
||||
accNodes
|
||||
);
|
||||
const total = this.evaluateEquation(equation, scopes);
|
||||
|
||||
return R.assoc(
|
||||
'previousPeriod',
|
||||
this.getTotalAmountMeta(total),
|
||||
totalNode
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IProfitLossSheetNode[]} accNodes -
|
||||
* @param {string} equation
|
||||
* @param {} horizontalTotalNode
|
||||
* @param {number} index
|
||||
*/
|
||||
private previousPeriodEquationHorizNodeCompose = R.curry(
|
||||
(
|
||||
accNodes: IProfitLossSheetNode[],
|
||||
equation: string,
|
||||
horizontalTotalNode,
|
||||
index: number
|
||||
) => {
|
||||
const assocHorizTotal = this.assocPreviousPeriodEquationHorizTotal(
|
||||
accNodes,
|
||||
equation,
|
||||
index
|
||||
);
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
this.assocPreviousPeriodTotalPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
this.assocPreviousPeriodTotalChangeNode
|
||||
),
|
||||
R.when(this.query.isPreviousPeriodActive, assocHorizTotal),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.assocPreviousPeriodHorizNodeFromToDates(
|
||||
this.query.displayColumnsBy
|
||||
)
|
||||
)
|
||||
)(horizontalTotalNode);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Assoc previous period equation to horizontal nodes.
|
||||
* @parma {IProfitLossSheetNode[]} accNodes -
|
||||
* @param {string} equation
|
||||
* @param {IProfitLossSheetEquationNode} node
|
||||
* @return {IProfitLossSheetEquationNode}
|
||||
*/
|
||||
private assocPreviousPeriodEquationHorizNode = R.curry(
|
||||
(
|
||||
accNodes: IProfitLossSheetNode[],
|
||||
equation: string,
|
||||
node: IProfitLossSheetEquationNode
|
||||
): IProfitLossSheetEquationNode => {
|
||||
const horizontalTotals = R.addIndex(R.map)(
|
||||
this.previousPeriodEquationHorizNodeCompose(accNodes, equation),
|
||||
node.horizontalTotals
|
||||
);
|
||||
return R.assoc('horizontalTotals', horizontalTotals, node);
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,367 @@
|
||||
import * as R from 'ramda';
|
||||
import { sumBy } from 'lodash';
|
||||
import { compose } from 'lodash/fp';
|
||||
import {
|
||||
IProfitLossSheetEquationNode,
|
||||
IProfitLossSheetAccountNode,
|
||||
IProfitLossSchemaNode,
|
||||
IProfitLossSheetNode,
|
||||
IProfitLossSheetTotal,
|
||||
} from '@/interfaces';
|
||||
import { ProfitLossSheetRepository } from './ProfitLossSheetRepository';
|
||||
import { FinancialPreviousYear } from '../FinancialPreviousYear';
|
||||
|
||||
export const ProfitLossSheetPreviousYear = (Base) =>
|
||||
class extends compose(FinancialPreviousYear)(Base) {
|
||||
repository: ProfitLossSheetRepository;
|
||||
|
||||
// ---------------------------
|
||||
// # Account
|
||||
// ---------------------------
|
||||
/**
|
||||
* Assoc previous year total attribute to account node.
|
||||
* @param {IProfitLossSheetAccountNode} accountNode
|
||||
* @returns {IProfitLossSheetAccountNode}
|
||||
*/
|
||||
private assocPreviousYearTotalAccountNode = (
|
||||
accountNode: IProfitLossSheetAccountNode
|
||||
) => {
|
||||
const total = this.repository.PYTotalAccountsLedger.whereAccountId(
|
||||
accountNode.id
|
||||
).getClosingBalance();
|
||||
|
||||
return R.assoc('previousYear', this.getAmountMeta(total), accountNode);
|
||||
};
|
||||
|
||||
/**
|
||||
* Compose previous year account node.
|
||||
* @param {IProfitLossSheetAccountNode} accountNode
|
||||
* @returns {IProfitLossSheetAccountNode}
|
||||
*/
|
||||
protected previousYearAccountNodeCompose = (
|
||||
accountNode: IProfitLossSheetAccountNode
|
||||
): IProfitLossSheetAccountNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizTotals,
|
||||
this.assocPreviousYearAccountHorizNodeCompose
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
this.assocPreviousYearPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
this.assocPreviousYearChangetNode
|
||||
),
|
||||
this.assocPreviousYearTotalAccountNode
|
||||
)(accountNode);
|
||||
};
|
||||
|
||||
// ---------------------------
|
||||
// # Aggregate
|
||||
// ---------------------------
|
||||
/**
|
||||
* Assoc previous year change attribute to aggregate node.
|
||||
* @param {IProfitLossSheetAccountNode} accountNode
|
||||
* @returns {IProfitLossSheetAccountNode}
|
||||
*/
|
||||
private assocPreviousYearTotalAggregateNode = (
|
||||
node: IProfitLossSheetAccountNode
|
||||
): IProfitLossSheetAccountNode => {
|
||||
const total = sumBy(node.children, 'previousYear.amount');
|
||||
|
||||
return R.assoc('previousYear', this.getTotalAmountMeta(total), node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Compose previous year to aggregate node.
|
||||
* @param {IProfitLossSheetAccountNode} accountNode
|
||||
* @returns {IProfitLossSheetAccountNode}
|
||||
*/
|
||||
protected previousYearAggregateNodeCompose = (
|
||||
accountNode: IProfitLossSheetAccountNode
|
||||
): IProfitLossSheetAccountNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizTotals,
|
||||
this.assocPreviousYearAggregateHorizNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
this.assocPreviousYearTotalPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
this.assocPreviousYearTotalChangeNode
|
||||
),
|
||||
this.assocPreviousYearTotalAggregateNode
|
||||
)(accountNode);
|
||||
};
|
||||
|
||||
// ---------------------------
|
||||
// # Equation
|
||||
// ---------------------------
|
||||
/**
|
||||
* Assoc previous year total to equation node.
|
||||
* @param {(IProfitLossSchemaNode | IProfitLossSheetNode)[]} accNodes
|
||||
* @param {string} equation
|
||||
* @param {IProfitLossSheetNode} node
|
||||
* @returns {IProfitLossSheetEquationNode}
|
||||
*/
|
||||
private assocPreviousYearTotalEquationNode = R.curry(
|
||||
(
|
||||
accNodes: (IProfitLossSchemaNode | IProfitLossSheetNode)[],
|
||||
equation: string,
|
||||
node: IProfitLossSheetNode
|
||||
) => {
|
||||
const previousPeriodNodePath = 'previousYear.amount';
|
||||
const tableNodes = this.getNodesTableForEvaluating(
|
||||
previousPeriodNodePath,
|
||||
accNodes
|
||||
);
|
||||
// Evaluate the given equation.
|
||||
const total = this.evaluateEquation(equation, tableNodes);
|
||||
|
||||
return R.assoc('previousYear', this.getTotalAmountMeta(total), node);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Previous year equation node.
|
||||
* @param {(IProfitLossSchemaNode | IProfitLossSheetNode)[]} accNodes -
|
||||
* @param {string} node
|
||||
* @param {IProfitLossSheetEquationNode} node
|
||||
* @returns {IProfitLossSheetEquationNode}
|
||||
*/
|
||||
protected previousYearEquationNodeCompose = R.curry(
|
||||
(
|
||||
accNodes: (IProfitLossSchemaNode | IProfitLossSheetNode)[],
|
||||
equation: string,
|
||||
node: IProfitLossSheetEquationNode
|
||||
) => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizTotals,
|
||||
this.assocPreviousYearEquationHorizNode(accNodes, equation)
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
this.assocPreviousYearTotalPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
this.assocPreviousYearTotalChangeNode
|
||||
),
|
||||
this.assocPreviousYearTotalEquationNode(accNodes, equation)
|
||||
)(node);
|
||||
}
|
||||
);
|
||||
|
||||
// ----------------------------------
|
||||
// # Horizontal Nodes - Account
|
||||
// ----------------------------------
|
||||
/**
|
||||
* Assoc preivous year to account horizontal total node.
|
||||
* @param {IProfitLossSheetAccountNode} node
|
||||
* @returns
|
||||
*/
|
||||
private assocPreviousYearAccountHorizTotal = R.curry(
|
||||
(node: IProfitLossSheetAccountNode, totalNode) => {
|
||||
const total = this.repository.PYPeriodsAccountsLedger.whereAccountId(
|
||||
node.id
|
||||
)
|
||||
.whereFromDate(totalNode.previousYearFromDate.date)
|
||||
.whereToDate(totalNode.previousYearToDate.date)
|
||||
.getClosingBalance();
|
||||
|
||||
return R.assoc('previousYear', this.getAmountMeta(total), totalNode);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Previous year account horizontal node composer.
|
||||
* @param {IProfitLossSheetAccountNode} horizontalTotalNode
|
||||
* @param {IProfitLossSheetTotal} horizontalTotalNode -
|
||||
* @returns {IProfitLossSheetTotal}
|
||||
*/
|
||||
private previousYearAccountHorizNodeCompose = R.curry(
|
||||
(
|
||||
node: IProfitLossSheetAccountNode,
|
||||
horizontalTotalNode: IProfitLossSheetTotal
|
||||
): IProfitLossSheetTotal => {
|
||||
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);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IProfitLossSheetAccountNode} node
|
||||
* @returns {IProfitLossSheetAccountNode}
|
||||
*/
|
||||
private assocPreviousYearAccountHorizNodeCompose = (
|
||||
node: IProfitLossSheetAccountNode
|
||||
): IProfitLossSheetAccountNode => {
|
||||
const horizontalTotals = R.map(
|
||||
this.previousYearAccountHorizNodeCompose(node),
|
||||
node.horizontalTotals
|
||||
);
|
||||
return R.assoc('horizontalTotals', horizontalTotals, node);
|
||||
};
|
||||
|
||||
// ----------------------------------
|
||||
// # Horizontal Nodes - Aggregate
|
||||
// ----------------------------------
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private assocPreviousYearAggregateHorizTotal = R.curry(
|
||||
(node, index, totalNode) => {
|
||||
const total = this.getPYHorizNodesTotalSumation(index, node);
|
||||
|
||||
return R.assoc(
|
||||
'previousYear',
|
||||
this.getTotalAmountMeta(total),
|
||||
totalNode
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private previousYearAggregateHorizNodeCompose = R.curry(
|
||||
(node, horizontalTotalNode, index: number) => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
this.assocPreviousYearTotalPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
this.assocPreviousYearTotalChangeNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.assocPreviousYearAggregateHorizTotal(node, index)
|
||||
)
|
||||
)(horizontalTotalNode);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IProfitLossSheetAccountNode} node
|
||||
* @returns {IProfitLossSheetAccountNode}
|
||||
*/
|
||||
private assocPreviousYearAggregateHorizNode = (
|
||||
node: IProfitLossSheetAccountNode
|
||||
): IProfitLossSheetAccountNode => {
|
||||
const horizontalTotals = R.addIndex(R.map)(
|
||||
this.previousYearAggregateHorizNodeCompose(node),
|
||||
node.horizontalTotals
|
||||
);
|
||||
return R.assoc('horizontalTotals', horizontalTotals, node);
|
||||
};
|
||||
|
||||
// ----------------------------------
|
||||
// # Horizontal Nodes - Equation
|
||||
// ----------------------------------
|
||||
/**
|
||||
*
|
||||
* @param {IProfitLossSheetNode[]} accNodes -
|
||||
* @param {string} equation
|
||||
* @param {number} index
|
||||
* @param {} totalNode -
|
||||
*/
|
||||
private assocPreviousYearEquationHorizTotal = R.curry(
|
||||
(
|
||||
accNodes: IProfitLossSheetNode[],
|
||||
equation: string,
|
||||
index: number,
|
||||
totalNode
|
||||
) => {
|
||||
const scopes = this.getNodesTableForEvaluating(
|
||||
`horizontalTotals[${index}].previousYear.amount`,
|
||||
accNodes
|
||||
);
|
||||
const total = this.evaluateEquation(equation, scopes);
|
||||
|
||||
return R.assoc(
|
||||
'previousYear',
|
||||
this.getTotalAmountMeta(total),
|
||||
totalNode
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IProfitLossSheetNode[]} accNodes -
|
||||
* @param {string} equation
|
||||
* @param {} horizontalTotalNode
|
||||
* @param {number} index
|
||||
*/
|
||||
private previousYearEquationHorizNodeCompose = R.curry(
|
||||
(
|
||||
accNodes: IProfitLossSheetNode[],
|
||||
equation: string,
|
||||
horizontalTotalNode,
|
||||
index: number
|
||||
) => {
|
||||
const assocHorizTotal = this.assocPreviousYearEquationHorizTotal(
|
||||
accNodes,
|
||||
equation,
|
||||
index
|
||||
);
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
this.assocPreviousYearTotalPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
this.assocPreviousYearTotalChangeNode
|
||||
),
|
||||
R.when(this.query.isPreviousYearActive, assocHorizTotal)
|
||||
)(horizontalTotalNode);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IProfitLossSheetNode[]} accNodes
|
||||
* @param {string} equation
|
||||
* @param {IProfitLossSheetEquationNode} node
|
||||
*/
|
||||
private assocPreviousYearEquationHorizNode = R.curry(
|
||||
(
|
||||
accNodes: IProfitLossSheetNode[],
|
||||
equation: string,
|
||||
node: IProfitLossSheetEquationNode
|
||||
) => {
|
||||
const horizontalTotals = R.addIndex(R.map)(
|
||||
this.previousYearEquationHorizNodeCompose(accNodes, equation),
|
||||
node.horizontalTotals
|
||||
);
|
||||
return R.assoc('horizontalTotals', horizontalTotals, node);
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,209 @@
|
||||
import { merge } from 'lodash';
|
||||
import * as R from 'ramda';
|
||||
import { IProfitLossSheetQuery, IFinancialDatePeriodsUnit } from '@/interfaces';
|
||||
import { DISPLAY_COLUMNS_BY } from './constants';
|
||||
import { FinancialDateRanges } from '../FinancialDateRanges';
|
||||
|
||||
export class ProfitLossSheetQuery extends R.compose(FinancialDateRanges)(
|
||||
class {}
|
||||
) {
|
||||
/**
|
||||
* P&L query.
|
||||
* @param {IProfitLossSheetQuery}
|
||||
*/
|
||||
public readonly query: IProfitLossSheetQuery;
|
||||
|
||||
/**
|
||||
* 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 {IProfitLossSheetQuery} query
|
||||
*/
|
||||
constructor(query: IProfitLossSheetQuery) {
|
||||
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 periods 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
};
|
||||
|
||||
// --------------------------------------
|
||||
// # 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves PY date based on the current query.
|
||||
* @returns {Date}
|
||||
*/
|
||||
public getTotalPreviousYear = (): Date => {
|
||||
return this.PYFromDate;
|
||||
};
|
||||
|
||||
// --------------------------------------
|
||||
// # Previous Period (PP)
|
||||
// --------------------------------------
|
||||
/**
|
||||
* Detarmines the report query has previous period enabled.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isPreviousPeriodActive = (): boolean => {
|
||||
return this.query.previousPeriod;
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines the report query has previous period percentage change active.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isPreviousPeriodPercentageActive = (): boolean => {
|
||||
return this.query.previousPeriodPercentageChange;
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines the report query has previous period change active.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isPreviousPeriodChangeActive = (): boolean => {
|
||||
return this.query.previousPeriodAmountChange;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves previous period date based on the current query.
|
||||
* @returns {Date}
|
||||
*/
|
||||
public getTotalPreviousPeriod = (): Date => {
|
||||
return this.PPFromDate;
|
||||
};
|
||||
|
||||
// --------------------------------------
|
||||
// # Percentage vertical/horizontal.
|
||||
// --------------------------------------
|
||||
/**
|
||||
* Detarmines whether percentage of expenses is active.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isExpensesPercentage = (): boolean => {
|
||||
return this.query.percentageExpense;
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines whether percentage of income is active.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isIncomePercentage = (): boolean => {
|
||||
return this.query.percentageIncome;
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines whether percentage of column is active.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isColumnPercentage = (): boolean => {
|
||||
return this.query.percentageColumn;
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines whether percentage of row is active.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isRowPercentage = (): boolean => {
|
||||
return this.query.percentageRow;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
import { defaultTo } from 'lodash';
|
||||
import * as R from 'ramda';
|
||||
import { Knex } from 'knex';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
import { transformToMapBy } from 'utils';
|
||||
import {
|
||||
IProfitLossSheetQuery,
|
||||
IAccount,
|
||||
IAccountTransactionsGroupBy,
|
||||
} from '@/interfaces';
|
||||
import Ledger from '@/services/Accounting/Ledger';
|
||||
import { ProfitLossSheetQuery } from './ProfitLossSheetQuery';
|
||||
import { FinancialDatePeriods } from '../FinancialDatePeriods';
|
||||
|
||||
export class ProfitLossSheetRepository extends R.compose(FinancialDatePeriods)(
|
||||
class {}
|
||||
) {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public models: any;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public accountsByType: any;
|
||||
|
||||
/**
|
||||
* @param {}
|
||||
*/
|
||||
public accounts: IAccount[];
|
||||
|
||||
/**
|
||||
* Transactions group type.
|
||||
* @param {IAccountTransactionsGroupBy}
|
||||
*/
|
||||
public transactionsGroupType: IAccountTransactionsGroupBy =
|
||||
IAccountTransactionsGroupBy.Month;
|
||||
|
||||
/**
|
||||
* @param {IProfitLossSheetQuery}
|
||||
*/
|
||||
public query: ProfitLossSheetQuery;
|
||||
|
||||
/**
|
||||
* Previous year to date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public PYToDate: Date;
|
||||
|
||||
/**
|
||||
* Previous year from date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public PYFromDate: Date;
|
||||
|
||||
/**
|
||||
* Previous year to date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public PPToDate: Date;
|
||||
|
||||
/**
|
||||
* Previous year from date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public PPFromDate: Date;
|
||||
|
||||
// ------------------------
|
||||
// # Total
|
||||
// ------------------------
|
||||
/**
|
||||
* Accounts total.
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public totalAccountsLedger: Ledger;
|
||||
|
||||
// ------------------------
|
||||
// # Date Periods.
|
||||
// ------------------------
|
||||
/**
|
||||
* Accounts date periods.
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public periodsAccountsLedger: Ledger;
|
||||
|
||||
// ------------------------
|
||||
// # Previous Year (PY)
|
||||
// ------------------------
|
||||
/**
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public PYTotalAccountsLedger: Ledger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public PYPeriodsAccountsLedger: Ledger;
|
||||
|
||||
// ------------------------
|
||||
// # Previous Period (PP).
|
||||
// ------------------------
|
||||
/**
|
||||
* PP Accounts Periods.
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public PPPeriodsAccountsLedger: Ledger;
|
||||
|
||||
/**
|
||||
* PP Accounts Total.
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public PPTotalAccountsLedger: Ledger;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {number} tenantId
|
||||
* @param {IBalanceSheetQuery} query
|
||||
*/
|
||||
constructor(models: any, query: IProfitLossSheetQuery) {
|
||||
super();
|
||||
|
||||
this.models = models;
|
||||
this.query = new ProfitLossSheetQuery(query);
|
||||
|
||||
this.transactionsGroupType = this.getGroupByFromDisplayColumnsBy(
|
||||
this.query.displayColumnsBy
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Async report repository.
|
||||
*/
|
||||
public asyncInitialize = async () => {
|
||||
await this.initAccounts();
|
||||
await this.initAccountsTotalLedger();
|
||||
|
||||
// Date Periods.
|
||||
if (this.query.isDatePeriodsColumnsType()) {
|
||||
await this.initTotalDatePeriods();
|
||||
}
|
||||
// Previous Period (PP)
|
||||
if (this.query.isPreviousPeriodActive()) {
|
||||
await this.initTotalPreviousPeriod();
|
||||
}
|
||||
if (
|
||||
this.query.isPreviousPeriodActive() &&
|
||||
this.query.isDatePeriodsColumnsType()
|
||||
) {
|
||||
await this.initPeriodsPreviousPeriod();
|
||||
}
|
||||
// Previous Year (PY).
|
||||
if (this.query.isPreviousYearActive()) {
|
||||
await this.initTotalPreviousYear();
|
||||
}
|
||||
if (
|
||||
this.query.isPreviousYearActive() &&
|
||||
this.query.isDatePeriodsColumnsType()
|
||||
) {
|
||||
await this.initPeriodsPreviousYear();
|
||||
}
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Accounts
|
||||
// ----------------------------
|
||||
/**
|
||||
* Initialize accounts of the report.
|
||||
*/
|
||||
private initAccounts = async () => {
|
||||
const accounts = await this.getAccounts();
|
||||
|
||||
// Inject to the repository.
|
||||
this.accounts = accounts;
|
||||
this.accountsByType = transformToMapBy(accounts, 'accountType');
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Closing Total.
|
||||
// ----------------------------
|
||||
/**
|
||||
* Initialize accounts closing total based on the given query.
|
||||
*/
|
||||
private initAccountsTotalLedger = async (): Promise<void> => {
|
||||
const totalByAccount = await this.accountsTotal(
|
||||
this.query.fromDate,
|
||||
this.query.toDate
|
||||
);
|
||||
// Inject to the repository.
|
||||
this.totalAccountsLedger = Ledger.fromTransactions(totalByAccount);
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Date periods.
|
||||
// ----------------------------
|
||||
/**
|
||||
* Initialize date periods total of accounts based on the given query.
|
||||
*/
|
||||
private initTotalDatePeriods = async (): Promise<void> => {
|
||||
// Retrieves grouped transactions by given date group.
|
||||
const periodsByAccount = await this.accountsDatePeriods(
|
||||
this.query.fromDate,
|
||||
this.query.toDate,
|
||||
this.transactionsGroupType
|
||||
);
|
||||
|
||||
// Inject to the repository.
|
||||
this.periodsAccountsLedger = Ledger.fromTransactions(periodsByAccount);
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Previous Period (PP).
|
||||
// ----------------------------
|
||||
/**
|
||||
* Initialize total of previous period (PP).
|
||||
*/
|
||||
private initTotalPreviousPeriod = async (): Promise<void> => {
|
||||
const PPTotalsByAccounts = await this.accountsTotal(
|
||||
this.query.PPFromDate,
|
||||
this.query.PPToDate
|
||||
);
|
||||
// Inject to the repository.
|
||||
this.PPTotalAccountsLedger = Ledger.fromTransactions(PPTotalsByAccounts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize date periods of previous period (PP).
|
||||
*/
|
||||
private initPeriodsPreviousPeriod = async (): Promise<void> => {
|
||||
// Retrieves grouped transactions by given date group.
|
||||
const periodsByAccount = await this.accountsDatePeriods(
|
||||
this.query.PPFromDate,
|
||||
this.query.PPToDate,
|
||||
this.transactionsGroupType
|
||||
);
|
||||
// Inject to the repository.
|
||||
this.PPPeriodsAccountsLedger = Ledger.fromTransactions(periodsByAccount);
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Previous Year (PY).
|
||||
// ----------------------------
|
||||
/**
|
||||
* Initialize total of previous year (PY).
|
||||
*/
|
||||
private initTotalPreviousYear = async (): Promise<void> => {
|
||||
const PYTotalsByAccounts = await this.accountsTotal(
|
||||
this.query.PYFromDate,
|
||||
this.query.PYToDate
|
||||
);
|
||||
// Inject to the repository.
|
||||
this.PYTotalAccountsLedger = Ledger.fromTransactions(PYTotalsByAccounts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize periods of previous year (PY).
|
||||
*/
|
||||
private initPeriodsPreviousYear = async () => {
|
||||
// Retrieves grouped transactions by given date group.
|
||||
const periodsByAccount = await this.accountsDatePeriods(
|
||||
this.query.PYFromDate,
|
||||
this.query.PYToDate,
|
||||
this.transactionsGroupType
|
||||
);
|
||||
// Inject to the repository.
|
||||
this.PYPeriodsAccountsLedger = Ledger.fromTransactions(periodsByAccount);
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Utils
|
||||
// ----------------------------
|
||||
/**
|
||||
* Retrieve the opening balance transactions of the report.
|
||||
*/
|
||||
public accountsTotal = async (fromDate: Date, toDate: Date) => {
|
||||
const { AccountTransaction } = this.models;
|
||||
|
||||
return AccountTransaction.query().onBuild((query) => {
|
||||
query.sum('credit as credit');
|
||||
query.sum('debit as debit');
|
||||
query.groupBy('accountId');
|
||||
query.select(['accountId']);
|
||||
|
||||
query.modify('filterDateRange', fromDate, toDate);
|
||||
query.withGraphFetched('account');
|
||||
|
||||
this.commonFilterBranchesQuery(query);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Closing accounts date periods.
|
||||
* @param openingDate
|
||||
* @param datePeriodsType
|
||||
* @returns
|
||||
*/
|
||||
public accountsDatePeriods = async (
|
||||
fromDate: Date,
|
||||
toDate: Date,
|
||||
datePeriodsType
|
||||
) => {
|
||||
const { AccountTransaction } = this.models;
|
||||
|
||||
return AccountTransaction.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);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Common branches filter query.
|
||||
* @param {Knex.QueryBuilder} query
|
||||
*/
|
||||
private commonFilterBranchesQuery = (query: Knex.QueryBuilder) => {
|
||||
if (!isEmpty(this.query.branchesIds)) {
|
||||
query.modify('filterByBranches', this.query.branchesIds);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve accounts of the report.
|
||||
* @return {Promise<IAccount[]>}
|
||||
*/
|
||||
private getAccounts = () => {
|
||||
const { Account } = this.models;
|
||||
|
||||
return Account.query();
|
||||
};
|
||||
|
||||
public getAccountsByType = (type: string) => {
|
||||
return defaultTo(this.accountsByType.get(type), []);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import {
|
||||
IProfitLossSheetQuery,
|
||||
IProfitLossSheetMeta,
|
||||
IProfitLossSheetNode,
|
||||
} from '@/interfaces';
|
||||
import ProfitLossSheet from './ProfitLossSheet';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import InventoryService from '@/services/Inventory/Inventory';
|
||||
import { parseBoolean } from 'utils';
|
||||
import { Tenant } from '@/system/models';
|
||||
import { mergeQueryWithDefaults } from './utils';
|
||||
import { ProfitLossSheetRepository } from './ProfitLossSheetRepository';
|
||||
|
||||
// Profit/Loss sheet service.
|
||||
@Service()
|
||||
export default class ProfitLossSheetService {
|
||||
@Inject()
|
||||
tenancy: TenancyService;
|
||||
|
||||
@Inject('logger')
|
||||
logger: any;
|
||||
|
||||
@Inject()
|
||||
inventoryService: InventoryService;
|
||||
|
||||
/**
|
||||
* Retrieve the trial balance sheet meta.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @returns {ITrialBalanceSheetMeta}
|
||||
*/
|
||||
reportMetadata(tenantId: number): IProfitLossSheetMeta {
|
||||
const settings = this.tenancy.settings(tenantId);
|
||||
|
||||
const isCostComputeRunning =
|
||||
this.inventoryService.isItemsCostComputeRunning(tenantId);
|
||||
const organizationName = settings.get({
|
||||
group: 'organization',
|
||||
key: 'name',
|
||||
});
|
||||
const baseCurrency = settings.get({
|
||||
group: 'organization',
|
||||
key: 'base_currency',
|
||||
});
|
||||
|
||||
return {
|
||||
isCostComputeRunning: parseBoolean(isCostComputeRunning, false),
|
||||
organizationName,
|
||||
baseCurrency,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve profit/loss sheet statement.
|
||||
* @param {number} tenantId
|
||||
* @param {IProfitLossSheetQuery} query
|
||||
* @return { }
|
||||
*/
|
||||
profitLossSheet = async (
|
||||
tenantId: number,
|
||||
query: IProfitLossSheetQuery
|
||||
): Promise<{
|
||||
data: IProfitLossSheetNode[];
|
||||
query: IProfitLossSheetQuery;
|
||||
meta: IProfitLossSheetMeta;
|
||||
}> => {
|
||||
const models = this.tenancy.models(tenantId);
|
||||
const i18n = this.tenancy.i18n(tenantId);
|
||||
|
||||
// Merges the given query with default filter query.
|
||||
const filter = mergeQueryWithDefaults(query);
|
||||
|
||||
// Get the given accounts or throw not found service error.
|
||||
// if (filter.accountsIds.length > 0) {
|
||||
// await this.accountsService.getAccountsOrThrowError(
|
||||
// tenantId,
|
||||
// filter.accountsIds
|
||||
// );
|
||||
// }
|
||||
const tenant = await Tenant.query()
|
||||
.findById(tenantId)
|
||||
.withGraphFetched('metadata');
|
||||
|
||||
const profitLossRepo = new ProfitLossSheetRepository(models, filter);
|
||||
|
||||
await profitLossRepo.asyncInitialize();
|
||||
|
||||
// Profit/Loss report instance.
|
||||
const profitLossInstance = new ProfitLossSheet(
|
||||
profitLossRepo,
|
||||
filter,
|
||||
tenant.metadata.baseCurrency,
|
||||
i18n
|
||||
);
|
||||
// Profit/loss report data and collumns.
|
||||
const profitLossData = profitLossInstance.reportData();
|
||||
|
||||
return {
|
||||
data: profitLossData,
|
||||
query: filter,
|
||||
meta: this.reportMetadata(tenantId),
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
IProfitLossSheetQuery,
|
||||
ITableColumn,
|
||||
IProfitLossSheetAccountsNode,
|
||||
ITableColumnAccessor,
|
||||
ITableRow,
|
||||
ProfitLossNodeType,
|
||||
ProfitLossSheetRowType,
|
||||
IProfitLossSheetNode,
|
||||
IProfitLossSheetEquationNode,
|
||||
IProfitLossSheetAccountNode,
|
||||
} from '@/interfaces';
|
||||
import { tableRowMapper } from 'utils';
|
||||
import { FinancialTable } from '../FinancialTable';
|
||||
import { ProfitLossSheetBase } from './ProfitLossSheetBase';
|
||||
import { ProfitLossSheetTablePercentage } from './ProfitLossSheetTablePercentage';
|
||||
import { ProfitLossSheetQuery } from './ProfitLossSheetQuery';
|
||||
import { ProfitLossTablePreviousPeriod } from './ProfitLossTablePreviousPeriod';
|
||||
import { ProfitLossTablePreviousYear } from './ProfitLossTablePreviousYear';
|
||||
import { FinancialSheetStructure } from '../FinancialSheetStructure';
|
||||
import { ProfitLossSheetTableDatePeriods } from './ProfitLossSheetTableDatePeriods';
|
||||
|
||||
export class ProfitLossSheetTable extends R.compose(
|
||||
ProfitLossTablePreviousPeriod,
|
||||
ProfitLossTablePreviousYear,
|
||||
ProfitLossSheetTablePercentage,
|
||||
ProfitLossSheetTableDatePeriods,
|
||||
ProfitLossSheetBase,
|
||||
FinancialSheetStructure,
|
||||
FinancialTable
|
||||
)(class {}) {
|
||||
readonly query: ProfitLossSheetQuery;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {} date
|
||||
* @param {IProfitLossSheetQuery} query
|
||||
*/
|
||||
constructor(data: any, query: IProfitLossSheetQuery, i18n: any) {
|
||||
super();
|
||||
|
||||
this.query = new ProfitLossSheetQuery(query);
|
||||
this.reportData = data;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
// ----------------------------------
|
||||
// # Rows
|
||||
// ----------------------------------
|
||||
/**
|
||||
* Retrieve the total column accessor.
|
||||
* @return {ITableColumnAccessor[]}
|
||||
*/
|
||||
private totalColumnAccessor = (): ITableColumnAccessor[] => {
|
||||
return R.pipe(
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
R.concat(this.previousPeriodColumnAccessor())
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
R.concat(this.previousYearColumnAccessor())
|
||||
),
|
||||
R.concat(this.percentageColumnsAccessor()),
|
||||
R.concat([{ key: 'total', accessor: 'total.formattedAmount' }])
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Common columns accessors.
|
||||
* @returns {ITableColumnAccessor}
|
||||
*/
|
||||
private commonColumnsAccessors = (): ITableColumnAccessor[] => {
|
||||
return R.compose(
|
||||
R.concat([{ key: 'name', accessor: 'name' }]),
|
||||
R.ifElse(
|
||||
this.query.isDatePeriodsColumnsType,
|
||||
R.concat(this.datePeriodsColumnsAccessors()),
|
||||
R.concat(this.totalColumnAccessor())
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IProfitLossSheetAccountNode} node
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
private accountNodeToTableRow = (
|
||||
node: IProfitLossSheetAccountNode
|
||||
): ITableRow => {
|
||||
const columns = this.commonColumnsAccessors();
|
||||
const meta = {
|
||||
rowTypes: [ProfitLossSheetRowType.ACCOUNT],
|
||||
id: node.id,
|
||||
};
|
||||
return tableRowMapper(node, columns, meta);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IProfitLossSheetAccountsNode} node
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
private accountsNodeToTableRow = (
|
||||
node: IProfitLossSheetAccountsNode
|
||||
): ITableRow => {
|
||||
const columns = this.commonColumnsAccessors();
|
||||
const meta = {
|
||||
rowTypes: [ProfitLossSheetRowType.ACCOUNTS],
|
||||
id: node.id,
|
||||
};
|
||||
return tableRowMapper(node, columns, meta);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IProfitLossSheetEquationNode} node
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
private equationNodeToTableRow = (
|
||||
node: IProfitLossSheetEquationNode
|
||||
): ITableRow => {
|
||||
const columns = this.commonColumnsAccessors();
|
||||
|
||||
const meta = {
|
||||
rowTypes: [ProfitLossSheetRowType.TOTAL],
|
||||
id: node.id,
|
||||
};
|
||||
return tableRowMapper(node, columns, meta);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IProfitLossSheetNode} node
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
private nodeToTableRowCompose = (node: IProfitLossSheetNode): ITableRow => {
|
||||
return R.cond([
|
||||
[
|
||||
this.isNodeType(ProfitLossNodeType.ACCOUNTS),
|
||||
this.accountsNodeToTableRow,
|
||||
],
|
||||
[
|
||||
this.isNodeType(ProfitLossNodeType.EQUATION),
|
||||
this.equationNodeToTableRow,
|
||||
],
|
||||
[this.isNodeType(ProfitLossNodeType.ACCOUNT), this.accountNodeToTableRow],
|
||||
])(node);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IProfitLossSheetNode[]} nodes
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
private nodesToTableRowsCompose = (
|
||||
nodes: IProfitLossSheetNode[]
|
||||
): ITableRow[] => {
|
||||
return this.mapNodesDeep(nodes, this.nodeToTableRowCompose);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the table rows.
|
||||
* @returns {ITableRow[]}
|
||||
*/
|
||||
public tableRows = (): ITableRow[] => {
|
||||
return R.compose(
|
||||
this.addTotalRows,
|
||||
this.nodesToTableRowsCompose
|
||||
)(this.reportData);
|
||||
};
|
||||
|
||||
// ----------------------------------
|
||||
// # Columns.
|
||||
// ----------------------------------
|
||||
/**
|
||||
* Retrieve total column children columns.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
private tableColumnChildren = (): ITableColumn[] => {
|
||||
return R.compose(
|
||||
R.unless(
|
||||
R.isEmpty,
|
||||
R.concat([
|
||||
{ key: 'total', label: this.i18n.__('profit_loss_sheet.total') },
|
||||
])
|
||||
),
|
||||
R.concat(this.percentageColumns()),
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
R.concat(this.getPreviousYearColumns())
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
R.concat(this.getPreviousPeriodColumns())
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the total column.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
private totalColumn = (): ITableColumn[] => {
|
||||
return [
|
||||
{
|
||||
key: 'total',
|
||||
label: this.i18n.__('profit_loss_sheet.total'),
|
||||
children: this.tableColumnChildren(),
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the table columns.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
public tableColumns = (): ITableColumn[] => {
|
||||
return R.compose(
|
||||
this.tableColumnsCellIndexing,
|
||||
R.concat([
|
||||
{ key: 'name', label: this.i18n.__('profit_loss_sheet.account_name') },
|
||||
]),
|
||||
R.ifElse(
|
||||
this.query.isDatePeriodsColumnsType,
|
||||
R.concat(this.datePeriodsColumns()),
|
||||
R.concat(this.totalColumn())
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
import * as R from 'ramda';
|
||||
import moment from 'moment';
|
||||
import { ITableColumn, IDateRange, ITableColumnAccessor } from '@/interfaces';
|
||||
import { FinancialDatePeriods } from '../FinancialDatePeriods';
|
||||
import { ProfitLossSheetTablePercentage } from './ProfitLossSheetTablePercentage';
|
||||
import { ProfitLossTablePreviousPeriod } from './ProfitLossTablePreviousPeriod';
|
||||
|
||||
export const ProfitLossSheetTableDatePeriods = (Base) =>
|
||||
class extends R.compose(
|
||||
ProfitLossSheetTablePercentage,
|
||||
ProfitLossTablePreviousPeriod,
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
// --------------------------------
|
||||
// # Accessors
|
||||
// --------------------------------
|
||||
/**
|
||||
* Date period columns accessor.
|
||||
* @param {IDateRange} dateRange -
|
||||
* @param {number} index -
|
||||
*/
|
||||
private datePeriodColumnsAccessor = R.curry(
|
||||
(dateRange: IDateRange, index: number) => {
|
||||
return R.pipe(
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
R.concat(this.previousPeriodHorizontalColumnAccessors(index))
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
R.concat(this.previousYearHorizontalColumnAccessors(index))
|
||||
),
|
||||
R.concat(this.percetangeHorizontalColumnsAccessor(index)),
|
||||
R.concat([
|
||||
{
|
||||
key: `date-range-${index}`,
|
||||
accessor: `horizontalTotals[${index}].total.formattedAmount`,
|
||||
},
|
||||
])
|
||||
)([]);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Retrieve the date periods columns accessors.
|
||||
* @returns {ITableColumnAccessor[]}
|
||||
*/
|
||||
protected datePeriodsColumnsAccessors = (): ITableColumnAccessor[] => {
|
||||
return R.compose(
|
||||
R.flatten,
|
||||
R.addIndex(R.map)(this.datePeriodColumnsAccessor)
|
||||
)(this.datePeriods);
|
||||
};
|
||||
|
||||
// --------------------------------
|
||||
// # Columns
|
||||
// --------------------------------
|
||||
/**
|
||||
* Retrieve the formatted column label from the given date range.
|
||||
* @param {ICashFlowDateRange} dateRange -
|
||||
* @return {string}
|
||||
*/
|
||||
private formatColumnLabel = (dateRange) => {
|
||||
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);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} index
|
||||
* @param {IDateRange} dateRange
|
||||
* @returns {}
|
||||
*/
|
||||
private datePeriodChildrenColumns = (
|
||||
index: number,
|
||||
dateRange: IDateRange
|
||||
) => {
|
||||
return R.compose(
|
||||
R.unless(
|
||||
R.isEmpty,
|
||||
R.concat([
|
||||
{ key: `total`, label: this.i18n.__('profit_loss_sheet.total') },
|
||||
])
|
||||
),
|
||||
R.concat(this.percentageColumns()),
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
R.concat(this.getPreviousYearDatePeriodColumnPlugin(dateRange))
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
R.concat(this.getPreviousPeriodDatePeriodsPlugin(dateRange))
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IDateRange} dateRange
|
||||
* @param {number} index
|
||||
* @returns {ITableColumn}
|
||||
*/
|
||||
private 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[]}
|
||||
*/
|
||||
protected datePeriodsColumns = (): ITableColumn[] => {
|
||||
return this.datePeriods.map(this.datePeriodColumn);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,131 @@
|
||||
import * as R from 'ramda';
|
||||
import { ITableColumn, ITableColumnAccessor } from '@/interfaces';
|
||||
import { ProfitLossSheetQuery } from './ProfitLossSheetQuery';
|
||||
|
||||
export const ProfitLossSheetTablePercentage = (Base) =>
|
||||
class extends Base {
|
||||
/**
|
||||
* @param {ProfitLossSheetQuery}
|
||||
*/
|
||||
readonly query: ProfitLossSheetQuery;
|
||||
|
||||
// ----------------------------------
|
||||
// # Columns.
|
||||
// ----------------------------------
|
||||
/**
|
||||
* Retrieve percentage of column/row columns.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
protected percentageColumns = (): ITableColumn[] => {
|
||||
return R.pipe(
|
||||
R.when(
|
||||
this.query.isIncomePercentage,
|
||||
R.append({
|
||||
key: 'percentage_income',
|
||||
label: this.i18n.__('profit_loss_sheet.percentage_of_income'),
|
||||
})
|
||||
),
|
||||
R.when(
|
||||
this.query.isExpensesPercentage,
|
||||
R.append({
|
||||
key: 'percentage_expenses',
|
||||
label: this.i18n.__('profit_loss_sheet.percentage_of_expenses'),
|
||||
})
|
||||
),
|
||||
R.when(
|
||||
this.query.isColumnPercentage,
|
||||
R.append({
|
||||
key: 'percentage_column',
|
||||
label: this.i18n.__('profit_loss_sheet.percentage_of_column'),
|
||||
})
|
||||
),
|
||||
R.when(
|
||||
this.query.isRowPercentage,
|
||||
R.append({
|
||||
key: 'percentage_row',
|
||||
label: this.i18n.__('profit_loss_sheet.percentage_of_row'),
|
||||
})
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
|
||||
// ----------------------------------
|
||||
// # Accessors.
|
||||
// ----------------------------------
|
||||
/**
|
||||
* Retrieves percentage of column/row accessors.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
protected percentageColumnsAccessor = (): ITableColumnAccessor[] => {
|
||||
return R.pipe(
|
||||
R.when(
|
||||
this.query.isIncomePercentage,
|
||||
R.append({
|
||||
key: 'percentage_income',
|
||||
accessor: 'percentageIncome.formattedAmount',
|
||||
})
|
||||
),
|
||||
R.when(
|
||||
this.query.isExpensesPercentage,
|
||||
R.append({
|
||||
key: 'percentage_expense',
|
||||
accessor: 'percentageExpense.formattedAmount',
|
||||
})
|
||||
),
|
||||
R.when(
|
||||
this.query.isColumnPercentage,
|
||||
R.append({
|
||||
key: 'percentage_column',
|
||||
accessor: 'percentageColumn.formattedAmount',
|
||||
})
|
||||
),
|
||||
R.when(
|
||||
this.query.isRowPercentage,
|
||||
R.append({
|
||||
key: 'percentage_row',
|
||||
accessor: 'percentageRow.formattedAmount',
|
||||
})
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves percentage horizontal columns accessors.
|
||||
* @param {number} index
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
protected percetangeHorizontalColumnsAccessor = (
|
||||
index: number
|
||||
): ITableColumnAccessor[] => {
|
||||
return R.pipe(
|
||||
R.when(
|
||||
this.query.isIncomePercentage,
|
||||
R.append({
|
||||
key: `percentage_income-${index}`,
|
||||
accessor: `horizontalTotals[${index}].percentageIncome.formattedAmount`,
|
||||
})
|
||||
),
|
||||
R.when(
|
||||
this.query.isExpensesPercentage,
|
||||
R.append({
|
||||
key: `percentage_expense-${index}`,
|
||||
accessor: `horizontalTotals[${index}].percentageExpense.formattedAmount`,
|
||||
})
|
||||
),
|
||||
R.when(
|
||||
this.query.isColumnPercentage,
|
||||
R.append({
|
||||
key: `percentage_of_column-${index}`,
|
||||
accessor: `horizontalTotals[${index}].percentageColumn.formattedAmount`,
|
||||
})
|
||||
),
|
||||
R.when(
|
||||
this.query.isRowPercentage,
|
||||
R.append({
|
||||
key: `percentage_of_row-${index}`,
|
||||
accessor: `horizontalTotals[${index}].percentageRow.formattedAmount`,
|
||||
})
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,93 @@
|
||||
import * as R from 'ramda';
|
||||
import { IDateRange, ITableColumn, ITableColumnAccessor } from '@/interfaces';
|
||||
import { ProfitLossSheetQuery } from './ProfitLossSheetQuery';
|
||||
import { FinancialTablePreviousPeriod } from '../FinancialTablePreviousPeriod';
|
||||
|
||||
export const ProfitLossTablePreviousPeriod = (Base) =>
|
||||
class extends R.compose(FinancialTablePreviousPeriod)(Base) {
|
||||
query: ProfitLossSheetQuery;
|
||||
|
||||
// ----------------------------
|
||||
// # Columns
|
||||
// ----------------------------
|
||||
/**
|
||||
* Retrieves pervious period comparison columns.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
protected getPreviousPeriodColumns = (
|
||||
dateRange?: IDateRange
|
||||
): ITableColumn[] => {
|
||||
return R.pipe(
|
||||
// Previous period columns.
|
||||
R.append(this.getPreviousPeriodTotalColumn(dateRange)),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
R.append(this.getPreviousPeriodChangeColumn())
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
R.append(this.getPreviousPeriodPercentageColumn())
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Compose the previous period for date periods columns.
|
||||
* @params {IDateRange}
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
protected getPreviousPeriodDatePeriodsPlugin = (
|
||||
dateRange: IDateRange
|
||||
): ITableColumn[] => {
|
||||
const PPDateRange = this.getPPDatePeriodDateRange(
|
||||
dateRange.fromDate,
|
||||
dateRange.toDate,
|
||||
this.query.displayColumnsBy
|
||||
);
|
||||
return this.getPreviousPeriodColumns(PPDateRange);
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Accessors
|
||||
// ----------------------------
|
||||
/**
|
||||
* Retrieves previous period columns accessors.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
protected previousPeriodColumnAccessor = (): ITableColumnAccessor[] => {
|
||||
return R.pipe(
|
||||
// Previous period columns.
|
||||
R.append(this.getPreviousPeriodTotalAccessor()),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
R.append(this.getPreviousPeriodChangeAccessor())
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
R.append(this.getPreviousPeriodPercentageAccessor())
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Previous period period column accessor.
|
||||
* @param {number} index
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
protected previousPeriodHorizontalColumnAccessors = (
|
||||
index: number
|
||||
): ITableColumnAccessor[] => {
|
||||
return R.pipe(
|
||||
// Previous period columns.
|
||||
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,107 @@
|
||||
import * as R from 'ramda';
|
||||
import { IDateRange, ITableColumn, ITableColumnAccessor } from '@/interfaces';
|
||||
import { ProfitLossSheetQuery } from './ProfitLossSheetQuery';
|
||||
import { FinancialTablePreviousYear } from '../FinancialTablePreviousYear';
|
||||
import { FinancialDateRanges } from '../FinancialDateRanges';
|
||||
|
||||
export const ProfitLossTablePreviousYear = (Base) =>
|
||||
class extends R.compose(
|
||||
FinancialTablePreviousYear,
|
||||
FinancialDateRanges
|
||||
)(Base) {
|
||||
query: ProfitLossSheetQuery;
|
||||
|
||||
// ------------------------------------
|
||||
// # Columns.
|
||||
// ------------------------------------
|
||||
/**
|
||||
* Retrieves pervious year comparison columns.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
protected getPreviousYearColumns = (
|
||||
dateRange?: IDateRange
|
||||
): ITableColumn[] => {
|
||||
return R.pipe(
|
||||
// Previous year columns.
|
||||
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 {ITableColumn[]}
|
||||
*/
|
||||
private previousYearDatePeriodColumnCompose = (
|
||||
dateRange: IDateRange
|
||||
): ITableColumn[] => {
|
||||
const PYDateRange = this.getPreviousYearDateRange(
|
||||
dateRange.fromDate,
|
||||
dateRange.toDate
|
||||
);
|
||||
return this.getPreviousYearColumns(PYDateRange);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves previous year date periods columns.
|
||||
* @param {IDateRange} dateRange
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
protected getPreviousYearDatePeriodColumnPlugin = (
|
||||
dateRange: IDateRange
|
||||
): ITableColumn[] => {
|
||||
return this.previousYearDatePeriodColumnCompose(dateRange);
|
||||
};
|
||||
|
||||
// ---------------------------------------------------
|
||||
// # Accessors.
|
||||
// ---------------------------------------------------
|
||||
/**
|
||||
* Retrieves previous year columns accessors.
|
||||
* @returns {ITableColumnAccessor[]}
|
||||
*/
|
||||
protected previousYearColumnAccessor = (): ITableColumnAccessor[] => {
|
||||
return R.pipe(
|
||||
// Previous year columns.
|
||||
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[]}
|
||||
*/
|
||||
protected previousYearHorizontalColumnAccessors = (
|
||||
index: number
|
||||
): ITableColumnAccessor[] => {
|
||||
return R.pipe(
|
||||
// Previous year columns.
|
||||
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,21 @@
|
||||
import { ProfitLossNodeType } from '@/interfaces';
|
||||
|
||||
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',
|
||||
TOTAL = 'TOTAL',
|
||||
}
|
||||
|
||||
export const TOTAL_NODE_TYPES = [
|
||||
ProfitLossNodeType.ACCOUNTS,
|
||||
ProfitLossNodeType.AGGREGATE,
|
||||
ProfitLossNodeType.EQUATION
|
||||
];
|
||||
@@ -0,0 +1,54 @@
|
||||
import moment from 'moment';
|
||||
import { merge } from 'lodash';
|
||||
import { IProfitLossSheetQuery } from '@/interfaces';
|
||||
|
||||
/**
|
||||
* Default sheet filter query.
|
||||
* @return {IBalanceSheetQuery}
|
||||
*/
|
||||
export const getDefaultPLQuery = (): IProfitLossSheetQuery => ({
|
||||
fromDate: moment().startOf('year').format('YYYY-MM-DD'),
|
||||
toDate: moment().endOf('year').format('YYYY-MM-DD'),
|
||||
|
||||
numberFormat: {
|
||||
divideOn1000: false,
|
||||
negativeFormat: 'mines',
|
||||
showZero: false,
|
||||
formatMoney: 'total',
|
||||
precision: 2,
|
||||
},
|
||||
basis: 'accural',
|
||||
|
||||
noneZero: false,
|
||||
noneTransactions: false,
|
||||
|
||||
displayColumnsType: 'total',
|
||||
displayColumnsBy: 'month',
|
||||
|
||||
accountsIds: [],
|
||||
|
||||
percentageColumn: false,
|
||||
percentageRow: false,
|
||||
|
||||
percentageIncome: false,
|
||||
percentageExpense: false,
|
||||
|
||||
previousPeriod: false,
|
||||
previousPeriodAmountChange: false,
|
||||
previousPeriodPercentageChange: false,
|
||||
|
||||
previousYear: false,
|
||||
previousYearAmountChange: false,
|
||||
previousYearPercentageChange: false,
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
* @param query
|
||||
* @returns
|
||||
*/
|
||||
export const mergeQueryWithDefaults = (
|
||||
query: IProfitLossSheetQuery
|
||||
): IProfitLossSheetQuery => {
|
||||
return merge(getDefaultPLQuery(), query);
|
||||
};
|
||||
Reference in New Issue
Block a user