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,121 @@
import moment from 'moment';
import { Inject, Service } from 'typedi';
import { IAPAgingSummaryQuery, IARAgingSummaryMeta } from '@/interfaces';
import TenancyService from '@/services/Tenancy/TenancyService';
import APAgingSummarySheet from './APAgingSummarySheet';
import { Tenant } from '@/system/models';
import { isEmpty } from 'lodash';
@Service()
export default class PayableAgingSummaryService {
@Inject()
tenancy: TenancyService;
@Inject('logger')
logger: any;
/**
* Default report query.
*/
get defaultQuery(): IAPAgingSummaryQuery {
return {
asDate: moment().format('YYYY-MM-DD'),
agingDaysBefore: 30,
agingPeriods: 3,
numberFormat: {
precision: 2,
divideOn1000: false,
showZero: false,
formatMoney: 'total',
negativeFormat: 'mines',
},
vendorsIds: [],
branchesIds: [],
noneZero: false,
};
}
/**
* Retrieve the balance sheet meta.
* @param {number} tenantId -
* @returns {IBalanceSheetMeta}
*/
reportMetadata(tenantId: number): IARAgingSummaryMeta {
const settings = this.tenancy.settings(tenantId);
const organizationName = settings.get({
group: 'organization',
key: 'name',
});
const baseCurrency = settings.get({
group: 'organization',
key: 'base_currency',
});
return {
organizationName,
baseCurrency,
};
}
/**
* Retrieve A/P aging summary report.
* @param {number} tenantId -
* @param {IAPAgingSummaryQuery} query -
*/
async APAgingSummary(tenantId: number, query: IAPAgingSummaryQuery) {
const { Bill } = this.tenancy.models(tenantId);
const { vendorRepository } = this.tenancy.repositories(tenantId);
const filter = {
...this.defaultQuery,
...query,
};
// Settings tenant service.
const tenant = await Tenant.query()
.findById(tenantId)
.withGraphFetched('metadata');
// Retrieve all vendors from the storage.
const vendors =
filter.vendorsIds.length > 0
? await vendorRepository.findWhereIn('id', filter.vendorsIds)
: await vendorRepository.all();
// Common query.
const commonQuery = (query) => {
if (isEmpty(filter.branchesIds)) {
query.modify('filterByBranches', filter.branchesIds);
}
};
// Retrieve all overdue vendors bills.
const overdueBills = await Bill.query()
.modify('overdueBillsFromDate', filter.asDate)
.onBuild(commonQuery);
// Retrieve all due vendors bills.
const dueBills = await Bill.query()
.modify('dueBillsFromDate', filter.asDate)
.onBuild(commonQuery);
// A/P aging summary report instance.
const APAgingSummaryReport = new APAgingSummarySheet(
tenantId,
filter,
vendors,
overdueBills,
dueBills,
tenant.metadata.baseCurrency
);
// A/P aging summary report data and columns.
const data = APAgingSummaryReport.reportData();
const columns = APAgingSummaryReport.reportColumns();
return {
data,
columns,
query: filter,
meta: this.reportMetadata(tenantId),
};
}
}

View File

@@ -0,0 +1,183 @@
import { groupBy, sum, isEmpty } from 'lodash';
import * as R from 'ramda';
import AgingSummaryReport from './AgingSummary';
import {
IAPAgingSummaryQuery,
IAgingPeriod,
IBill,
IVendor,
IAPAgingSummaryData,
IAPAgingSummaryVendor,
IAPAgingSummaryColumns,
IAPAgingSummaryTotal,
} from '@/interfaces';
import { Dictionary } from 'tsyringe/dist/typings/types';
import { allPassedConditionsPass } from 'utils';
export default class APAgingSummarySheet extends AgingSummaryReport {
readonly tenantId: number;
readonly query: IAPAgingSummaryQuery;
readonly contacts: IVendor[];
readonly unpaidBills: IBill[];
readonly baseCurrency: string;
readonly overdueInvoicesByContactId: Dictionary<IBill[]>;
readonly currentInvoicesByContactId: Dictionary<IBill[]>;
readonly agingPeriods: IAgingPeriod[];
/**
* Constructor method.
* @param {number} tenantId - Tenant id.
* @param {IAPAgingSummaryQuery} query - Report query.
* @param {IVendor[]} vendors - Unpaid bills.
* @param {string} baseCurrency - Base currency of the organization.
*/
constructor(
tenantId: number,
query: IAPAgingSummaryQuery,
vendors: IVendor[],
overdueBills: IBill[],
unpaidBills: IBill[],
baseCurrency: string
) {
super();
this.tenantId = tenantId;
this.query = query;
this.numberFormat = this.query.numberFormat;
this.contacts = vendors;
this.baseCurrency = baseCurrency;
this.overdueInvoicesByContactId = groupBy(overdueBills, 'vendorId');
this.currentInvoicesByContactId = groupBy(unpaidBills, 'vendorId');
// Initializes the aging periods.
this.agingPeriods = this.agingRangePeriods(
this.query.asDate,
this.query.agingDaysBefore,
this.query.agingPeriods
);
}
/**
* Retrieve the vendors aging and current total.
* @param {IAPAgingSummaryTotal} vendorsAgingPeriods
* @return {IAPAgingSummaryTotal}
*/
private getVendorsTotal = (vendorsAgingPeriods): IAPAgingSummaryTotal => {
const totalAgingPeriods = this.getTotalAgingPeriods(vendorsAgingPeriods);
const totalCurrent = this.getTotalCurrent(vendorsAgingPeriods);
const totalVendorsTotal = this.getTotalContactsTotals(vendorsAgingPeriods);
return {
current: this.formatTotalAmount(totalCurrent),
aging: totalAgingPeriods,
total: this.formatTotalAmount(totalVendorsTotal),
};
};
/**
* Retrieve the vendor section data.
* @param {IVendor} vendor
* @return {IAPAgingSummaryVendor}
*/
private vendorTransformer = (vendor: IVendor): IAPAgingSummaryVendor => {
const agingPeriods = this.getContactAgingPeriods(vendor.id);
const currentTotal = this.getContactCurrentTotal(vendor.id);
const agingPeriodsTotal = this.getAgingPeriodsTotal(agingPeriods);
const amount = sum([agingPeriodsTotal, currentTotal]);
return {
vendorName: vendor.displayName,
current: this.formatTotalAmount(currentTotal),
aging: agingPeriods,
total: this.formatTotalAmount(amount),
};
};
/**
* Mappes the given vendor objects to vendor report node.
* @param {IVendor[]} vendors
* @returns {IAPAgingSummaryVendor[]}
*/
private vendorsMapper = (vendors: IVendor[]): IAPAgingSummaryVendor[] => {
return vendors.map(this.vendorTransformer);
};
/**
* Detarmines whether the given vendor node is none zero.
* @param {IAPAgingSummaryVendor} vendorNode
* @returns {boolean}
*/
private filterNoneZeroVendorNode = (
vendorNode: IAPAgingSummaryVendor
): boolean => {
return vendorNode.total.amount !== 0;
};
/**
* Filters vendors report nodes based on the given report query.
* @param {IAPAgingSummaryVendor} vendorNode
* @returns {boolean}
*/
private vendorNodeFilter = (vendorNode: IAPAgingSummaryVendor): boolean => {
const { noneZero } = this.query;
const conditions = [[noneZero, this.filterNoneZeroVendorNode]];
return allPassedConditionsPass(conditions)(vendorNode);
};
/**
* Filtesr the given report vendors nodes.
* @param {IAPAgingSummaryVendor[]} vendorNodes
* @returns {IAPAgingSummaryVendor[]}
*/
private vendorsFilter = (
vendorNodes: IAPAgingSummaryVendor[]
): IAPAgingSummaryVendor[] => {
return vendorNodes.filter(this.vendorNodeFilter);
};
/**
* Detarmines whether vendors nodes filter enabled.
* @returns {boolean}
*/
private isVendorNodesFilter = (): boolean => {
return isEmpty(this.query.vendorsIds);
};
/**
* Retrieve vendors aging periods.
* @return {IAPAgingSummaryVendor[]}
*/
private vendorsSection = (vendors: IVendor[]): IAPAgingSummaryVendor[] => {
return R.compose(
R.when(this.isVendorNodesFilter, this.vendorsFilter),
this.vendorsMapper
)(vendors);
};
/**
* Retrieve the A/P aging summary report data.
* @return {IAPAgingSummaryData}
*/
public reportData = (): IAPAgingSummaryData => {
const vendorsAgingPeriods = this.vendorsSection(this.contacts);
const vendorsTotal = this.getVendorsTotal(vendorsAgingPeriods);
return {
vendors: vendorsAgingPeriods,
total: vendorsTotal,
};
};
/**
* Retrieve the A/P aging summary report columns.
*/
public reportColumns = (): IAPAgingSummaryColumns => {
return this.agingPeriods;
};
}

View File

@@ -0,0 +1,120 @@
import moment from 'moment';
import { Inject, Service } from 'typedi';
import { isEmpty } from 'lodash';
import { IARAgingSummaryQuery, IARAgingSummaryMeta } from '@/interfaces';
import TenancyService from '@/services/Tenancy/TenancyService';
import ARAgingSummarySheet from './ARAgingSummarySheet';
import { Tenant } from '@/system/models';
@Service()
export default class ARAgingSummaryService {
@Inject()
tenancy: TenancyService;
@Inject('logger')
logger: any;
/**
* Default report query.
*/
get defaultQuery(): IARAgingSummaryQuery {
return {
asDate: moment().format('YYYY-MM-DD'),
agingDaysBefore: 30,
agingPeriods: 3,
numberFormat: {
divideOn1000: false,
negativeFormat: 'mines',
showZero: false,
formatMoney: 'total',
precision: 2,
},
customersIds: [],
branchesIds: [],
noneZero: false,
};
}
/**
* Retrieve the balance sheet meta.
* @param {number} tenantId -
* @returns {IBalanceSheetMeta}
*/
reportMetadata(tenantId: number): IARAgingSummaryMeta {
const settings = this.tenancy.settings(tenantId);
const organizationName = settings.get({
group: 'organization',
key: 'name',
});
const baseCurrency = settings.get({
group: 'organization',
key: 'base_currency',
});
return {
organizationName,
baseCurrency,
};
}
/**
* Retrieve A/R aging summary report.
* @param {number} tenantId - Tenant id.
* @param {IARAgingSummaryQuery} query -
*/
async ARAgingSummary(tenantId: number, query: IARAgingSummaryQuery) {
const { SaleInvoice } = this.tenancy.models(tenantId);
const { customerRepository } = this.tenancy.repositories(tenantId);
const filter = {
...this.defaultQuery,
...query,
};
const tenant = await Tenant.query()
.findById(tenantId)
.withGraphFetched('metadata');
// Retrieve all customers from the storage.
const customers =
filter.customersIds.length > 0
? await customerRepository.findWhereIn('id', filter.customersIds)
: await customerRepository.all();
// Common query.
const commonQuery = (query) => {
if (!isEmpty(filter.branchesIds)) {
query.modify('filterByBranches', filter.branchesIds);
}
};
// Retrieve all overdue sale invoices.
const overdueSaleInvoices = await SaleInvoice.query()
.modify('dueInvoicesFromDate', filter.asDate)
.onBuild(commonQuery);
// Retrieve all due sale invoices.
const currentInvoices = await SaleInvoice.query()
.modify('overdueInvoicesFromDate', filter.asDate)
.onBuild(commonQuery);
// AR aging summary report instance.
const ARAgingSummaryReport = new ARAgingSummarySheet(
tenantId,
filter,
customers,
overdueSaleInvoices,
currentInvoices,
tenant.metadata.baseCurrency
);
// AR aging summary report data and columns.
const data = ARAgingSummaryReport.reportData();
const columns = ARAgingSummaryReport.reportColumns();
return {
data,
columns,
query: filter,
meta: this.reportMetadata(tenantId),
};
}
}

View File

@@ -0,0 +1,198 @@
import { groupBy, isEmpty, sum } from 'lodash';
import * as R from 'ramda';
import {
ICustomer,
IARAgingSummaryQuery,
IARAgingSummaryCustomer,
IAgingPeriod,
ISaleInvoice,
IARAgingSummaryData,
IARAgingSummaryColumns,
IARAgingSummaryTotal,
} from '@/interfaces';
import AgingSummaryReport from './AgingSummary';
import { allPassedConditionsPass } from '../../../utils';
export default class ARAgingSummarySheet extends AgingSummaryReport {
readonly tenantId: number;
readonly query: IARAgingSummaryQuery;
readonly contacts: ICustomer[];
readonly agingPeriods: IAgingPeriod[];
readonly baseCurrency: string;
readonly overdueInvoicesByContactId: Dictionary<ISaleInvoice[]>;
readonly currentInvoicesByContactId: Dictionary<ISaleInvoice[]>;
/**
* Constructor method.
* @param {number} tenantId
* @param {IARAgingSummaryQuery} query
* @param {ICustomer[]} customers
* @param {IJournalPoster} journal
*/
constructor(
tenantId: number,
query: IARAgingSummaryQuery,
customers: ICustomer[],
overdueSaleInvoices: ISaleInvoice[],
currentSaleInvoices: ISaleInvoice[],
baseCurrency: string
) {
super();
this.tenantId = tenantId;
this.contacts = customers;
this.query = query;
this.baseCurrency = baseCurrency;
this.numberFormat = this.query.numberFormat;
this.overdueInvoicesByContactId = groupBy(
overdueSaleInvoices,
'customerId'
);
this.currentInvoicesByContactId = groupBy(
currentSaleInvoices,
'customerId'
);
// Initializes the aging periods.
this.agingPeriods = this.agingRangePeriods(
this.query.asDate,
this.query.agingDaysBefore,
this.query.agingPeriods
);
}
/**
* Mapping aging customer.
* @param {ICustomer} customer -
* @return {IARAgingSummaryCustomer[]}
*/
private customerTransformer = (
customer: ICustomer
): IARAgingSummaryCustomer => {
const agingPeriods = this.getContactAgingPeriods(customer.id);
const currentTotal = this.getContactCurrentTotal(customer.id);
const agingPeriodsTotal = this.getAgingPeriodsTotal(agingPeriods);
const amount = sum([agingPeriodsTotal, currentTotal]);
return {
customerName: customer.displayName,
current: this.formatAmount(currentTotal),
aging: agingPeriods,
total: this.formatTotalAmount(amount),
};
};
/**
* Mappes the customers objects to report accounts nodes.
* @param {ICustomer[]} customers
* @returns {IARAgingSummaryCustomer[]}
*/
private customersMapper = (
customers: ICustomer[]
): IARAgingSummaryCustomer[] => {
return customers.map(this.customerTransformer);
};
/**
* Filters the none-zero account report node.
* @param {IARAgingSummaryCustomer} node
* @returns {boolean}
*/
private filterNoneZeroAccountNode = (
node: IARAgingSummaryCustomer
): boolean => {
return node.total.amount !== 0;
};
/**
* Filters customer report node based on the given report query.
* @param {IARAgingSummaryCustomer} customerNode
* @returns {boolean}
*/
private customerNodeFilter = (
customerNode: IARAgingSummaryCustomer
): boolean => {
const { noneZero } = this.query;
const conditions = [[noneZero, this.filterNoneZeroAccountNode]];
return allPassedConditionsPass(conditions)(customerNode);
};
/**
* Filters customers report nodes.
* @param {IARAgingSummaryCustomer[]} customers
* @returns {IARAgingSummaryCustomer[]}
*/
private customersFilter = (
customers: IARAgingSummaryCustomer[]
): IARAgingSummaryCustomer[] => {
return customers.filter(this.customerNodeFilter);
};
/**
* Detarmines the customers nodes filter is enabled.
* @returns {boolean}
*/
private isCustomersFilterEnabled = (): boolean => {
return isEmpty(this.query.customersIds);
}
/**
* Retrieve customers report.
* @param {ICustomer[]} customers
* @return {IARAgingSummaryCustomer[]}
*/
private customersWalker = (
customers: ICustomer[]
): IARAgingSummaryCustomer[] => {
return R.compose(
R.when(this.isCustomersFilterEnabled, this.customersFilter),
this.customersMapper
)(customers);
};
/**
* Retrieve the customers aging and current total.
* @param {IARAgingSummaryCustomer} customersAgingPeriods
*/
private getCustomersTotal = (
customersAgingPeriods: IARAgingSummaryCustomer[]
): IARAgingSummaryTotal => {
const totalAgingPeriods = this.getTotalAgingPeriods(customersAgingPeriods);
const totalCurrent = this.getTotalCurrent(customersAgingPeriods);
const totalCustomersTotal = this.getTotalContactsTotals(
customersAgingPeriods
);
return {
current: this.formatTotalAmount(totalCurrent),
aging: totalAgingPeriods,
total: this.formatTotalAmount(totalCustomersTotal),
};
};
/**
* Retrieve A/R aging summary report data.
* @return {IARAgingSummaryData}
*/
public reportData = (): IARAgingSummaryData => {
const customersAgingPeriods = this.customersWalker(this.contacts);
const customersTotal = this.getCustomersTotal(customersAgingPeriods);
return {
customers: customersAgingPeriods,
total: customersTotal,
};
};
/**
* Retrieve AR aging summary report columns.
* @return {IARAgingSummaryColumns}
*/
public reportColumns(): IARAgingSummaryColumns {
return this.agingPeriods;
}
}

View File

@@ -0,0 +1,54 @@
import moment from 'moment';
import {
IAgingPeriod,
} from '@/interfaces';
import FinancialSheet from "../FinancialSheet";
export default abstract class AgingReport extends FinancialSheet{
/**
* Retrieve the aging periods range.
* @param {string} asDay
* @param {number} agingDaysBefore
* @param {number} agingPeriodsFreq
*/
agingRangePeriods(
asDay: Date|string,
agingDaysBefore: number,
agingPeriodsFreq: number
): IAgingPeriod[] {
const totalAgingDays = agingDaysBefore * agingPeriodsFreq;
const startAging = moment(asDay).startOf('day');
const endAging = startAging
.clone()
.subtract(totalAgingDays, 'days')
.endOf('day');
const agingPeriods: IAgingPeriod[] = [];
const startingAging = startAging.clone();
let beforeDays = 1;
let toDays = 0;
while (startingAging > endAging) {
const currentAging = startingAging.clone();
startingAging.subtract(agingDaysBefore, 'days').endOf('day');
toDays += agingDaysBefore;
agingPeriods.push({
fromPeriod: moment(currentAging).format('YYYY-MM-DD'),
toPeriod: moment(startingAging).format('YYYY-MM-DD'),
beforeDays: beforeDays === 1 ? 0 : beforeDays,
toDays: toDays,
...(startingAging.valueOf() === endAging.valueOf()
? {
toPeriod: null,
toDays: null,
}
: {}),
});
beforeDays += agingDaysBefore;
}
return agingPeriods;
}
}

View File

@@ -0,0 +1,228 @@
import { defaultTo, sumBy, get } from 'lodash';
import {
IAgingPeriod,
ISaleInvoice,
IBill,
IAgingPeriodTotal,
IARAgingSummaryCustomer,
IContact,
IARAgingSummaryQuery,
IFormatNumberSettings,
IAgingAmount,
IAgingSummaryContact,
} from '@/interfaces';
import AgingReport from './AgingReport';
import { Dictionary } from 'tsyringe/dist/typings/types';
export default abstract class AgingSummaryReport extends AgingReport {
protected readonly contacts: IContact[];
protected readonly agingPeriods: IAgingPeriod[] = [];
protected readonly baseCurrency: string;
protected readonly query: IARAgingSummaryQuery;
protected readonly overdueInvoicesByContactId: Dictionary<
(ISaleInvoice | IBill)[]
>;
protected readonly currentInvoicesByContactId: Dictionary<
(ISaleInvoice | IBill)[]
>;
/**
* Setes initial aging periods to the contact.
*/
protected getInitialAgingPeriodsTotal(): IAgingPeriodTotal[] {
return this.agingPeriods.map((agingPeriod) => ({
...agingPeriod,
total: this.formatAmount(0),
}));
}
/**
* Calculates the given contact aging periods.
* @param {number} contactId - Contact id.
* @return {IAgingPeriodTotal[]}
*/
protected getContactAgingPeriods(contactId: number): IAgingPeriodTotal[] {
const unpaidInvoices = this.getUnpaidInvoicesByContactId(contactId);
const initialAgingPeriods = this.getInitialAgingPeriodsTotal();
return unpaidInvoices.reduce(
(agingPeriods: IAgingPeriodTotal[], unpaidInvoice) => {
const newAgingPeriods = this.getContactAgingDueAmount(
agingPeriods,
unpaidInvoice.dueAmount,
unpaidInvoice.overdueDays
);
return newAgingPeriods;
},
initialAgingPeriods
);
}
/**
* Sets the contact aging due amount to the table.
* @param {IAgingPeriodTotal} agingPeriods - Aging periods.
* @param {number} dueAmount - Due amount.
* @param {number} overdueDays - Overdue days.
* @return {IAgingPeriodTotal[]}
*/
protected getContactAgingDueAmount(
agingPeriods: IAgingPeriodTotal[],
dueAmount: number,
overdueDays: number
): IAgingPeriodTotal[] {
const newAgingPeriods = agingPeriods.map((agingPeriod) => {
const isInAgingPeriod =
agingPeriod.beforeDays <= overdueDays &&
(agingPeriod.toDays > overdueDays || !agingPeriod.toDays);
const total: number = isInAgingPeriod
? agingPeriod.total.amount + dueAmount
: agingPeriod.total.amount;
return {
...agingPeriod,
total: this.formatAmount(total),
};
});
return newAgingPeriods;
}
/**
* Retrieve the aging period total object.
* @param {number} amount
* @param {IFormatNumberSettings} settings - Override the format number settings.
* @return {IAgingAmount}
*/
protected formatAmount(
amount: number,
settings: IFormatNumberSettings = {}
): IAgingAmount {
return {
amount,
formattedAmount: this.formatNumber(amount, settings),
currencyCode: this.baseCurrency,
};
}
/**
* Retrieve the aging period total object.
* @param {number} amount
* @param {IFormatNumberSettings} settings - Override the format number settings.
* @return {IAgingPeriodTotal}
*/
protected formatTotalAmount(
amount: number,
settings: IFormatNumberSettings = {}
): IAgingAmount {
return this.formatAmount(amount, {
money: true,
excerptZero: false,
...settings,
});
}
/**
* Calculates the total of the aging period by the given index.
* @param {number} index
* @return {number}
*/
protected getTotalAgingPeriodByIndex(
contactsAgingPeriods: any,
index: number
): number {
return this.contacts.reduce((acc, contact) => {
const totalPeriod = contactsAgingPeriods[index]
? contactsAgingPeriods[index].total
: 0;
return acc + totalPeriod;
}, 0);
}
/**
* Retrieve the due invoices by the given contact id.
* @param {number} contactId -
* @return {(ISaleInvoice | IBill)[]}
*/
protected getUnpaidInvoicesByContactId(
contactId: number
): (ISaleInvoice | IBill)[] {
return defaultTo(this.overdueInvoicesByContactId[contactId], []);
}
/**
* Retrieve total aging periods of the report.
* @return {(IAgingPeriodTotal & IAgingPeriod)[]}
*/
protected getTotalAgingPeriods(
contactsAgingPeriods: IARAgingSummaryCustomer[]
): IAgingPeriodTotal[] {
return this.agingPeriods.map((agingPeriod, index) => {
const total = sumBy(
contactsAgingPeriods,
(summary: IARAgingSummaryCustomer) => {
const aging = summary.aging[index];
if (!aging) {
return 0;
}
return aging.total.amount;
}
);
return {
...agingPeriod,
total: this.formatTotalAmount(total),
};
});
}
/**
* Retrieve the current invoices by the given contact id.
* @param {number} contactId - Specific contact id.
* @return {(ISaleInvoice | IBill)[]}
*/
protected getCurrentInvoicesByContactId(
contactId: number
): (ISaleInvoice | IBill)[] {
return get(this.currentInvoicesByContactId, contactId, []);
}
/**
* Retrieve the contact total due amount.
* @param {number} contactId - Specific contact id.
* @return {number}
*/
protected getContactCurrentTotal(contactId: number): number {
const currentInvoices = this.getCurrentInvoicesByContactId(contactId);
return sumBy(currentInvoices, (invoice) => invoice.dueAmount);
}
/**
* Retrieve to total sumation of the given contacts summeries sections.
* @param {IARAgingSummaryCustomer[]} contactsSections -
* @return {number}
*/
protected getTotalCurrent(contactsSummaries: IAgingSummaryContact[]): number {
return sumBy(contactsSummaries, (summary) => summary.current.amount);
}
/**
* Retrieve the total of the given aging periods.
* @param {IAgingPeriodTotal[]} agingPeriods
* @return {number}
*/
protected getAgingPeriodsTotal(agingPeriods: IAgingPeriodTotal[]): number {
return sumBy(agingPeriods, (period) => period.total.amount);
}
/**
* Retrieve total of contacts totals.
* @param {IAgingSummaryContact[]} contactsSummaries
*/
protected getTotalContactsTotals(
contactsSummaries: IAgingSummaryContact[]
): number {
return sumBy(contactsSummaries, (summary) => summary.total.amount);
}
}