add server to monorepo.

This commit is contained in:
a.bouhuolia
2023-02-03 11:57:50 +02:00
parent 28e309981b
commit 80b97b5fdc
1303 changed files with 137049 additions and 0 deletions

View File

@@ -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);
};
}

View File

@@ -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;
};
};

View File

@@ -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);
};
};

View File

@@ -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);
};

View File

@@ -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);
};
};

View File

@@ -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);
};
};

View File

@@ -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)
);
};
};

View File

@@ -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;
};
}

View File

@@ -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);
}
};
}

View File

@@ -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,
},
];

View File

@@ -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),
};
}
}

View File

@@ -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())
)
)([]);
};
}

View File

@@ -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);
};
};

View File

@@ -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`,
})
)
)([]);
};
};

View File

@@ -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))
)
)([]);
};
};

View File

@@ -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))
)
)([]);
};
};

View File

@@ -0,0 +1,3 @@
import * as R from 'ramda';
export const BalanceSheetTotal = (Base: any) => class extends Base {};

View File

@@ -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',
}