Files
bigcapital/packages/server/src/services/FinancialStatements/AgingSummary/APAgingSummarySheet.ts
2023-08-27 00:56:14 +02:00

184 lines
5.2 KiB
TypeScript

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.formatAmount(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;
};
}