From 09b2aa57a0d005fcdb3c8946e67c2c5be39d8a40 Mon Sep 17 00:00:00 2001 From: "a.bouhuolia" Date: Thu, 7 Jan 2021 16:05:32 +0200 Subject: [PATCH] feat: AR/AP aging summary report. --- .../AgingSummary/APAgingSummarySheet.ts | 24 +++-- .../AgingSummary/ARAgingSummarySheet.ts | 13 +-- .../AgingSummary/AgingReport.ts | 2 +- .../AgingSummary/AgingSummary.ts | 95 ++++++++----------- 4 files changed, 63 insertions(+), 71 deletions(-) diff --git a/server/src/services/FinancialStatements/AgingSummary/APAgingSummarySheet.ts b/server/src/services/FinancialStatements/AgingSummary/APAgingSummarySheet.ts index 49472c32f..397c5261e 100644 --- a/server/src/services/FinancialStatements/AgingSummary/APAgingSummarySheet.ts +++ b/server/src/services/FinancialStatements/AgingSummary/APAgingSummarySheet.ts @@ -20,6 +20,13 @@ export default class APAgingSummarySheet extends AgingSummaryReport { readonly unpaidInvoicesByContactId: Dictionary; 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, @@ -44,16 +51,14 @@ export default class APAgingSummarySheet extends AgingSummaryReport { this.query.agingDaysBefore, this.query.agingPeriods ); - this.initContactsAgingPeriods(); - this.calcUnpaidInvoicesAgingPeriods(); } /** * Retrieve the vendor section data. * @param {IVendor} vendor - * @return {IAPAgingSummaryData} + * @return {IAPAgingSummaryVendor} */ - protected vendorData(vendor: IVendor): IAPAgingSummaryVendor { + private vendorData(vendor: IVendor): IAPAgingSummaryVendor { const agingPeriods = this.getContactAgingPeriods(vendor.id); const amount = sumBy(agingPeriods, 'total'); @@ -68,8 +73,8 @@ export default class APAgingSummarySheet extends AgingSummaryReport { * Retrieve vendors aging periods. * @return {IAPAgingSummaryVendor[]} */ - private vendorsWalker(): IAPAgingSummaryVendor[] { - return this.contacts + private vendorsWalker(vendors: IVendor[]): IAPAgingSummaryVendor[] { + return vendors .map((vendor) => this.vendorData(vendor)) .filter( (vendor: IAPAgingSummaryVendor) => @@ -82,9 +87,12 @@ export default class APAgingSummarySheet extends AgingSummaryReport { * @return {IAPAgingSummaryData} */ public reportData(): IAPAgingSummaryData { + const vendorsAgingPeriods = this.vendorsWalker(this.contacts); + const totalAgingPeriods = this.getTotalAgingPeriods(vendorsAgingPeriods); + return { - vendors: this.vendorsWalker(), - total: this.getTotalAgingPeriods(), + vendors: vendorsAgingPeriods, + total: totalAgingPeriods, } } diff --git a/server/src/services/FinancialStatements/AgingSummary/ARAgingSummarySheet.ts b/server/src/services/FinancialStatements/AgingSummary/ARAgingSummarySheet.ts index 2302b6788..28ee6569c 100644 --- a/server/src/services/FinancialStatements/AgingSummary/ARAgingSummarySheet.ts +++ b/server/src/services/FinancialStatements/AgingSummary/ARAgingSummarySheet.ts @@ -52,8 +52,6 @@ export default class ARAgingSummarySheet extends AgingSummaryReport { this.query.agingDaysBefore, this.query.agingPeriods ); - this.initContactsAgingPeriods(); - this.calcUnpaidInvoicesAgingPeriods(); } /** @@ -77,8 +75,8 @@ export default class ARAgingSummarySheet extends AgingSummaryReport { * @param {ICustomer[]} customers * @return {IARAgingSummaryCustomer[]} */ - private customersWalker(): IARAgingSummaryCustomer[] { - return this.contacts + private customersWalker(customers: ICustomer[]): IARAgingSummaryCustomer[] { + return customers .map((customer) => this.customerData(customer)) .filter( (customer: IARAgingSummaryCustomer) => @@ -91,9 +89,12 @@ export default class ARAgingSummarySheet extends AgingSummaryReport { * @return {IARAgingSummaryData} */ public reportData(): IARAgingSummaryData { + const customersAgingPeriods = this.customersWalker(this.contacts); + const totalAgingPeriods = this.getTotalAgingPeriods(customersAgingPeriods); + return { - customers: this.customersWalker(), - total: this.getTotalAgingPeriods(), + customers: customersAgingPeriods, + total: totalAgingPeriods, }; } diff --git a/server/src/services/FinancialStatements/AgingSummary/AgingReport.ts b/server/src/services/FinancialStatements/AgingSummary/AgingReport.ts index 197c4fbff..17bad96c5 100644 --- a/server/src/services/FinancialStatements/AgingSummary/AgingReport.ts +++ b/server/src/services/FinancialStatements/AgingSummary/AgingReport.ts @@ -13,7 +13,7 @@ export default abstract class AgingReport extends FinancialSheet{ * @param {number} agingPeriodsFreq */ agingRangePeriods( - asDay: string, + asDay: Date|string, agingDaysBefore: number, agingPeriodsFreq: number ): IAgingPeriod[] { diff --git a/server/src/services/FinancialStatements/AgingSummary/AgingSummary.ts b/server/src/services/FinancialStatements/AgingSummary/AgingSummary.ts index bc7eba449..91bcb5b77 100644 --- a/server/src/services/FinancialStatements/AgingSummary/AgingSummary.ts +++ b/server/src/services/FinancialStatements/AgingSummary/AgingSummary.ts @@ -1,10 +1,10 @@ -import moment from 'moment'; -import { defaultTo } from 'lodash'; +import { defaultTo, sumBy } from 'lodash'; import { IAgingPeriod, ISaleInvoice, IBill, IAgingPeriodTotal, + IARAgingSummaryCustomer, IContact, } from 'interfaces'; import AgingReport from './AgingReport'; @@ -15,7 +15,7 @@ export default abstract class AgingSummaryReport extends AgingReport { protected readonly agingPeriods: IAgingPeriod[] = []; protected readonly baseCurrency: string; protected readonly unpaidInvoices: (ISaleInvoice | IBill)[]; - readonly unpaidInvoicesByContactId: Dictionary< + protected readonly unpaidInvoicesByContactId: Dictionary< (ISaleInvoice | IBill)[] >; protected periodsByContactId: { @@ -26,13 +26,11 @@ export default abstract class AgingSummaryReport extends AgingReport { * Setes initial aging periods to the given customer id. * @param {number} customerId - Customer id. */ - protected setInitialAgingPeriods(contactId: number): void { - this.periodsByContactId[contactId] = this.agingPeriods.map( - (agingPeriod) => ({ - ...agingPeriod, - ...this.formatTotalAmount(0), - }) - ); + protected getInitialAgingPeriodsTotal() { + return this.agingPeriods.map((agingPeriod) => ({ + ...agingPeriod, + ...this.formatTotalAmount(0), + })); } /** @@ -43,29 +41,34 @@ export default abstract class AgingSummaryReport extends AgingReport { protected getContactAgingPeriods( contactId: number ): (IAgingPeriod & IAgingPeriodTotal)[] { - return defaultTo(this.periodsByContactId[contactId], []); + const unpaidInvoices = this.getUnpaidInvoicesByContactId(contactId); + const initialAgingPeriods = this.getInitialAgingPeriodsTotal(); + + return unpaidInvoices.reduce((agingPeriods, unpaidInvoice) => { + const newAgingPeriods = this.getContactAgingDueAmount( + agingPeriods, + unpaidInvoice.dueAmount, + unpaidInvoice.overdueDays + ); + return newAgingPeriods; + }, initialAgingPeriods); } /** - * Sets the customer aging due amount to the table. + * Sets the customer aging due amount to the table. (Xx) * @param {number} customerId - Customer id. * @param {number} dueAmount - Due amount. * @param {number} overdueDays - Overdue days. */ - protected setContactAgingDueAmount( - customerId: number, + protected getContactAgingDueAmount( + agingPeriods: any, dueAmount: number, overdueDays: number - ): void { - if (!this.periodsByContactId[customerId]) { - this.setInitialAgingPeriods(customerId); - } - const agingPeriods = this.periodsByContactId[customerId]; - + ): (IAgingPeriod & IAgingPeriodTotal)[] { const newAgingPeriods = agingPeriods.map((agingPeriod) => { const isInAgingPeriod = - agingPeriod.beforeDays < overdueDays && - agingPeriod.toDays > overdueDays; + agingPeriod.beforeDays <= overdueDays && + (agingPeriod.toDays > overdueDays || !agingPeriod.toDays); return { ...agingPeriod, @@ -74,11 +77,11 @@ export default abstract class AgingSummaryReport extends AgingReport { : agingPeriod.total, }; }); - this.periodsByContactId[customerId] = newAgingPeriods; + return newAgingPeriods; } /** - * Retrieve the aging period total object. + * Retrieve the aging period total object. (xx) * @param {number} amount * @return {IAgingPeriodTotal} */ @@ -95,26 +98,21 @@ export default abstract class AgingSummaryReport extends AgingReport { * @param {number} index * @return {number} */ - protected getTotalAgingPeriodByIndex(index: number): number { + protected getTotalAgingPeriodByIndex( + contactsAgingPeriods: any, + index: number + ): number { return this.contacts.reduce((acc, customer) => { - const periods = this.getContactAgingPeriods(customer.id); - const totalPeriod = periods[index] ? periods[index].total : 0; + const totalPeriod = contactsAgingPeriods[index] + ? contactsAgingPeriods[index].total + : 0; return acc + totalPeriod; }, 0); } /** - * Sets the initial aging periods to the all customers. - */ - protected initContactsAgingPeriods(): void { - this.contacts.forEach((contact) => { - this.setInitialAgingPeriods(contact.id); - }); - } - - /** - * Retrieve the due invoices by the given customer id. + * Retrieve the due invoices by the given customer id. (XX) * @param {number} customerId - * @return {ISaleInvoice[]} */ @@ -128,9 +126,11 @@ export default abstract class AgingSummaryReport extends AgingReport { * Retrieve total aging periods of the report. * @return {(IAgingPeriodTotal & IAgingPeriod)[]} */ - protected getTotalAgingPeriods(): (IAgingPeriodTotal & IAgingPeriod)[] { + protected getTotalAgingPeriods( + contactsAgingPeriods: IARAgingSummaryCustomer[] + ): (IAgingPeriodTotal & IAgingPeriod)[] { return this.agingPeriods.map((agingPeriod, index) => { - const total = this.getTotalAgingPeriodByIndex(index); + const total = sumBy(contactsAgingPeriods, `aging[${index}].total`); return { ...agingPeriod, @@ -138,21 +138,4 @@ export default abstract class AgingSummaryReport extends AgingReport { }; }); } - - /** - * Sets customers invoices to aging periods. - */ - protected calcUnpaidInvoicesAgingPeriods(): void { - this.contacts.forEach((contact) => { - const unpaidInvoices = this.getUnpaidInvoicesByContactId(contact.id); - - unpaidInvoices.forEach((unpaidInvoice) => { - this.setContactAgingDueAmount( - contact.id, - unpaidInvoice.dueAmount, - unpaidInvoice.overdueDays - ); - }); - }); - } }