mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 05:10:31 +00:00
add server to monorepo.
This commit is contained in:
@@ -0,0 +1,305 @@
|
||||
import * as R from 'ramda';
|
||||
import { defaultTo, isEmpty, sumBy } from 'lodash';
|
||||
import FinancialSheet from '../FinancialSheet';
|
||||
import {
|
||||
IBalanceSheetAggregateNode,
|
||||
IBalanceSheetAccountNode,
|
||||
BALANCE_SHEET_SCHEMA_NODE_TYPE,
|
||||
IBalanceSheetQuery,
|
||||
INumberFormatQuery,
|
||||
IAccount,
|
||||
IBalanceSheetSchemaNode,
|
||||
IBalanceSheetSchemaAggregateNode,
|
||||
IBalanceSheetDataNode,
|
||||
IBalanceSheetSchemaAccountNode,
|
||||
IBalanceSheetCommonNode,
|
||||
} from '../../../interfaces';
|
||||
import { BalanceSheetSchema } from './BalanceSheetSchema';
|
||||
import { BalanceSheetPercentage } from './BalanceSheetPercentage';
|
||||
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
|
||||
import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear';
|
||||
import { BalanceSheetDatePeriods } from './BalanceSheetDatePeriods';
|
||||
import { BalanceSheetBase } from './BalanceSheetBase';
|
||||
import { FinancialSheetStructure } from '../FinancialSheetStructure';
|
||||
import BalanceSheetRepository from './BalanceSheetRepository';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { BalanceSheetFiltering } from './BalanceSheetFiltering';
|
||||
|
||||
export default class BalanceSheet extends R.compose(
|
||||
BalanceSheetFiltering,
|
||||
BalanceSheetDatePeriods,
|
||||
BalanceSheetComparsionPreviousPeriod,
|
||||
BalanceSheetComparsionPreviousYear,
|
||||
BalanceSheetPercentage,
|
||||
BalanceSheetSchema,
|
||||
BalanceSheetBase,
|
||||
FinancialSheetStructure
|
||||
)(FinancialSheet) {
|
||||
/**
|
||||
* Balance sheet query.
|
||||
* @param {BalanceSheetQuery}
|
||||
*/
|
||||
readonly query: BalanceSheetQuery;
|
||||
|
||||
/**
|
||||
* Balance sheet number format query.
|
||||
* @param {INumberFormatQuery}
|
||||
*/
|
||||
readonly numberFormat: INumberFormatQuery;
|
||||
|
||||
/**
|
||||
* Base currency of the organization.
|
||||
* @param {string}
|
||||
*/
|
||||
readonly baseCurrency: string;
|
||||
|
||||
readonly i18n: any;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {IBalanceSheetQuery} query -
|
||||
* @param {IAccount[]} accounts -
|
||||
* @param {string} baseCurrency -
|
||||
*/
|
||||
constructor(
|
||||
query: IBalanceSheetQuery,
|
||||
repository: BalanceSheetRepository,
|
||||
baseCurrency: string,
|
||||
i18n
|
||||
) {
|
||||
super();
|
||||
|
||||
this.query = new BalanceSheetQuery(query);
|
||||
this.repository = repository;
|
||||
this.baseCurrency = baseCurrency;
|
||||
this.numberFormat = this.query.query.numberFormat;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the accounts node of accounts types.
|
||||
* @param {string} accountsTypes
|
||||
* @returns {IAccount[]}
|
||||
*/
|
||||
private getAccountsByAccountTypes = (accountsTypes: string[]): IAccount[] => {
|
||||
const mapAccountsByTypes = R.map((accountType) =>
|
||||
defaultTo(this.repository.accountsByType.get(accountType), [])
|
||||
);
|
||||
return R.compose(R.flatten, mapAccountsByTypes)(accountsTypes);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the aggregate schema node type.
|
||||
* @param {IBalanceSheetSchemaAggregateNode} node - Schema node.
|
||||
* @return {IBalanceSheetAggregateNode}
|
||||
*/
|
||||
private reportSchemaAggregateNodeMapper = (
|
||||
node: IBalanceSheetSchemaAggregateNode
|
||||
): IBalanceSheetAggregateNode => {
|
||||
const total = this.getTotalOfNodes(node.children);
|
||||
|
||||
return {
|
||||
name: this.i18n.__(node.name),
|
||||
id: node.id,
|
||||
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE,
|
||||
total: this.getTotalAmountMeta(total),
|
||||
children: node.children,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Compose shema aggregate node of balance sheet schema.
|
||||
* @param {IBalanceSheetSchemaAggregateNode} node
|
||||
* @returns {IBalanceSheetSchemaAggregateNode}
|
||||
*/
|
||||
private schemaAggregateNodeCompose = (
|
||||
node: IBalanceSheetSchemaAggregateNode
|
||||
) => {
|
||||
return R.compose(
|
||||
this.aggregateNodeTotalMapper,
|
||||
this.reportSchemaAggregateNodeMapper
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the account model to report account node.
|
||||
* @param {IAccount} account
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
private reportSchemaAccountNodeMapper = (
|
||||
account: IAccount
|
||||
): IBalanceSheetAccountNode => {
|
||||
const total = this.repository.totalAccountsLedger
|
||||
.whereAccountId(account.id)
|
||||
.getClosingBalance();
|
||||
|
||||
return {
|
||||
id: account.id,
|
||||
index: account.index,
|
||||
name: account.name,
|
||||
code: account.code,
|
||||
total: this.getAmountMeta(total),
|
||||
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNT,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IAccount} account
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
private reportSchemaAccountNodeComposer = (
|
||||
account: IAccount
|
||||
): IBalanceSheetAccountNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.previousYearAccountNodeComposer
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.previousPeriodAccountNodeComposer
|
||||
),
|
||||
R.when(
|
||||
this.query.isDatePeriodsColumnsType,
|
||||
this.assocAccountNodeDatePeriods
|
||||
),
|
||||
this.reportSchemaAccountNodeMapper
|
||||
)(account);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the total of the given nodes.
|
||||
* @param {IBalanceSheetCommonNode[]} nodes
|
||||
* @returns {number}
|
||||
*/
|
||||
private getTotalOfNodes = (nodes: IBalanceSheetCommonNode[]) => {
|
||||
return sumBy(nodes, 'total.amount');
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the report accounts node by the given accounts types.
|
||||
* @param {string[]} accountsTypes
|
||||
* @returns {}
|
||||
*/
|
||||
private getAccountsNodesByAccountTypes = (accountsTypes: string[]) => {
|
||||
const accounts = this.getAccountsByAccountTypes(accountsTypes);
|
||||
|
||||
return R.compose(R.map(this.reportSchemaAccountNodeComposer))(accounts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the accounts schema node type.
|
||||
* @param {IBalanceSheetSchemaNode} node - Schema node.
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
private reportSchemaAccountsNodeMapper = (
|
||||
node: IBalanceSheetSchemaAccountNode
|
||||
): IBalanceSheetAccountNode => {
|
||||
const accounts = this.getAccountsNodesByAccountTypes(node.accountsTypes);
|
||||
const total = this.getTotalOfNodes(accounts);
|
||||
|
||||
return {
|
||||
id: node.id,
|
||||
name: this.i18n.__(node.name),
|
||||
type: node.type,
|
||||
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
children: accounts,
|
||||
total: this.getTotalAmountMeta(total),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Compose account schema node to report node.
|
||||
* @param {IBalanceSheetSchemaAccountNode} node
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
private reportSchemaAccountsNodeComposer = (
|
||||
node: IBalanceSheetSchemaAccountNode
|
||||
): IBalanceSheetAccountNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.previousYearAggregateNodeComposer
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.previousPeriodAggregateNodeComposer
|
||||
),
|
||||
R.when(
|
||||
this.query.isDatePeriodsColumnsType,
|
||||
this.assocAccountsNodeDatePeriods
|
||||
),
|
||||
this.reportSchemaAccountsNodeMapper
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the given report schema node.
|
||||
* @param {IBalanceSheetSchemaNode} node - Schema node.
|
||||
* @return {IBalanceSheetDataNode}
|
||||
*/
|
||||
private reportSchemaNodeMapper = (
|
||||
schemaNode: IBalanceSheetSchemaNode
|
||||
): IBalanceSheetDataNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE),
|
||||
this.schemaAggregateNodeCompose
|
||||
),
|
||||
R.when(
|
||||
this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS),
|
||||
this.reportSchemaAccountsNodeComposer
|
||||
)
|
||||
)(schemaNode);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the report schema nodes.
|
||||
* @param {IBalanceSheetSchemaNode[]} nodes -
|
||||
* @return {IBalanceSheetStructureSection[]}
|
||||
*/
|
||||
private reportSchemaAccountNodesMapper = (
|
||||
schemaNodes: IBalanceSheetSchemaNode[]
|
||||
): IBalanceSheetDataNode[] => {
|
||||
return this.mapNodesDeepReverse(schemaNodes, this.reportSchemaNodeMapper);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets total amount that calculated from node children.
|
||||
* @param {IBalanceSheetSection} node
|
||||
* @returns {IBalanceSheetDataNode}
|
||||
*/
|
||||
private aggregateNodeTotalMapper = (
|
||||
node: IBalanceSheetDataNode
|
||||
): IBalanceSheetDataNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.previousYearAggregateNodeComposer
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.previousPeriodAggregateNodeComposer
|
||||
),
|
||||
R.when(
|
||||
this.query.isDatePeriodsColumnsType,
|
||||
this.assocAggregateNodeDatePeriods
|
||||
)
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the report statement data.
|
||||
* @returns {IBalanceSheetDataNode[]}
|
||||
*/
|
||||
public reportData = () => {
|
||||
const balanceSheetSchema = this.getSchema();
|
||||
|
||||
return R.compose(
|
||||
this.reportFilterPlugin,
|
||||
this.reportPercentageCompose,
|
||||
this.reportSchemaAccountNodesMapper
|
||||
)(balanceSheetSchema);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import * as R from 'ramda';
|
||||
import { IBalanceSheetDataNode, IBalanceSheetSchemaNode } from '@/interfaces';
|
||||
|
||||
export const BalanceSheetBase = (Base) =>
|
||||
class extends Base {
|
||||
/**
|
||||
* Detarmines the node type of the given schema node.
|
||||
* @param {IBalanceSheetStructureSection} node -
|
||||
* @param {string} type -
|
||||
* @return {boolean}
|
||||
*/
|
||||
protected isSchemaNodeType = R.curry(
|
||||
(type: string, node: IBalanceSheetSchemaNode): boolean => {
|
||||
return node.type === type;
|
||||
}
|
||||
);
|
||||
|
||||
isNodeType = R.curry(
|
||||
(type: string, node: IBalanceSheetDataNode): boolean => {
|
||||
return node.nodeType === type;
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Detarmines the given display columns by type.
|
||||
* @param {string} displayColumnsBy
|
||||
* @returns {boolean}
|
||||
*/
|
||||
protected isDisplayColumnsBy = (displayColumnsBy: string): boolean => {
|
||||
return this.query.displayColumnsType === displayColumnsBy;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,267 @@
|
||||
import * as R from 'ramda';
|
||||
import { sumBy } from 'lodash';
|
||||
import {
|
||||
IBalanceSheetAccountNode,
|
||||
IBalanceSheetDataNode,
|
||||
IBalanceSheetAggregateNode,
|
||||
IBalanceSheetTotal,
|
||||
IBalanceSheetCommonNode,
|
||||
} from '@/interfaces';
|
||||
import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod';
|
||||
import { FinancialHorizTotals } from '../FinancialHorizTotals';
|
||||
|
||||
export const BalanceSheetComparsionPreviousPeriod = (Base: any) =>
|
||||
class
|
||||
extends R.compose(FinancialPreviousPeriod, FinancialHorizTotals)(Base)
|
||||
implements IBalanceSheetComparsions
|
||||
{
|
||||
// ------------------------------
|
||||
// # Account
|
||||
// ------------------------------
|
||||
/**
|
||||
* Associates the previous period to account node.
|
||||
* @param {IBalanceSheetDataNode} node
|
||||
* @returns {IBalanceSheetDataNode}
|
||||
*/
|
||||
protected assocPreviousPeriodAccountNode = (
|
||||
node: IBalanceSheetDataNode
|
||||
): IBalanceSheetDataNode => {
|
||||
const total = this.repository.PPTotalAccountsLedger.whereAccountId(
|
||||
node.id
|
||||
).getClosingBalance();
|
||||
|
||||
return R.assoc('previousPeriod', this.getAmountMeta(total), node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Previous period account node composer.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
protected previousPeriodAccountNodeComposer = (
|
||||
node: IBalanceSheetAccountNode
|
||||
): IBalanceSheetAccountNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizTotals,
|
||||
this.assocPreivousPeriodAccountHorizNodeComposer
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
this.assocPreviousPeriodPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
this.assocPreviousPeriodChangeNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.assocPreviousPeriodAccountNode
|
||||
)
|
||||
)(node);
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
// # Aggregate
|
||||
// ------------------------------
|
||||
/**
|
||||
* Assoc previous period total to aggregate node.
|
||||
* @param {IBalanceSheetAggregateNode} node
|
||||
* @returns {IBalanceSheetAggregateNode}
|
||||
*/
|
||||
protected assocPreviousPeriodAggregateNode = (
|
||||
node: IBalanceSheetAggregateNode
|
||||
): IBalanceSheetAggregateNode => {
|
||||
const total = sumBy(node.children, 'previousYear.amount');
|
||||
|
||||
return R.assoc('previousPeriod', this.getTotalAmountMeta(total), node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Previous period aggregate node composer.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
protected previousPeriodAggregateNodeComposer = (
|
||||
node: IBalanceSheetAccountNode
|
||||
): IBalanceSheetAccountNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizTotals,
|
||||
this.assocPreviousPeriodAggregateHorizNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
this.assocPreviousPeriodTotalPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
this.assocPreviousPeriodTotalChangeNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.assocPreviousPeriodAggregateNode
|
||||
)
|
||||
)(node);
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
// # Horizontal Nodes - Account.
|
||||
// ------------------------------
|
||||
/**
|
||||
* Retrieve the given account total in the given period.
|
||||
* @param {number} accountId - Account id.
|
||||
* @param {Date} fromDate - From date.
|
||||
* @param {Date} toDate - To date.
|
||||
* @returns {number}
|
||||
*/
|
||||
private getAccountPPDatePeriodTotal = R.curry(
|
||||
(accountId: number, fromDate: Date, toDate: Date): number => {
|
||||
const PPPeriodsTotal =
|
||||
this.repository.PPPeriodsAccountsLedger.whereAccountId(accountId)
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
const PPPeriodsOpeningTotal =
|
||||
this.repository.PPPeriodsOpeningAccountLedger.whereAccountId(
|
||||
accountId
|
||||
).getClosingBalance();
|
||||
|
||||
return PPPeriodsOpeningTotal + PPPeriodsTotal;
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Assoc preivous period to account horizontal total node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {}
|
||||
*/
|
||||
private assocPreviousPeriodAccountHorizTotal = R.curry(
|
||||
(node: IBalanceSheetAccountNode, totalNode) => {
|
||||
const total = this.getAccountPPDatePeriodTotal(
|
||||
node.id,
|
||||
totalNode.previousPeriodFromDate.date,
|
||||
totalNode.previousPeriodToDate.date
|
||||
);
|
||||
return R.assoc('previousPeriod', this.getAmountMeta(total), totalNode);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Previous year account horizontal node composer.
|
||||
* @param {IBalanceSheetAccountNode} node -
|
||||
* @param {IBalanceSheetTotal}
|
||||
* @returns {IBalanceSheetTotal}
|
||||
*/
|
||||
private previousPeriodAccountHorizNodeCompose = R.curry(
|
||||
(
|
||||
node: IBalanceSheetAccountNode,
|
||||
horizontalTotalNode: IBalanceSheetTotal
|
||||
): IBalanceSheetTotal => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
this.assocPreviousPeriodPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
this.assocPreviousPeriodChangeNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.assocPreviousPeriodAccountHorizTotal(node)
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.assocPreviousPeriodHorizNodeFromToDates(
|
||||
this.query.displayColumnsBy
|
||||
)
|
||||
)
|
||||
)(horizontalTotalNode);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns
|
||||
*/
|
||||
private assocPreivousPeriodAccountHorizNodeComposer = (
|
||||
node: IBalanceSheetAccountNode
|
||||
) => {
|
||||
const horizontalTotals = R.map(
|
||||
this.previousPeriodAccountHorizNodeCompose(node),
|
||||
node.horizontalTotals
|
||||
);
|
||||
return R.assoc('horizontalTotals', horizontalTotals, node);
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
// # Horizontal Nodes - Aggregate
|
||||
// ------------------------------
|
||||
/**
|
||||
* Assoc previous year total to horizontal node.
|
||||
* @param node
|
||||
* @returns
|
||||
*/
|
||||
private assocPreviousPeriodAggregateHorizTotalNode = R.curry(
|
||||
(node, index: number, totalNode) => {
|
||||
const total = this.getPPHorizNodesTotalSumation(index, node);
|
||||
|
||||
return R.assoc(
|
||||
'previousPeriod',
|
||||
this.getTotalAmountMeta(total),
|
||||
totalNode
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Compose previous period to aggregate horizontal nodes.
|
||||
* @param {IBalanceSheetTotal} node
|
||||
* @returns {IBalanceSheetTotal}
|
||||
*/
|
||||
private previousPeriodAggregateHorizNodeComposer = R.curry(
|
||||
(
|
||||
node: IBalanceSheetCommonNode,
|
||||
horiontalTotalNode: IBalanceSheetTotal,
|
||||
index: number
|
||||
): IBalanceSheetTotal => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
this.assocPreviousPeriodTotalPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
this.assocPreviousPeriodTotalChangeNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.assocPreviousPeriodAggregateHorizTotalNode(node, index)
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.assocPreviousPeriodHorizNodeFromToDates(
|
||||
this.query.displayColumnsBy
|
||||
)
|
||||
)
|
||||
)(horiontalTotalNode);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Assoc
|
||||
* @param {IBalanceSheetCommonNode} node
|
||||
* @returns {IBalanceSheetCommonNode}
|
||||
*/
|
||||
private assocPreviousPeriodAggregateHorizNode = (
|
||||
node: IBalanceSheetCommonNode
|
||||
) => {
|
||||
const horizontalTotals = R.addIndex(R.map)(
|
||||
this.previousPeriodAggregateHorizNodeComposer(node),
|
||||
node.horizontalTotals
|
||||
);
|
||||
return R.assoc('horizontalTotals', horizontalTotals, node);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,269 @@
|
||||
import * as R from 'ramda';
|
||||
import { sumBy, isEmpty } from 'lodash';
|
||||
import {
|
||||
IBalanceSheetAccountNode,
|
||||
IBalanceSheetCommonNode,
|
||||
IBalanceSheetDataNode,
|
||||
IBalanceSheetTotal,
|
||||
ITableColumn,
|
||||
} from '@/interfaces';
|
||||
import { FinancialPreviousYear } from '../FinancialPreviousYear';
|
||||
|
||||
export const BalanceSheetComparsionPreviousYear = (Base: any) =>
|
||||
class
|
||||
extends R.compose(FinancialPreviousYear)(Base)
|
||||
implements IBalanceSheetComparsions
|
||||
{
|
||||
// ------------------------------
|
||||
// # Account
|
||||
// ------------------------------
|
||||
/**
|
||||
* Associates the previous year to account node.
|
||||
* @param {IBalanceSheetDataNode} node
|
||||
* @returns {IBalanceSheetDataNode}
|
||||
*/
|
||||
protected assocPreviousYearAccountNode = (
|
||||
node: IBalanceSheetDataNode
|
||||
): IBalanceSheetDataNode => {
|
||||
const closingBalance =
|
||||
this.repository.PYTotalAccountsLedger.whereAccountId(
|
||||
node.id
|
||||
).getClosingBalance();
|
||||
|
||||
return R.assoc('previousYear', this.getAmountMeta(closingBalance), node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Assoc previous year attributes to account node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
protected previousYearAccountNodeComposer = (
|
||||
node: IBalanceSheetAccountNode
|
||||
): IBalanceSheetAccountNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizontalTotals,
|
||||
this.assocPreviousYearAccountHorizNodeComposer
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
this.assocPreviousYearPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
this.assocPreviousYearChangetNode
|
||||
),
|
||||
this.assocPreviousYearAccountNode
|
||||
)(node);
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
// # Aggregate
|
||||
// ------------------------------
|
||||
/**
|
||||
* Assoc previous year on aggregate node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
protected assocPreviousYearAggregateNode = (
|
||||
node: IBalanceSheetAccountNode
|
||||
): IBalanceSheetAccountNode => {
|
||||
const total = sumBy(node.children, 'previousYear.amount');
|
||||
|
||||
return R.assoc('previousYear', this.getTotalAmountMeta(total), node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Assoc previous year attributes to aggregate node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
protected previousYearAggregateNodeComposer = (
|
||||
node: IBalanceSheetAccountNode
|
||||
): IBalanceSheetAccountNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
this.assocPreviousYearTotalPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
this.assocPreviousYearTotalChangeNode
|
||||
),
|
||||
R.when(
|
||||
this.isNodeHasHorizontalTotals,
|
||||
this.assocPreviousYearAggregateHorizNode
|
||||
),
|
||||
this.assocPreviousYearAggregateNode
|
||||
)(node);
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
// # Horizontal Nodes - Aggregate
|
||||
// ------------------------------
|
||||
/**
|
||||
* Assoc previous year total to horizontal node.
|
||||
* @param node
|
||||
* @returns
|
||||
*/
|
||||
private assocPreviousYearAggregateHorizTotalNode = R.curry(
|
||||
(node, index, totalNode) => {
|
||||
const total = this.getPYHorizNodesTotalSumation(index, node);
|
||||
|
||||
return R.assoc(
|
||||
'previousYear',
|
||||
this.getTotalAmountMeta(total),
|
||||
totalNode
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Compose previous year to aggregate horizontal nodes.
|
||||
* @param {IBalanceSheetTotal} node
|
||||
* @returns {IBalanceSheetTotal}
|
||||
*/
|
||||
private previousYearAggregateHorizNodeComposer = R.curry(
|
||||
(
|
||||
node: IBalanceSheetCommonNode,
|
||||
horiontalTotalNode: IBalanceSheetTotal,
|
||||
index: number
|
||||
): IBalanceSheetTotal => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
this.assocPreviousYearTotalPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
this.assocPreviousYearTotalChangeNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.assocPreviousYearAggregateHorizTotalNode(node, index)
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.assocPreviousYearHorizNodeFromToDates
|
||||
)
|
||||
)(horiontalTotalNode);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Assoc
|
||||
* @param {IBalanceSheetCommonNode} node
|
||||
* @returns {IBalanceSheetCommonNode}
|
||||
*/
|
||||
private assocPreviousYearAggregateHorizNode = (
|
||||
node: IBalanceSheetCommonNode
|
||||
) => {
|
||||
const horizontalTotals = R.addIndex(R.map)(
|
||||
this.previousYearAggregateHorizNodeComposer(node),
|
||||
node.horizontalTotals
|
||||
);
|
||||
return R.assoc('horizontalTotals', horizontalTotals, node);
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
// # Horizontal Nodes - Account.
|
||||
// ------------------------------
|
||||
/**
|
||||
* Retrieve the given account total in the given period.
|
||||
* @param {number} accountId - Account id.
|
||||
* @param {Date} fromDate - From date.
|
||||
* @param {Date} toDate - To date.
|
||||
* @returns {number}
|
||||
*/
|
||||
private getAccountPYDatePeriodTotal = R.curry(
|
||||
(accountId: number, fromDate: Date, toDate: Date): number => {
|
||||
const PYPeriodsTotal =
|
||||
this.repository.PYPeriodsAccountsLedger.whereAccountId(accountId)
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
const PYPeriodsOpeningTotal =
|
||||
this.repository.PYPeriodsOpeningAccountLedger.whereAccountId(
|
||||
accountId
|
||||
).getClosingBalance();
|
||||
|
||||
return PYPeriodsOpeningTotal + PYPeriodsTotal;
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Assoc preivous year to account horizontal total node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {}
|
||||
*/
|
||||
private assocPreviousYearAccountHorizTotal = R.curry(
|
||||
(node: IBalanceSheetAccountNode, totalNode) => {
|
||||
const total = this.getAccountPYDatePeriodTotal(
|
||||
node.id,
|
||||
totalNode.previousYearFromDate.date,
|
||||
totalNode.previousYearToDate.date
|
||||
);
|
||||
return R.assoc('previousYear', this.getAmountMeta(total), totalNode);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Previous year account horizontal node composer.
|
||||
* @param {IBalanceSheetAccountNode} node -
|
||||
* @param {IBalanceSheetTotal}
|
||||
* @returns {IBalanceSheetTotal}
|
||||
*/
|
||||
private previousYearAccountHorizNodeCompose = R.curry(
|
||||
(
|
||||
node: IBalanceSheetAccountNode,
|
||||
horizontalTotalNode: IBalanceSheetTotal
|
||||
): IBalanceSheetTotal => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
this.assocPreviousYearPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
this.assocPreviousYearChangetNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.assocPreviousYearAccountHorizTotal(node)
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.assocPreviousYearHorizNodeFromToDates
|
||||
)
|
||||
)(horizontalTotalNode);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Assoc previous year horizontal nodes to account node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
private assocPreviousYearAccountHorizNodeComposer = (
|
||||
node: IBalanceSheetAccountNode
|
||||
) => {
|
||||
const horizontalTotals = R.map(
|
||||
this.previousYearAccountHorizNodeCompose(node),
|
||||
node.horizontalTotals
|
||||
);
|
||||
return R.assoc('horizontalTotals', horizontalTotals, node);
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
// # Horizontal Nodes - Aggregate.
|
||||
// ------------------------------
|
||||
|
||||
/**
|
||||
* Detarmines whether the given node has horizontal totals.
|
||||
* @param {IBalanceSheetCommonNode} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private isNodeHasHorizontalTotals = (node: IBalanceSheetCommonNode) =>
|
||||
!isEmpty(node.horizontalTotals);
|
||||
};
|
||||
@@ -0,0 +1,211 @@
|
||||
import * as R from 'ramda';
|
||||
import { sumBy } from 'lodash';
|
||||
import {
|
||||
IBalanceSheetQuery,
|
||||
IFormatNumberSettings,
|
||||
IBalanceSheetDatePeriods,
|
||||
IBalanceSheetAccountNode,
|
||||
IBalanceSheetTotalPeriod,
|
||||
IDateRange,
|
||||
IBalanceSheetCommonNode,
|
||||
} from '@/interfaces';
|
||||
import FinancialSheet from '../FinancialSheet';
|
||||
import { FinancialDatePeriods } from '../FinancialDatePeriods';
|
||||
|
||||
/**
|
||||
* Balance sheet date periods.
|
||||
*/
|
||||
export const BalanceSheetDatePeriods = (Base: FinancialSheet) =>
|
||||
class
|
||||
extends R.compose(FinancialDatePeriods)(Base)
|
||||
implements IBalanceSheetDatePeriods
|
||||
{
|
||||
/**
|
||||
* @param {IBalanceSheetQuery}
|
||||
*/
|
||||
readonly query: IBalanceSheetQuery;
|
||||
|
||||
/**
|
||||
* Retrieves the date periods based on the report query.
|
||||
* @returns {IDateRange[]}
|
||||
*/
|
||||
get datePeriods(): IDateRange[] {
|
||||
return this.getDateRanges(
|
||||
this.query.fromDate,
|
||||
this.query.toDate,
|
||||
this.query.displayColumnsBy
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the date periods of the given node based on the report query.
|
||||
* @param {IBalanceSheetCommonNode} node
|
||||
* @param {Function} callback
|
||||
* @returns {}
|
||||
*/
|
||||
protected getReportNodeDatePeriods = (
|
||||
node: IBalanceSheetCommonNode,
|
||||
callback: (
|
||||
node: IBalanceSheetCommonNode,
|
||||
fromDate: Date,
|
||||
toDate: Date,
|
||||
index: number
|
||||
) => any
|
||||
) => {
|
||||
return this.getNodeDatePeriods(
|
||||
this.query.fromDate,
|
||||
this.query.toDate,
|
||||
this.query.displayColumnsBy,
|
||||
node,
|
||||
callback
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the date period meta.
|
||||
* @param {number} total - Total amount.
|
||||
* @param {Date} fromDate - From date.
|
||||
* @param {Date} toDate - To date.
|
||||
* @return {ICashFlowDatePeriod}
|
||||
*/
|
||||
private getDatePeriodTotalMeta = (
|
||||
total: number,
|
||||
fromDate: Date,
|
||||
toDate: Date,
|
||||
overrideSettings: IFormatNumberSettings = {}
|
||||
): IBalanceSheetTotalPeriod => {
|
||||
return this.getDatePeriodMeta(total, fromDate, toDate, {
|
||||
money: true,
|
||||
...overrideSettings,
|
||||
});
|
||||
};
|
||||
|
||||
// --------------------------------
|
||||
// # Account
|
||||
// --------------------------------
|
||||
/**
|
||||
* Retrieve the given account date period total.
|
||||
* @param {number} accountId
|
||||
* @param {Date} toDate
|
||||
* @returns {number}
|
||||
*/
|
||||
private getAccountDatePeriodTotal = (
|
||||
accountId: number,
|
||||
toDate: Date
|
||||
): number => {
|
||||
const periodTotalBetween = this.repository.periodsAccountsLedger
|
||||
.whereAccountId(accountId)
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
const periodOpening = this.repository.periodsOpeningAccountLedger
|
||||
.whereAccountId(accountId)
|
||||
.getClosingBalance();
|
||||
|
||||
return periodOpening + periodTotalBetween;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @param {Date} fromDate
|
||||
* @param {Date} toDate
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
private getAccountNodeDatePeriod = (
|
||||
node: IBalanceSheetAccountNode,
|
||||
fromDate: Date,
|
||||
toDate: Date
|
||||
): IBalanceSheetTotalPeriod => {
|
||||
const periodTotal = this.getAccountDatePeriodTotal(node.id, toDate);
|
||||
|
||||
return this.getDatePeriodTotalMeta(periodTotal, fromDate, toDate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve total date periods of the given account node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
private getAccountsNodeDatePeriods = (
|
||||
node: IBalanceSheetAccountNode
|
||||
): IBalanceSheetTotalPeriod[] => {
|
||||
return this.getReportNodeDatePeriods(node, this.getAccountNodeDatePeriod);
|
||||
};
|
||||
|
||||
/**
|
||||
* Assoc total date periods to account node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
public assocAccountNodeDatePeriods = (
|
||||
node: IBalanceSheetAccountNode
|
||||
): IBalanceSheetAccountNode => {
|
||||
const datePeriods = this.getAccountsNodeDatePeriods(node);
|
||||
|
||||
return R.assoc('horizontalTotals', datePeriods, node);
|
||||
};
|
||||
|
||||
// --------------------------------
|
||||
// # Aggregate
|
||||
// --------------------------------
|
||||
/**
|
||||
*
|
||||
* @param {} node
|
||||
* @param {number} index
|
||||
* @returns {number}
|
||||
*/
|
||||
private getAggregateDatePeriodIndexTotal = (node, index) => {
|
||||
return sumBy(node.children, `horizontalTotals[${index}].total.amount`);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @param {Date} fromDate
|
||||
* @param {Date} toDate
|
||||
* @returns
|
||||
*/
|
||||
public getAggregateNodeDatePeriod = (
|
||||
node: IBalanceSheetAccountNode,
|
||||
fromDate: Date,
|
||||
toDate: Date,
|
||||
index: number
|
||||
) => {
|
||||
const periodTotal = this.getAggregateDatePeriodIndexTotal(node, index);
|
||||
|
||||
return this.getDatePeriodTotalMeta(periodTotal, fromDate, toDate);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* @returns
|
||||
*/
|
||||
public getAggregateNodeDatePeriods = (node) => {
|
||||
return this.getReportNodeDatePeriods(
|
||||
node,
|
||||
this.getAggregateNodeDatePeriod
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Assoc total date periods to aggregate node.
|
||||
* @param node
|
||||
* @returns {}
|
||||
*/
|
||||
public assocAggregateNodeDatePeriods = (node) => {
|
||||
const datePeriods = this.getAggregateNodeDatePeriods(node);
|
||||
|
||||
return R.assoc('horizontalTotals', datePeriods, node);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* @returns
|
||||
*/
|
||||
public assocAccountsNodeDatePeriods = (node) => {
|
||||
return this.assocAggregateNodeDatePeriods(node);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
import * as R from 'ramda';
|
||||
import { get } from 'lodash';
|
||||
import {
|
||||
IBalanceSheetDataNode,
|
||||
BALANCE_SHEET_NODE_TYPE,
|
||||
} from '../../../interfaces';
|
||||
import { FinancialFilter } from '../FinancialFilter';
|
||||
|
||||
export const BalanceSheetFiltering = (Base) =>
|
||||
class extends R.compose(FinancialFilter)(Base) {
|
||||
// -----------------------
|
||||
// # Account
|
||||
// -----------------------
|
||||
/**
|
||||
* Filter report node detarmine.
|
||||
* @param {IBalanceSheetDataNode} node - Balance sheet node.
|
||||
* @return {boolean}
|
||||
*/
|
||||
private accountNoneZeroNodesFilterDetarminer = (
|
||||
node: IBalanceSheetDataNode
|
||||
): boolean => {
|
||||
return R.ifElse(
|
||||
this.isNodeType(BALANCE_SHEET_NODE_TYPE.ACCOUNT),
|
||||
this.isNodeNoneZero,
|
||||
R.always(true)
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines account none-transactions node.
|
||||
* @param {IBalanceSheetDataNode} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private accountNoneTransFilterDetarminer = (
|
||||
node: IBalanceSheetDataNode
|
||||
): boolean => {
|
||||
return R.ifElse(
|
||||
this.isNodeType(BALANCE_SHEET_NODE_TYPE.ACCOUNT),
|
||||
this.isNodeNoneZero,
|
||||
R.always(true)
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Report nodes filter.
|
||||
* @param {IBalanceSheetSection[]} nodes -
|
||||
* @return {IBalanceSheetSection[]}
|
||||
*/
|
||||
private accountsNoneZeroNodesFilter = (
|
||||
nodes: IBalanceSheetDataNode[]
|
||||
): IBalanceSheetDataNode[] => {
|
||||
return this.filterNodesDeep(
|
||||
nodes,
|
||||
this.accountNoneZeroNodesFilterDetarminer
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Filters the accounts none-transactions nodes.
|
||||
* @param {IBalanceSheetDataNode[]} nodes
|
||||
* @returns {IBalanceSheetDataNode[]}
|
||||
*/
|
||||
private accountsNoneTransactionsNodesFilter = (
|
||||
nodes: IBalanceSheetDataNode[]
|
||||
) => {
|
||||
return this.filterNodesDeep(nodes, this.accountNoneTransFilterDetarminer);
|
||||
};
|
||||
|
||||
// -----------------------
|
||||
// # Aggregate/Accounts.
|
||||
// -----------------------
|
||||
/**
|
||||
* Detearmines aggregate none-children filtering.
|
||||
* @param {IBalanceSheetDataNode} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private aggregateNoneChildrenFilterDetarminer = (
|
||||
node: IBalanceSheetDataNode
|
||||
): boolean => {
|
||||
// Detarmines whether the given node is aggregate or accounts node.
|
||||
const isAggregateOrAccounts =
|
||||
this.isNodeType(BALANCE_SHEET_NODE_TYPE.AGGREGATE, node) ||
|
||||
this.isNodeType(BALANCE_SHEET_NODE_TYPE.ACCOUNTS, node);
|
||||
|
||||
// Retrieve the schema node of the given id.
|
||||
const schemaNode = this.getSchemaNodeById(node.id);
|
||||
|
||||
// Detarmines if the schema node is always should show.
|
||||
const isSchemaAlwaysShow = get(schemaNode, 'alwaysShow', false);
|
||||
|
||||
return isAggregateOrAccounts && !isSchemaAlwaysShow
|
||||
? this.isNodeHasChildren(node)
|
||||
: true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Filters aggregate none-children nodes.
|
||||
* @param {IBalanceSheetDataNode[]} nodes
|
||||
* @returns {IBalanceSheetDataNode[]}
|
||||
*/
|
||||
private aggregateNoneChildrenFilter = (
|
||||
nodes: IBalanceSheetDataNode[]
|
||||
): IBalanceSheetDataNode[] => {
|
||||
return this.filterNodesDeep2(
|
||||
this.aggregateNoneChildrenFilterDetarminer,
|
||||
nodes
|
||||
);
|
||||
};
|
||||
|
||||
// -----------------------
|
||||
// # Composers.
|
||||
// -----------------------
|
||||
/**
|
||||
* Filters none-zero nodes.
|
||||
* @param {IBalanceSheetDataNode[]} nodes
|
||||
* @returns {IBalanceSheetDataNode[]}
|
||||
*/
|
||||
private filterNoneZeroNodesCompose = (
|
||||
nodes: IBalanceSheetDataNode[]
|
||||
): IBalanceSheetDataNode[] => {
|
||||
return R.compose(
|
||||
this.aggregateNoneChildrenFilter,
|
||||
this.accountsNoneZeroNodesFilter
|
||||
)(nodes);
|
||||
};
|
||||
|
||||
/**
|
||||
* Filters none-transactions nodes.
|
||||
* @param {IBalanceSheetDataNode[]} nodes
|
||||
* @returns {IBalanceSheetDataNode[]}
|
||||
*/
|
||||
private filterNoneTransNodesCompose = (
|
||||
nodes: IBalanceSheetDataNode[]
|
||||
): IBalanceSheetDataNode[] => {
|
||||
return R.compose(
|
||||
this.aggregateNoneChildrenFilter,
|
||||
this.accountsNoneTransactionsNodesFilter
|
||||
)(nodes);
|
||||
};
|
||||
|
||||
/**
|
||||
* Supress nodes when accounts transactions ledger is empty.
|
||||
* @param {IBalanceSheetDataNode[]} nodes
|
||||
* @returns {IBalanceSheetDataNode[]}
|
||||
*/
|
||||
private supressNodesWhenAccountsTransactionsEmpty = (
|
||||
nodes: IBalanceSheetDataNode[]
|
||||
): IBalanceSheetDataNode[] => {
|
||||
return this.repository.totalAccountsLedger.isEmpty() ? [] : nodes;
|
||||
};
|
||||
|
||||
/**
|
||||
* Compose report nodes filtering.
|
||||
* @param {IBalanceSheetDataNode[]} nodes
|
||||
* @returns {IBalanceSheetDataNode[]}
|
||||
*/
|
||||
protected reportFilterPlugin = (nodes: IBalanceSheetDataNode[]) => {
|
||||
return R.compose(
|
||||
this.supressNodesWhenAccountsTransactionsEmpty,
|
||||
R.when(R.always(this.query.noneZero), this.filterNoneZeroNodesCompose),
|
||||
R.when(
|
||||
R.always(this.query.noneTransactions),
|
||||
this.filterNoneTransNodesCompose
|
||||
)
|
||||
)(nodes);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,225 @@
|
||||
import * as R from 'ramda';
|
||||
import { get } from 'lodash';
|
||||
import { IBalanceSheetDataNode } from '@/interfaces';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
|
||||
export const BalanceSheetPercentage = (Base: any) =>
|
||||
class extends Base {
|
||||
readonly query: BalanceSheetQuery;
|
||||
|
||||
/**
|
||||
* Assoc percentage of column to report node.
|
||||
* @param {IBalanceSheetDataNode} node
|
||||
* @returns {IBalanceSheetDataNode}
|
||||
*/
|
||||
protected assocReportNodeColumnPercentage = R.curry(
|
||||
(
|
||||
parentTotal: number,
|
||||
node: IBalanceSheetDataNode
|
||||
): IBalanceSheetDataNode => {
|
||||
const percentage = this.getPercentageBasis(
|
||||
parentTotal,
|
||||
node.total.amount
|
||||
);
|
||||
return R.assoc(
|
||||
'percentageColumn',
|
||||
this.getPercentageAmountMeta(percentage),
|
||||
node
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Assoc percentage of row to report node.
|
||||
* @param {IBalanceSheetDataNode} node
|
||||
* @returns {IBalanceSheetDataNode}
|
||||
*/
|
||||
protected assocReportNodeRowPercentage = R.curry(
|
||||
(
|
||||
parentTotal: number,
|
||||
node: IBalanceSheetDataNode
|
||||
): IBalanceSheetDataNode => {
|
||||
const percenatage = this.getPercentageBasis(
|
||||
parentTotal,
|
||||
node.total.amount
|
||||
);
|
||||
return R.assoc(
|
||||
'percentageRow',
|
||||
this.getPercentageAmountMeta(percenatage),
|
||||
node
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Assoc percentage of row to horizontal total.
|
||||
* @param {number} parentTotal -
|
||||
* @param {IBalanceSheetDataNode} node
|
||||
* @returns {IBalanceSheetDataNode}
|
||||
*/
|
||||
protected assocRowPercentageHorizTotals = R.curry(
|
||||
(
|
||||
parentTotal: number,
|
||||
node: IBalanceSheetDataNode
|
||||
): IBalanceSheetDataNode => {
|
||||
const assocRowPercen = this.assocReportNodeRowPercentage(parentTotal);
|
||||
const horTotals = R.map(assocRowPercen)(node.horizontalTotals);
|
||||
|
||||
return R.assoc('horizontalTotals', horTotals, node);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {} parentNode -
|
||||
* @param {} horTotalNode -
|
||||
* @param {number} index -
|
||||
*/
|
||||
private assocColumnPercentageHorizTotal = R.curry(
|
||||
(parentNode, horTotalNode, index) => {
|
||||
const parentTotal = get(
|
||||
parentNode,
|
||||
`horizontalTotals[${index}].total.amount`,
|
||||
0
|
||||
);
|
||||
return this.assocReportNodeColumnPercentage(parentTotal, horTotalNode);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Assoc column percentage to horizontal totals nodes.
|
||||
* @param {IBalanceSheetDataNode} node
|
||||
* @returns {IBalanceSheetDataNode}
|
||||
*/
|
||||
protected assocColumnPercentageHorizTotals = R.curry(
|
||||
(
|
||||
parentNode: IBalanceSheetDataNode,
|
||||
node: IBalanceSheetDataNode
|
||||
): IBalanceSheetDataNode => {
|
||||
// Horizontal totals.
|
||||
const assocColPerc = this.assocColumnPercentageHorizTotal(parentNode);
|
||||
const horTotals = R.addIndex(R.map)(assocColPerc)(
|
||||
node.horizontalTotals
|
||||
);
|
||||
return R.assoc('horizontalTotals', horTotals, node);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} parentTotal -
|
||||
* @param {} node
|
||||
* @returns
|
||||
*/
|
||||
protected reportNodeColumnPercentageComposer = R.curry(
|
||||
(parentNode, node) => {
|
||||
const parentTotal = parentNode.total.amount;
|
||||
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizoTotals,
|
||||
this.assocColumnPercentageHorizTotals(parentNode)
|
||||
),
|
||||
this.assocReportNodeColumnPercentage(parentTotal)
|
||||
)(node);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* @returns
|
||||
*/
|
||||
private reportNodeRowPercentageComposer = (node) => {
|
||||
const total = node.total.amount;
|
||||
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizoTotals,
|
||||
this.assocRowPercentageHorizTotals(total)
|
||||
),
|
||||
this.assocReportNodeRowPercentage(total)
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private assocNodeColumnPercentageChildren = (node) => {
|
||||
const children = this.mapNodesDeep(
|
||||
node.children,
|
||||
this.reportNodeColumnPercentageComposer(node)
|
||||
);
|
||||
return R.assoc('children', children, node);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* @returns
|
||||
*/
|
||||
private reportNodeColumnPercentageDeepMap = (node) => {
|
||||
const parentTotal = node.total.amount;
|
||||
const parentNode = node;
|
||||
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizoTotals,
|
||||
this.assocColumnPercentageHorizTotals(parentNode)
|
||||
),
|
||||
this.assocReportNodeColumnPercentage(parentTotal),
|
||||
this.assocNodeColumnPercentageChildren
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IBalanceSheetDataNode[]} node
|
||||
* @returns {IBalanceSheetDataNode[]}
|
||||
*/
|
||||
private reportColumnsPercentageMapper = (
|
||||
nodes: IBalanceSheetDataNode[]
|
||||
): IBalanceSheetDataNode[] => {
|
||||
return R.map(this.reportNodeColumnPercentageDeepMap, nodes);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param nodes
|
||||
* @returns
|
||||
*/
|
||||
private reportRowsPercentageMapper = (nodes) => {
|
||||
return this.mapNodesDeep(nodes, this.reportNodeRowPercentageComposer);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param nodes
|
||||
* @returns
|
||||
*/
|
||||
protected reportPercentageCompose = (nodes) => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isColumnsPercentageActive,
|
||||
this.reportColumnsPercentageMapper
|
||||
),
|
||||
R.when(
|
||||
this.query.isRowsPercentageActive,
|
||||
this.reportRowsPercentageMapper
|
||||
)
|
||||
)(nodes);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines whether the given node has horizontal total.
|
||||
* @param {IBalanceSheetDataNode} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
protected isNodeHasHorizoTotals = (
|
||||
node: IBalanceSheetDataNode
|
||||
): boolean => {
|
||||
return (
|
||||
!R.isEmpty(node.horizontalTotals) && !R.isNil(node.horizontalTotals)
|
||||
);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,177 @@
|
||||
import { merge } from 'lodash';
|
||||
import * as R from 'ramda';
|
||||
import { IBalanceSheetQuery, IFinancialDatePeriodsUnit } from '@/interfaces';
|
||||
import { FinancialDateRanges } from '../FinancialDateRanges';
|
||||
import { DISPLAY_COLUMNS_BY } from './constants';
|
||||
|
||||
export class BalanceSheetQuery extends R.compose(FinancialDateRanges)(
|
||||
class {}
|
||||
) {
|
||||
/**
|
||||
* Balance sheet query.
|
||||
* @param {IBalanceSheetQuery}
|
||||
*/
|
||||
public readonly query: IBalanceSheetQuery;
|
||||
/**
|
||||
* Previous year to date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public readonly PYToDate: Date;
|
||||
/**
|
||||
* Previous year from date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public readonly PYFromDate: Date;
|
||||
/**
|
||||
* Previous period to date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public readonly PPToDate: Date;
|
||||
/**
|
||||
* Previous period from date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public readonly PPFromDate: Date;
|
||||
/**
|
||||
* Constructor method
|
||||
* @param {IBalanceSheetQuery} query
|
||||
*/
|
||||
constructor(query: IBalanceSheetQuery) {
|
||||
super();
|
||||
this.query = query;
|
||||
|
||||
// Pervious Year (PY) Dates.
|
||||
this.PYToDate = this.getPreviousYearDate(this.query.toDate);
|
||||
this.PYFromDate = this.getPreviousYearDate(this.query.fromDate);
|
||||
|
||||
// Previous Period (PP) Dates for Total column.
|
||||
if (this.isTotalColumnType()) {
|
||||
const { fromDate, toDate } = this.getPPTotalDateRange(
|
||||
this.query.fromDate,
|
||||
this.query.toDate
|
||||
);
|
||||
this.PPToDate = toDate;
|
||||
this.PPFromDate = fromDate;
|
||||
// Previous Period (PP) Dates for Date period columns type.
|
||||
} else if (this.isDatePeriodsColumnsType()) {
|
||||
const { fromDate, toDate } = this.getPPDatePeriodDateRange(
|
||||
this.query.fromDate,
|
||||
this.query.toDate,
|
||||
this.query.displayColumnsBy as IFinancialDatePeriodsUnit
|
||||
);
|
||||
this.PPToDate = toDate;
|
||||
this.PPFromDate = fromDate;
|
||||
}
|
||||
return merge(this, query);
|
||||
}
|
||||
|
||||
// ---------------------------
|
||||
// # Columns Type/By.
|
||||
// ---------------------------
|
||||
/**
|
||||
* Detarmines the given display columns type.
|
||||
* @param {string} displayColumnsBy
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isDisplayColumnsBy = (displayColumnsBy: string): boolean => {
|
||||
return this.query.displayColumnsBy === displayColumnsBy;
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines the given display columns by type.
|
||||
* @param {string} displayColumnsBy
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isDisplayColumnsType = (displayColumnsType: string): boolean => {
|
||||
return this.query.displayColumnsType === displayColumnsType;
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines whether the columns type is date periods.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isDatePeriodsColumnsType = (): boolean => {
|
||||
return this.isDisplayColumnsType(DISPLAY_COLUMNS_BY.DATE_PERIODS);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines whether the columns type is total.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isTotalColumnType = (): boolean => {
|
||||
return this.isDisplayColumnsType(DISPLAY_COLUMNS_BY.TOTAL);
|
||||
};
|
||||
|
||||
// ---------------------------
|
||||
// # Percentage column/row.
|
||||
// ---------------------------
|
||||
/**
|
||||
* Detarmines whether the percentage of column active.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isColumnsPercentageActive = (): boolean => {
|
||||
return this.query.percentageOfColumn;
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines whether the percentage of row active.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isRowsPercentageActive = (): boolean => {
|
||||
return this.query.percentageOfRow;
|
||||
};
|
||||
|
||||
// ---------------------------
|
||||
// # Previous Year (PY)
|
||||
// ---------------------------
|
||||
/**
|
||||
* Detarmines the report query has previous year enabled.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isPreviousYearActive = (): boolean => {
|
||||
return this.query.previousYear;
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines the report query has previous year percentage change active.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isPreviousYearPercentageActive = (): boolean => {
|
||||
return this.query.previousYearPercentageChange;
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines the report query has previous year change active.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isPreviousYearChangeActive = (): boolean => {
|
||||
return this.query.previousYearAmountChange;
|
||||
};
|
||||
|
||||
// ---------------------------
|
||||
// # Previous Period (PP).
|
||||
// ---------------------------
|
||||
/**
|
||||
* Detarmines the report query has previous period enabled.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isPreviousPeriodActive = (): boolean => {
|
||||
return this.query.previousPeriod;
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines wether the preivous period percentage is active.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isPreviousPeriodPercentageActive = (): boolean => {
|
||||
return this.query.previousPeriodPercentageChange;
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines wether the previous period change is active.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isPreviousPeriodChangeActive = (): boolean => {
|
||||
return this.query.previousPeriodAmountChange;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
import { Service } from 'typedi';
|
||||
import * as R from 'ramda';
|
||||
import { Knex } from 'knex';
|
||||
import { isEmpty } from 'lodash';
|
||||
import {
|
||||
IAccountTransactionsGroupBy,
|
||||
IBalanceSheetQuery,
|
||||
ILedger,
|
||||
} from '@/interfaces';
|
||||
import { transformToMapBy } from 'utils';
|
||||
import Ledger from '@/services/Accounting/Ledger';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { FinancialDatePeriods } from '../FinancialDatePeriods';
|
||||
|
||||
@Service()
|
||||
export default class BalanceSheetRepository extends R.compose(
|
||||
FinancialDatePeriods
|
||||
)(class {}) {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private readonly models;
|
||||
|
||||
/**
|
||||
* @param {number}
|
||||
*/
|
||||
public readonly tenantId: number;
|
||||
|
||||
/**
|
||||
* @param {BalanceSheetQuery}
|
||||
*/
|
||||
public readonly query: BalanceSheetQuery;
|
||||
|
||||
/**
|
||||
* @param {}
|
||||
*/
|
||||
public accounts: any;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public accountsByType: any;
|
||||
|
||||
/**
|
||||
* PY from date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public readonly PYFromDate: Date;
|
||||
|
||||
/**
|
||||
* PY to date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public readonly PYToDate: Date;
|
||||
|
||||
/**
|
||||
* PP to date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public readonly PPToDate: Date;
|
||||
|
||||
/**
|
||||
* PP from date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public readonly PPFromDate: Date;
|
||||
|
||||
public totalAccountsLedger: Ledger;
|
||||
|
||||
/**
|
||||
* Transactions group type.
|
||||
* @param {IAccountTransactionsGroupBy}
|
||||
*/
|
||||
public transactionsGroupType: IAccountTransactionsGroupBy =
|
||||
IAccountTransactionsGroupBy.Month;
|
||||
|
||||
// -----------------------
|
||||
// # Date Periods
|
||||
// -----------------------
|
||||
/**
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public periodsAccountsLedger: Ledger;
|
||||
|
||||
/**
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public periodsOpeningAccountLedger: Ledger;
|
||||
|
||||
// -----------------------
|
||||
// # Previous Year (PY).
|
||||
// -----------------------
|
||||
/**
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public PYPeriodsOpeningAccountLedger: Ledger;
|
||||
|
||||
/**
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public PYPeriodsAccountsLedger: Ledger;
|
||||
|
||||
/**
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public PYTotalAccountsLedger: ILedger;
|
||||
|
||||
// -----------------------
|
||||
// # Previous Period (PP).
|
||||
// -----------------------
|
||||
/**
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public PPTotalAccountsLedger: Ledger;
|
||||
|
||||
/**
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public PPPeriodsAccountsLedger: ILedger;
|
||||
|
||||
/**
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public PPPeriodsOpeningAccountLedger: ILedger;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {number} tenantId
|
||||
* @param {IBalanceSheetQuery} query
|
||||
*/
|
||||
constructor(models: any, query: IBalanceSheetQuery) {
|
||||
super();
|
||||
|
||||
this.query = new BalanceSheetQuery(query);
|
||||
this.models = models;
|
||||
|
||||
this.transactionsGroupType = this.getGroupByFromDisplayColumnsBy(
|
||||
this.query.displayColumnsBy
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Async initialize.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public asyncInitialize = async () => {
|
||||
await this.initAccounts();
|
||||
await this.initAccountsTotalLedger();
|
||||
|
||||
// Date periods.
|
||||
if (this.query.isDatePeriodsColumnsType()) {
|
||||
await this.initTotalDatePeriods();
|
||||
}
|
||||
// Previous Year (PY).
|
||||
if (this.query.isPreviousYearActive()) {
|
||||
await this.initTotalPreviousYear();
|
||||
}
|
||||
if (
|
||||
this.query.isPreviousYearActive() &&
|
||||
this.query.isDatePeriodsColumnsType()
|
||||
) {
|
||||
await this.initPeriodsPreviousYear();
|
||||
}
|
||||
// Previous Period (PP).
|
||||
if (this.query.isPreviousPeriodActive()) {
|
||||
await this.initTotalPreviousPeriod();
|
||||
}
|
||||
if (
|
||||
this.query.isPreviousPeriodActive() &&
|
||||
this.query.isDatePeriodsColumnsType()
|
||||
) {
|
||||
await this.initPeriodsPreviousPeriod();
|
||||
}
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Accounts
|
||||
// ----------------------------
|
||||
public initAccounts = async () => {
|
||||
const accounts = await this.getAccounts();
|
||||
|
||||
this.accounts = accounts;
|
||||
this.accountsByType = transformToMapBy(accounts, 'accountType');
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Closing Total
|
||||
// ----------------------------
|
||||
/**
|
||||
* Initialize accounts closing total based on the given query.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private initAccountsTotalLedger = async (): Promise<void> => {
|
||||
const totalByAccount = await this.closingAccountsTotal(this.query.toDate);
|
||||
|
||||
// Inject to the repository.
|
||||
this.totalAccountsLedger = Ledger.fromTransactions(totalByAccount);
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Date periods.
|
||||
// ----------------------------
|
||||
/**
|
||||
* Initialize date periods total.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public initTotalDatePeriods = async (): Promise<void> => {
|
||||
// Retrieves grouped transactions by given date group.
|
||||
const periodsByAccount = await this.accountsDatePeriods(
|
||||
this.query.fromDate,
|
||||
this.query.toDate,
|
||||
this.transactionsGroupType
|
||||
);
|
||||
// Retrieves opening balance of grouped transactions.
|
||||
const periodsOpeningByAccount = await this.closingAccountsTotal(
|
||||
this.query.fromDate
|
||||
);
|
||||
// Inject to the repository.
|
||||
this.periodsAccountsLedger = Ledger.fromTransactions(periodsByAccount);
|
||||
this.periodsOpeningAccountLedger = Ledger.fromTransactions(
|
||||
periodsOpeningByAccount
|
||||
);
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Previous Year (PY).
|
||||
// ----------------------------
|
||||
/**
|
||||
* Initialize total of previous year.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private initTotalPreviousYear = async (): Promise<void> => {
|
||||
const PYTotalsByAccounts = await this.closingAccountsTotal(
|
||||
this.query.PYToDate
|
||||
);
|
||||
// Inject to the repository.
|
||||
this.PYTotalAccountsLedger = Ledger.fromTransactions(PYTotalsByAccounts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize date periods of previous year.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private initPeriodsPreviousYear = async (): Promise<void> => {
|
||||
const PYPeriodsBYAccounts = await this.accountsDatePeriods(
|
||||
this.query.PYFromDate,
|
||||
this.query.PYToDate,
|
||||
this.transactionsGroupType
|
||||
);
|
||||
// Retrieves opening balance of grouped transactions.
|
||||
const periodsOpeningByAccount = await this.closingAccountsTotal(
|
||||
this.query.PYFromDate
|
||||
);
|
||||
// Inject to the repository.
|
||||
this.PYPeriodsAccountsLedger = Ledger.fromTransactions(PYPeriodsBYAccounts);
|
||||
this.PYPeriodsOpeningAccountLedger = Ledger.fromTransactions(
|
||||
periodsOpeningByAccount
|
||||
);
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Previous Year (PP).
|
||||
// ----------------------------
|
||||
/**
|
||||
* Initialize total of previous year.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private initTotalPreviousPeriod = async (): Promise<void> => {
|
||||
const PPTotalsByAccounts = await this.closingAccountsTotal(
|
||||
this.query.PPToDate
|
||||
);
|
||||
// Inject to the repository.
|
||||
this.PPTotalAccountsLedger = Ledger.fromTransactions(PPTotalsByAccounts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize date periods of previous year.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private initPeriodsPreviousPeriod = async (): Promise<void> => {
|
||||
const PPPeriodsBYAccounts = await this.accountsDatePeriods(
|
||||
this.query.PPFromDate,
|
||||
this.query.PPToDate,
|
||||
this.transactionsGroupType
|
||||
);
|
||||
// Retrieves opening balance of grouped transactions.
|
||||
const periodsOpeningByAccount = await this.closingAccountsTotal(
|
||||
this.query.PPFromDate
|
||||
);
|
||||
// Inject to the repository.
|
||||
this.PPPeriodsAccountsLedger = Ledger.fromTransactions(PPPeriodsBYAccounts);
|
||||
this.PPPeriodsOpeningAccountLedger = Ledger.fromTransactions(
|
||||
periodsOpeningByAccount
|
||||
);
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Utils
|
||||
// ----------------------------
|
||||
/**
|
||||
* Retrieve accounts of the report.
|
||||
* @return {Promise<IAccount[]>}
|
||||
*/
|
||||
private getAccounts = () => {
|
||||
const { Account } = this.models;
|
||||
|
||||
return Account.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);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the opening balance transactions of the report.
|
||||
*/
|
||||
public closingAccountsTotal = async (openingDate: Date | string) => {
|
||||
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', null, openingDate);
|
||||
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);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
BALANCE_SHEET_SCHEMA_NODE_ID,
|
||||
BALANCE_SHEET_SCHEMA_NODE_TYPE,
|
||||
} from '@/interfaces';
|
||||
import { ACCOUNT_TYPE } from '@/data/AccountTypes';
|
||||
import { FinancialSchema } from '../FinancialSchema';
|
||||
|
||||
|
||||
export const BalanceSheetSchema = (Base) =>
|
||||
class extends R.compose(FinancialSchema)(Base) {
|
||||
/**
|
||||
* Retrieves the balance sheet schema.
|
||||
* @returns
|
||||
*/
|
||||
getSchema = () => {
|
||||
return getBalanceSheetSchema();
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the balance sheet report schema.
|
||||
*/
|
||||
export const getBalanceSheetSchema = () => [
|
||||
{
|
||||
name: 'balance_sheet.assets',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.ASSETS,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE,
|
||||
children: [
|
||||
{
|
||||
name: 'balance_sheet.current_asset',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.CURRENT_ASSETS,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE,
|
||||
children: [
|
||||
{
|
||||
name: 'balance_sheet.cash_and_cash_equivalents',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.CASH_EQUIVALENTS,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.CASH, ACCOUNT_TYPE.BANK],
|
||||
},
|
||||
{
|
||||
name: 'balance_sheet.accounts_receivable',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.ACCOUNTS_RECEIVABLE,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.ACCOUNTS_RECEIVABLE],
|
||||
},
|
||||
{
|
||||
name: 'balance_sheet.inventory',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.INVENTORY,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.INVENTORY],
|
||||
},
|
||||
{
|
||||
name: 'balance_sheet.other_current_assets',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.OTHER_CURRENT_ASSET,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.OTHER_CURRENT_ASSET],
|
||||
},
|
||||
],
|
||||
alwaysShow: true,
|
||||
},
|
||||
{
|
||||
name: 'balance_sheet.fixed_asset',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.FIXED_ASSET,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.FIXED_ASSET],
|
||||
},
|
||||
{
|
||||
name: 'balance_sheet.non_current_assets',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.NON_CURRENT_ASSET,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.NON_CURRENT_ASSET],
|
||||
},
|
||||
],
|
||||
alwaysShow: true,
|
||||
},
|
||||
{
|
||||
name: 'balance_sheet.liabilities_and_equity',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.LIABILITY_EQUITY,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE,
|
||||
children: [
|
||||
{
|
||||
name: 'balance_sheet.liabilities',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.LIABILITY,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE,
|
||||
children: [
|
||||
{
|
||||
name: 'balance_sheet.current_liabilties',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.CURRENT_LIABILITY,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
accountsTypes: [
|
||||
ACCOUNT_TYPE.ACCOUNTS_PAYABLE,
|
||||
ACCOUNT_TYPE.TAX_PAYABLE,
|
||||
ACCOUNT_TYPE.CREDIT_CARD,
|
||||
ACCOUNT_TYPE.OTHER_CURRENT_LIABILITY,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'balance_sheet.long_term_liabilities',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.LOGN_TERM_LIABILITY,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.LOGN_TERM_LIABILITY],
|
||||
},
|
||||
{
|
||||
name: 'balance_sheet.non_current_liabilities',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.NON_CURRENT_LIABILITY,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.NON_CURRENT_LIABILITY],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'balance_sheet.equity',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.EQUITY,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.EQUITY],
|
||||
},
|
||||
],
|
||||
alwaysShow: true,
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,138 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import moment from 'moment';
|
||||
import {
|
||||
IBalanceSheetStatementService,
|
||||
IBalanceSheetQuery,
|
||||
IBalanceSheetStatement,
|
||||
IBalanceSheetMeta,
|
||||
} from '@/interfaces';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import Journal from '@/services/Accounting/JournalPoster';
|
||||
import BalanceSheetStatement from './BalanceSheet';
|
||||
import InventoryService from '@/services/Inventory/Inventory';
|
||||
import { parseBoolean } from 'utils';
|
||||
import { Tenant } from '@/system/models';
|
||||
import BalanceSheetRepository from './BalanceSheetRepository';
|
||||
|
||||
@Service()
|
||||
export default class BalanceSheetStatementService
|
||||
implements IBalanceSheetStatementService
|
||||
{
|
||||
@Inject()
|
||||
tenancy: TenancyService;
|
||||
|
||||
@Inject('logger')
|
||||
logger: any;
|
||||
|
||||
@Inject()
|
||||
inventoryService: InventoryService;
|
||||
|
||||
/**
|
||||
* Defaults balance sheet filter query.
|
||||
* @return {IBalanceSheetQuery}
|
||||
*/
|
||||
get defaultQuery(): IBalanceSheetQuery {
|
||||
return {
|
||||
displayColumnsType: 'total',
|
||||
displayColumnsBy: 'month',
|
||||
|
||||
fromDate: moment().startOf('year').format('YYYY-MM-DD'),
|
||||
toDate: moment().endOf('year').format('YYYY-MM-DD'),
|
||||
|
||||
numberFormat: {
|
||||
precision: 2,
|
||||
divideOn1000: false,
|
||||
showZero: false,
|
||||
formatMoney: 'total',
|
||||
negativeFormat: 'mines',
|
||||
},
|
||||
noneZero: false,
|
||||
noneTransactions: false,
|
||||
|
||||
basis: 'cash',
|
||||
accountIds: [],
|
||||
|
||||
percentageOfColumn: false,
|
||||
percentageOfRow: false,
|
||||
|
||||
previousPeriod: false,
|
||||
previousPeriodAmountChange: false,
|
||||
previousPeriodPercentageChange: false,
|
||||
|
||||
previousYear: false,
|
||||
previousYearAmountChange: false,
|
||||
previousYearPercentageChange: false,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the balance sheet meta.
|
||||
* @param {number} tenantId -
|
||||
* @returns {IBalanceSheetMeta}
|
||||
*/
|
||||
private reportMetadata(tenantId: number): IBalanceSheetMeta {
|
||||
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 balance sheet statement.
|
||||
* -------------
|
||||
* @param {number} tenantId
|
||||
* @param {IBalanceSheetQuery} query
|
||||
*
|
||||
* @return {IBalanceSheetStatement}
|
||||
*/
|
||||
public async balanceSheet(
|
||||
tenantId: number,
|
||||
query: IBalanceSheetQuery
|
||||
): Promise<IBalanceSheetStatement> {
|
||||
const i18n = this.tenancy.i18n(tenantId);
|
||||
|
||||
const tenant = await Tenant.query()
|
||||
.findById(tenantId)
|
||||
.withGraphFetched('metadata');
|
||||
|
||||
const filter = {
|
||||
...this.defaultQuery,
|
||||
...query,
|
||||
};
|
||||
const models = this.tenancy.models(tenantId);
|
||||
const balanceSheetRepo = new BalanceSheetRepository(models, filter);
|
||||
|
||||
await balanceSheetRepo.asyncInitialize();
|
||||
|
||||
// Balance sheet report instance.
|
||||
const balanceSheetInstanace = new BalanceSheetStatement(
|
||||
filter,
|
||||
balanceSheetRepo,
|
||||
tenant.metadata.baseCurrency,
|
||||
i18n
|
||||
);
|
||||
// Balance sheet data.
|
||||
const balanceSheetData = balanceSheetInstanace.reportData();
|
||||
|
||||
return {
|
||||
data: balanceSheetData,
|
||||
query: filter,
|
||||
meta: this.reportMetadata(tenantId),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
IBalanceSheetStatementData,
|
||||
ITableColumnAccessor,
|
||||
IBalanceSheetQuery,
|
||||
ITableColumn,
|
||||
ITableRow,
|
||||
BALANCE_SHEET_SCHEMA_NODE_TYPE,
|
||||
IBalanceSheetDataNode,
|
||||
IBalanceSheetSchemaNode,
|
||||
} from '@/interfaces';
|
||||
import { tableRowMapper } from 'utils';
|
||||
import FinancialSheet from '../FinancialSheet';
|
||||
import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear';
|
||||
import { IROW_TYPE, DISPLAY_COLUMNS_BY } from './constants';
|
||||
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
|
||||
import { BalanceSheetPercentage } from './BalanceSheetPercentage';
|
||||
import { FinancialSheetStructure } from '../FinancialSheetStructure';
|
||||
import { BalanceSheetBase } from './BalanceSheetBase';
|
||||
import { BalanceSheetTablePercentage } from './BalanceSheetTablePercentage';
|
||||
import { BalanceSheetTablePreviousYear } from './BalanceSheetTablePreviousYear';
|
||||
import { BalanceSheetTablePreviousPeriod } from './BalanceSheetTablePreviousPeriod';
|
||||
import { FinancialTable } from '../FinancialTable';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { BalanceSheetTableDatePeriods } from './BalanceSheetTableDatePeriods';
|
||||
|
||||
export default class BalanceSheetTable extends R.compose(
|
||||
BalanceSheetTablePreviousPeriod,
|
||||
BalanceSheetTablePreviousYear,
|
||||
BalanceSheetTableDatePeriods,
|
||||
BalanceSheetTablePercentage,
|
||||
BalanceSheetComparsionPreviousYear,
|
||||
BalanceSheetComparsionPreviousPeriod,
|
||||
BalanceSheetPercentage,
|
||||
FinancialSheetStructure,
|
||||
FinancialTable,
|
||||
BalanceSheetBase
|
||||
)(FinancialSheet) {
|
||||
/**
|
||||
* @param {}
|
||||
*/
|
||||
reportData: IBalanceSheetStatementData;
|
||||
|
||||
/**
|
||||
* Balance sheet query.
|
||||
* @parma {}
|
||||
*/
|
||||
query: BalanceSheetQuery;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {IBalanceSheetStatementData} reportData -
|
||||
* @param {IBalanceSheetQuery} query -
|
||||
*/
|
||||
constructor(
|
||||
reportData: IBalanceSheetStatementData,
|
||||
query: IBalanceSheetQuery,
|
||||
i18n: any
|
||||
) {
|
||||
super();
|
||||
|
||||
this.reportData = reportData;
|
||||
this.query = new BalanceSheetQuery(query);
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detarmines the node type of the given schema node.
|
||||
* @param {IBalanceSheetStructureSection} node -
|
||||
* @param {string} type -
|
||||
* @return {boolean}
|
||||
*/
|
||||
protected isNodeType = R.curry(
|
||||
(type: string, node: IBalanceSheetSchemaNode): boolean => {
|
||||
return node.nodeType === type;
|
||||
}
|
||||
);
|
||||
|
||||
// -------------------------
|
||||
// # Accessors.
|
||||
// -------------------------
|
||||
/**
|
||||
* Retrieve the common columns for all report nodes.
|
||||
* @param {ITableColumnAccessor[]}
|
||||
*/
|
||||
private commonColumnsAccessors = (): ITableColumnAccessor[] => {
|
||||
return R.compose(
|
||||
R.concat([{ key: 'name', accessor: 'name' }]),
|
||||
R.ifElse(
|
||||
R.always(this.isDisplayColumnsBy(DISPLAY_COLUMNS_BY.DATE_PERIODS)),
|
||||
R.concat(this.datePeriodsColumnsAccessors()),
|
||||
R.concat(this.totalColumnAccessor())
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the total column accessor.
|
||||
* @return {ITableColumnAccessor[]}
|
||||
*/
|
||||
private totalColumnAccessor = (): ITableColumnAccessor[] => {
|
||||
return R.pipe(
|
||||
R.concat(this.previousPeriodColumnAccessor()),
|
||||
R.concat(this.previousYearColumnAccessor()),
|
||||
R.concat(this.percentageColumnsAccessor()),
|
||||
R.concat([{ key: 'total', accessor: 'total.formattedAmount' }])
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
private aggregateNodeTableRowsMapper = (node): ITableRow => {
|
||||
const columns = this.commonColumnsAccessors();
|
||||
const meta = {
|
||||
rowTypes: [IROW_TYPE.AGGREGATE],
|
||||
id: node.id,
|
||||
};
|
||||
return tableRowMapper(node, columns, meta);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
private accountsNodeTableRowsMapper = (node): ITableRow => {
|
||||
const columns = this.commonColumnsAccessors();
|
||||
const meta = {
|
||||
rowTypes: [IROW_TYPE.ACCOUNTS],
|
||||
id: node.id,
|
||||
};
|
||||
return tableRowMapper(node, columns, meta);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {} node
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
private accountNodeTableRowsMapper = (node): ITableRow => {
|
||||
const columns = this.commonColumnsAccessors();
|
||||
|
||||
const meta = {
|
||||
rowTypes: [IROW_TYPE.ACCOUNT],
|
||||
id: node.id,
|
||||
};
|
||||
return tableRowMapper(node, columns, meta);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the given report node to table rows.
|
||||
* @param {IBalanceSheetDataNode} node -
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
private nodeToTableRowsMapper = (node: IBalanceSheetDataNode): ITableRow => {
|
||||
return R.cond([
|
||||
[
|
||||
this.isNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE),
|
||||
this.aggregateNodeTableRowsMapper,
|
||||
],
|
||||
[
|
||||
this.isNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS),
|
||||
this.accountsNodeTableRowsMapper,
|
||||
],
|
||||
[
|
||||
this.isNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNT),
|
||||
this.accountNodeTableRowsMapper,
|
||||
],
|
||||
])(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the given report sections to table rows.
|
||||
* @param {IBalanceSheetDataNode[]} nodes -
|
||||
* @return {ITableRow}
|
||||
*/
|
||||
private nodesToTableRowsMapper = (
|
||||
nodes: IBalanceSheetDataNode[]
|
||||
): ITableRow[] => {
|
||||
return this.mapNodesDeep(nodes, this.nodeToTableRowsMapper);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the total children columns.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
private totalColumnChildren = (): ITableColumn[] => {
|
||||
return R.compose(
|
||||
R.unless(
|
||||
R.isEmpty,
|
||||
R.concat([{ key: 'total', Label: this.i18n.__('balance_sheet.total') }])
|
||||
),
|
||||
R.concat(this.percentageColumns()),
|
||||
R.concat(this.getPreviousYearColumns()),
|
||||
R.concat(this.previousPeriodColumns())
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the total column.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
private totalColumn = (): ITableColumn[] => {
|
||||
return [
|
||||
{
|
||||
key: 'total',
|
||||
label: this.i18n.__('balance_sheet.total'),
|
||||
children: this.totalColumnChildren(),
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the report table rows.
|
||||
* @returns {ITableRow[]}
|
||||
*/
|
||||
public tableRows = (): ITableRow[] => {
|
||||
return R.compose(
|
||||
this.addTotalRows,
|
||||
this.nodesToTableRowsMapper
|
||||
)(this.reportData);
|
||||
};
|
||||
|
||||
// -------------------------
|
||||
// # Columns.
|
||||
// -------------------------
|
||||
/**
|
||||
* Retrieve the report table columns.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
public tableColumns = (): ITableColumn[] => {
|
||||
return R.compose(
|
||||
this.tableColumnsCellIndexing,
|
||||
R.concat([
|
||||
{ key: 'name', label: this.i18n.__('balance_sheet.account_name') },
|
||||
]),
|
||||
R.ifElse(
|
||||
this.query.isDatePeriodsColumnsType,
|
||||
R.concat(this.datePeriodsColumns()),
|
||||
R.concat(this.totalColumn())
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
import * as R from 'ramda';
|
||||
import moment from 'moment';
|
||||
import {
|
||||
ITableColumn,
|
||||
IDateRange,
|
||||
ICashFlowDateRange,
|
||||
ITableColumnAccessor,
|
||||
} from '@/interfaces';
|
||||
import { FinancialDatePeriods } from '../FinancialDatePeriods';
|
||||
|
||||
export const BalanceSheetTableDatePeriods = (Base) =>
|
||||
class extends R.compose(FinancialDatePeriods)(Base) {
|
||||
/**
|
||||
* Retrieves the date periods based on the report query.
|
||||
* @returns {IDateRange[]}
|
||||
*/
|
||||
get datePeriods() {
|
||||
return this.getDateRanges(
|
||||
this.query.fromDate,
|
||||
this.query.toDate,
|
||||
this.query.displayColumnsBy
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the formatted column label from the given date range.
|
||||
* @param {ICashFlowDateRange} dateRange -
|
||||
* @return {string}
|
||||
*/
|
||||
private formatColumnLabel = (dateRange: ICashFlowDateRange) => {
|
||||
const monthFormat = (range) => moment(range.toDate).format('YYYY-MM');
|
||||
const yearFormat = (range) => moment(range.toDate).format('YYYY');
|
||||
const dayFormat = (range) => moment(range.toDate).format('YYYY-MM-DD');
|
||||
|
||||
const conditions = [
|
||||
['month', monthFormat],
|
||||
['year', yearFormat],
|
||||
['day', dayFormat],
|
||||
['quarter', monthFormat],
|
||||
['week', dayFormat],
|
||||
];
|
||||
const conditionsPairs = R.map(
|
||||
([type, formatFn]) => [
|
||||
R.always(this.query.isDisplayColumnsBy(type)),
|
||||
formatFn,
|
||||
],
|
||||
conditions
|
||||
);
|
||||
return R.compose(R.cond(conditionsPairs))(dateRange);
|
||||
};
|
||||
|
||||
// -------------------------
|
||||
// # Accessors.
|
||||
// -------------------------
|
||||
/**
|
||||
* Date period columns accessor.
|
||||
* @param {IDateRange} dateRange -
|
||||
* @param {number} index -
|
||||
*/
|
||||
private datePeriodColumnsAccessor = R.curry(
|
||||
(dateRange: IDateRange, index: number) => {
|
||||
return R.pipe(
|
||||
R.concat(this.previousPeriodHorizColumnAccessors(index)),
|
||||
R.concat(this.previousYearHorizontalColumnAccessors(index)),
|
||||
R.concat(this.percetangeDatePeriodColumnsAccessor(index)),
|
||||
R.concat([
|
||||
{
|
||||
key: `date-range-${index}`,
|
||||
accessor: `horizontalTotals[${index}].total.formattedAmount`,
|
||||
},
|
||||
])
|
||||
)([]);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Retrieve the date periods columns accessors.
|
||||
* @returns {ITableColumnAccessor[]}
|
||||
*/
|
||||
protected datePeriodsColumnsAccessors = (): ITableColumnAccessor[] => {
|
||||
return R.compose(
|
||||
R.flatten,
|
||||
R.addIndex(R.map)(this.datePeriodColumnsAccessor)
|
||||
)(this.datePeriods);
|
||||
};
|
||||
|
||||
// -------------------------
|
||||
// # Columns.
|
||||
// -------------------------
|
||||
/**
|
||||
*
|
||||
* @param {number} index
|
||||
* @param {} dateRange
|
||||
* @returns {}
|
||||
*/
|
||||
private datePeriodChildrenColumns = (
|
||||
index: number,
|
||||
dateRange: IDateRange
|
||||
) => {
|
||||
return R.compose(
|
||||
R.unless(
|
||||
R.isEmpty,
|
||||
R.concat([
|
||||
{ key: `total`, label: this.i18n.__('balance_sheet.total') },
|
||||
])
|
||||
),
|
||||
R.concat(this.percentageColumns()),
|
||||
R.concat(this.getPreviousYearHorizontalColumns(dateRange)),
|
||||
R.concat(this.previousPeriodHorizontalColumns(dateRange))
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param dateRange
|
||||
* @param index
|
||||
* @returns
|
||||
*/
|
||||
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,83 @@
|
||||
import * as R from 'ramda';
|
||||
import { ITableColumn } from '@/interfaces';
|
||||
|
||||
export const BalanceSheetTablePercentage = (Base) =>
|
||||
class extends Base {
|
||||
// --------------------
|
||||
// # Columns
|
||||
// --------------------
|
||||
/**
|
||||
* Retrieve percentage of column/row columns.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
protected percentageColumns = (): ITableColumn[] => {
|
||||
return R.pipe(
|
||||
R.when(
|
||||
this.query.isColumnsPercentageActive,
|
||||
R.append({
|
||||
key: 'percentage_of_column',
|
||||
label: this.i18n.__('balance_sheet.percentage_of_column'),
|
||||
})
|
||||
),
|
||||
R.when(
|
||||
this.query.isRowsPercentageActive,
|
||||
R.append({
|
||||
key: 'percentage_of_row',
|
||||
label: this.i18n.__('balance_sheet.percentage_of_row'),
|
||||
})
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
|
||||
// --------------------
|
||||
// # Accessors
|
||||
// --------------------
|
||||
/**
|
||||
* Retrieves percentage of column/row accessors.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
protected percentageColumnsAccessor = (): ITableColumn[] => {
|
||||
return R.pipe(
|
||||
R.when(
|
||||
this.query.isColumnsPercentageActive,
|
||||
R.append({
|
||||
key: 'percentage_of_column',
|
||||
accessor: 'percentageColumn.formattedAmount',
|
||||
})
|
||||
),
|
||||
R.when(
|
||||
this.query.isRowsPercentageActive,
|
||||
R.append({
|
||||
key: 'percentage_of_row',
|
||||
accessor: 'percentageRow.formattedAmount',
|
||||
})
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Percentage columns accessors for date period columns.
|
||||
* @param {number} index
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
protected percetangeDatePeriodColumnsAccessor = (
|
||||
index: number
|
||||
): ITableColumn[] => {
|
||||
return R.pipe(
|
||||
R.when(
|
||||
this.query.isColumnsPercentageActive,
|
||||
R.append({
|
||||
key: `percentage_of_column-${index}`,
|
||||
accessor: `horizontalTotals[${index}].percentageColumn.formattedAmount`,
|
||||
})
|
||||
),
|
||||
R.when(
|
||||
this.query.isRowsPercentageActive,
|
||||
R.append({
|
||||
key: `percentage_of_row-${index}`,
|
||||
accessor: `horizontalTotals[${index}].percentageRow.formattedAmount`,
|
||||
})
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,109 @@
|
||||
import * as R from 'ramda';
|
||||
import { IDateRange, ITableColumn } from '@/interfaces';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { FinancialTablePreviousPeriod } from '../FinancialTablePreviousPeriod';
|
||||
import { FinancialDateRanges } from '../FinancialDateRanges';
|
||||
|
||||
export const BalanceSheetTablePreviousPeriod = (Base) =>
|
||||
class extends R.compose(
|
||||
FinancialTablePreviousPeriod,
|
||||
FinancialDateRanges
|
||||
)(Base) {
|
||||
readonly query: BalanceSheetQuery;
|
||||
|
||||
// --------------------
|
||||
// # Columns
|
||||
// --------------------
|
||||
/**
|
||||
* Retrieves the previous period columns.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
protected previousPeriodColumns = (
|
||||
dateRange?: IDateRange
|
||||
): ITableColumn[] => {
|
||||
return R.pipe(
|
||||
// Previous period columns.
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
R.append(this.getPreviousPeriodTotalColumn(dateRange))
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
R.append(this.getPreviousPeriodChangeColumn())
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
R.append(this.getPreviousPeriodPercentageColumn())
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Previous period for date periods
|
||||
* @param {IDateRange} dateRange
|
||||
* @returns {ITableColumn}
|
||||
*/
|
||||
protected previousPeriodHorizontalColumns = (
|
||||
dateRange: IDateRange
|
||||
): ITableColumn[] => {
|
||||
const PPDateRange = this.getPPDatePeriodDateRange(
|
||||
dateRange.fromDate,
|
||||
dateRange.toDate,
|
||||
this.query.displayColumnsBy
|
||||
);
|
||||
return this.previousPeriodColumns({
|
||||
fromDate: PPDateRange.fromDate,
|
||||
toDate: PPDateRange.toDate,
|
||||
});
|
||||
};
|
||||
|
||||
// --------------------
|
||||
// # Accessors
|
||||
// --------------------
|
||||
/**
|
||||
* Retrieves previous period columns accessors.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
protected previousPeriodColumnAccessor = (): ITableColumn[] => {
|
||||
return R.pipe(
|
||||
// Previous period columns.
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
R.append(this.getPreviousPeriodTotalAccessor())
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
R.append(this.getPreviousPeriodChangeAccessor())
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
R.append(this.getPreviousPeriodPercentageAccessor())
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} index
|
||||
* @returns
|
||||
*/
|
||||
protected previousPeriodHorizColumnAccessors = (
|
||||
index: number
|
||||
): ITableColumn[] => {
|
||||
return R.pipe(
|
||||
// Previous period columns.
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
R.append(this.getPreviousPeriodTotalHorizAccessor(index))
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
R.append(this.getPreviousPeriodChangeHorizAccessor(index))
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
R.append(this.getPreviousPeriodPercentageHorizAccessor(index))
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,97 @@
|
||||
import * as R from 'ramda';
|
||||
import { IDateRange, ITableColumn } from '@/interfaces';
|
||||
import { FinancialTablePreviousYear } from '../FinancialTablePreviousYear';
|
||||
import { FinancialDateRanges } from '../FinancialDateRanges';
|
||||
|
||||
export const BalanceSheetTablePreviousYear = (Base) =>
|
||||
class extends R.compose(FinancialTablePreviousYear, FinancialDateRanges)(Base) {
|
||||
// --------------------
|
||||
// # Columns.
|
||||
// --------------------
|
||||
/**
|
||||
* Retrieves pervious year comparison columns.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
protected getPreviousYearColumns = (
|
||||
dateRange?: IDateRange
|
||||
): ITableColumn[] => {
|
||||
return R.pipe(
|
||||
// Previous year columns.
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
R.append(this.getPreviousYearTotalColumn(dateRange))
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
R.append(this.getPreviousYearChangeColumn())
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
R.append(this.getPreviousYearPercentageColumn())
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IDateRange} dateRange
|
||||
* @returns
|
||||
*/
|
||||
protected getPreviousYearHorizontalColumns = (dateRange: IDateRange) => {
|
||||
const PYDateRange = this.getPreviousYearDateRange(
|
||||
dateRange.fromDate,
|
||||
dateRange.toDate
|
||||
);
|
||||
return this.getPreviousYearColumns(PYDateRange);
|
||||
};
|
||||
|
||||
// --------------------
|
||||
// # Accessors.
|
||||
// --------------------
|
||||
/**
|
||||
* Retrieves previous year columns accessors.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
protected previousYearColumnAccessor = (): ITableColumn[] => {
|
||||
return R.pipe(
|
||||
// Previous year columns.
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
R.append(this.getPreviousYearTotalAccessor())
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
R.append(this.getPreviousYearChangeAccessor())
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
R.append(this.getPreviousYearPercentageAccessor())
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Previous year period column accessor.
|
||||
* @param {number} index
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
protected previousYearHorizontalColumnAccessors = (
|
||||
index: number
|
||||
): ITableColumn[] => {
|
||||
return R.pipe(
|
||||
// Previous year columns.
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
R.append(this.getPreviousYearTotalHorizAccessor(index))
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
R.append(this.getPreviousYearChangeHorizAccessor(index))
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
R.append(this.getPreviousYearPercentageHorizAccessor(index))
|
||||
)
|
||||
)([]);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
import * as R from 'ramda';
|
||||
|
||||
export const BalanceSheetTotal = (Base: any) => class extends Base {};
|
||||
@@ -0,0 +1,13 @@
|
||||
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',
|
||||
}
|
||||
Reference in New Issue
Block a user