From 4e66d1ac98e660ed830e16dda78bf164d650da96 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Thu, 24 Aug 2023 23:24:05 +0200 Subject: [PATCH 1/5] feat(server): AP/AR aging summary table transformer --- .../FinancialStatements/APAgingSummary.ts | 44 +++- .../FinancialStatements/ARAgingSummary.ts | 41 +++- .../src/interfaces/APAgingSummaryReport.ts | 53 ++--- .../src/interfaces/ARAgingSummaryReport.ts | 37 ++-- packages/server/src/interfaces/AgingReport.ts | 24 ++- .../AgingSummary/APAgingSummaryService.ts | 21 +- .../AgingSummary/APAgingSummaryTable.ts | 46 ++++ .../AgingSummary/ARAgingSummaryService.ts | 23 +- .../AgingSummary/ARAgingSummarySheet.ts | 5 +- .../AgingSummary/ARAgingSummaryTable.ts | 38 ++++ .../AgingSummary/AgingSummaryTable.ts | 203 ++++++++++++++++++ .../AgingSummary/_constants.ts | 4 + .../ApplyVendorCreditSyncBills.ts | 8 +- 13 files changed, 459 insertions(+), 88 deletions(-) create mode 100644 packages/server/src/services/FinancialStatements/AgingSummary/APAgingSummaryTable.ts create mode 100644 packages/server/src/services/FinancialStatements/AgingSummary/ARAgingSummaryTable.ts create mode 100644 packages/server/src/services/FinancialStatements/AgingSummary/AgingSummaryTable.ts create mode 100644 packages/server/src/services/FinancialStatements/AgingSummary/_constants.ts diff --git a/packages/server/src/api/controllers/FinancialStatements/APAgingSummary.ts b/packages/server/src/api/controllers/FinancialStatements/APAgingSummary.ts index 8bd6c1014..cf84377f1 100644 --- a/packages/server/src/api/controllers/FinancialStatements/APAgingSummary.ts +++ b/packages/server/src/api/controllers/FinancialStatements/APAgingSummary.ts @@ -33,10 +33,13 @@ export default class APAgingSummaryReportController extends BaseFinancialReportC return [ ...this.sheetNumberFormatValidationSchema, query('as_date').optional().isISO8601(), - query('aging_days_before').optional().isNumeric().toInt(), - query('aging_periods').optional().isNumeric().toInt(), + + query('aging_days_before').default(30).isNumeric().toInt(), + query('aging_periods').default(3).isNumeric().toInt(), + query('vendors_ids').optional().isArray({ min: 1 }), query('vendors_ids.*').isInt({ min: 1 }).toInt(), + query('none_zero').default(true).isBoolean().toBoolean(), // Filtering by branches. @@ -53,15 +56,36 @@ export default class APAgingSummaryReportController extends BaseFinancialReportC const filter = this.matchedQueryData(req); try { - const { data, columns, query, meta } = - await this.APAgingSummaryService.APAgingSummary(tenantId, filter); + const accept = this.accepts(req); + const acceptType = accept.types(['json', 'application/json+table']); - return res.status(200).send({ - data: this.transfromToResponse(data), - columns: this.transfromToResponse(columns), - query: this.transfromToResponse(query), - meta: this.transfromToResponse(meta), - }); + switch (acceptType) { + case 'application/json+table': + const table = await this.APAgingSummaryService.APAgingSummaryTable( + tenantId, + filter + ); + return res.status(200).send({ + table: { + rows: table.rows, + columns: table.columns, + }, + meta: table.meta, + query: table.query, + }); + break; + default: + const { data, columns, query, meta } = + await this.APAgingSummaryService.APAgingSummary(tenantId, filter); + + return res.status(200).send({ + data: this.transfromToResponse(data), + columns: this.transfromToResponse(columns), + query: this.transfromToResponse(query), + meta: this.transfromToResponse(meta), + }); + break; + } } catch (error) { next(error); } diff --git a/packages/server/src/api/controllers/FinancialStatements/ARAgingSummary.ts b/packages/server/src/api/controllers/FinancialStatements/ARAgingSummary.ts index deb69a172..489eb04ae 100644 --- a/packages/server/src/api/controllers/FinancialStatements/ARAgingSummary.ts +++ b/packages/server/src/api/controllers/FinancialStatements/ARAgingSummary.ts @@ -36,8 +36,8 @@ export default class ARAgingSummaryReportController extends BaseFinancialReportC query('as_date').optional().isISO8601(), - query('aging_days_before').optional().isInt({ max: 500 }).toInt(), - query('aging_periods').optional().isInt({ max: 12 }).toInt(), + query('aging_days_before').default(30).isInt({ max: 500 }).toInt(), + query('aging_periods').default(3).isInt({ max: 12 }).toInt(), query('customers_ids').optional().isArray({ min: 1 }), query('customers_ids.*').isInt({ min: 1 }).toInt(), @@ -58,15 +58,36 @@ export default class ARAgingSummaryReportController extends BaseFinancialReportC const filter = this.matchedQueryData(req); try { - const { data, columns, query, meta } = - await this.ARAgingSummaryService.ARAgingSummary(tenantId, filter); + const accept = this.accepts(req); + const acceptType = accept.types(['json', 'application/json+table']); - return res.status(200).send({ - data: this.transfromToResponse(data), - columns: this.transfromToResponse(columns), - query: this.transfromToResponse(query), - meta: this.transfromToResponse(meta), - }); + switch (acceptType) { + case 'application/json+table': + const table = await this.ARAgingSummaryService.ARAgingSummaryTable( + tenantId, + filter + ); + return res.status(200).send({ + table: { + rows: table.rows, + columns: table.columns, + }, + meta: table.meta, + query: table.query, + }); + break; + default: + const { data, columns, query, meta } = + await this.ARAgingSummaryService.ARAgingSummary(tenantId, filter); + + return res.status(200).send({ + data: this.transfromToResponse(data), + columns: this.transfromToResponse(columns), + query: this.transfromToResponse(query), + meta: this.transfromToResponse(meta), + }); + break; + } } catch (error) { console.log(error); } diff --git a/packages/server/src/interfaces/APAgingSummaryReport.ts b/packages/server/src/interfaces/APAgingSummaryReport.ts index 788892112..db6626167 100644 --- a/packages/server/src/interfaces/APAgingSummaryReport.ts +++ b/packages/server/src/interfaces/APAgingSummaryReport.ts @@ -1,51 +1,36 @@ import { IAgingPeriod, IAgingPeriodTotal, - IAgingAmount + IAgingAmount, + IAgingSummaryQuery, + IAgingSummaryTotal, + IAgingSummaryContact, + IAgingSummaryData, } from './AgingReport'; -import { - INumberFormatQuery -} from './FinancialStatements'; +import { INumberFormatQuery } from './FinancialStatements'; -export interface IAPAgingSummaryQuery { - asDate: Date | string; - agingDaysBefore: number; - agingPeriods: number; - numberFormat: INumberFormatQuery; +export interface IAPAgingSummaryQuery extends IAgingSummaryQuery { vendorsIds: number[]; - noneZero: boolean; - - branchesIds?: number[] } -export interface IAPAgingSummaryVendor { - vendorName: string, - current: IAgingAmount, - aging: IAgingPeriodTotal[], - total: IAgingAmount, -}; +export interface IAPAgingSummaryVendor extends IAgingSummaryContact { + vendorName: string; +} -export interface IAPAgingSummaryTotal { - current: IAgingAmount, - aging: IAgingPeriodTotal[], - total: IAgingAmount, -}; +export interface IAPAgingSummaryTotal extends IAgingSummaryTotal {} -export interface IAPAgingSummaryData { - vendors: IAPAgingSummaryVendor[], - total: IAPAgingSummaryTotal, -}; +export interface IAPAgingSummaryData extends IAgingSummaryData { + vendors: IAPAgingSummaryVendor[]; +} export type IAPAgingSummaryColumns = IAgingPeriod[]; - export interface IARAgingSummaryMeta { - baseCurrency: string, - organizationName: string, + baseCurrency: string; + organizationName: string; } - export interface IAPAgingSummaryMeta { - baseCurrency: string, - organizationName: string, -} \ No newline at end of file + baseCurrency: string; + organizationName: string; +} diff --git a/packages/server/src/interfaces/ARAgingSummaryReport.ts b/packages/server/src/interfaces/ARAgingSummaryReport.ts index a9d6ff3f5..7d25e2b2c 100644 --- a/packages/server/src/interfaces/ARAgingSummaryReport.ts +++ b/packages/server/src/interfaces/ARAgingSummaryReport.ts @@ -1,37 +1,28 @@ -import { IAgingPeriod, IAgingPeriodTotal, IAgingAmount } from './AgingReport'; -import { INumberFormatQuery } from './FinancialStatements'; +import { + IAgingPeriod, + IAgingSummaryQuery, + IAgingSummaryTotal, + IAgingSummaryContact, + IAgingSummaryData, +} from './AgingReport'; -export interface IARAgingSummaryQuery { - asDate: Date | string; - agingDaysBefore: number; - agingPeriods: number; - numberFormat: INumberFormatQuery; +export interface IARAgingSummaryQuery extends IAgingSummaryQuery { customersIds: number[]; - branchesIds: number[]; - noneZero: boolean; } -export interface IARAgingSummaryCustomer { +export interface IARAgingSummaryCustomer extends IAgingSummaryContact { customerName: string; - current: IAgingAmount; - aging: IAgingPeriodTotal[]; - total: IAgingAmount; } -export interface IARAgingSummaryTotal { - current: IAgingAmount; - aging: IAgingPeriodTotal[]; - total: IAgingAmount; -} +export interface IARAgingSummaryTotal extends IAgingSummaryTotal {} -export interface IARAgingSummaryData { +export interface IARAgingSummaryData extends IAgingSummaryData { customers: IARAgingSummaryCustomer[]; - total: IARAgingSummaryTotal; } export type IARAgingSummaryColumns = IAgingPeriod[]; export interface IARAgingSummaryMeta { - organizationName: string, - baseCurrency: string, -} \ No newline at end of file + organizationName: string; + baseCurrency: string; +} diff --git a/packages/server/src/interfaces/AgingReport.ts b/packages/server/src/interfaces/AgingReport.ts index 65983d44f..c68b6b389 100644 --- a/packages/server/src/interfaces/AgingReport.ts +++ b/packages/server/src/interfaces/AgingReport.ts @@ -1,6 +1,9 @@ + +import { INumberFormatQuery } from './FinancialStatements'; + export interface IAgingPeriodTotal extends IAgingPeriod { total: IAgingAmount; -}; +} export interface IAgingAmount { amount: number; @@ -20,3 +23,22 @@ export interface IAgingSummaryContact { aging: IAgingPeriodTotal[]; total: IAgingAmount; } + +export interface IAgingSummaryQuery { + asDate: Date | string; + agingDaysBefore: number; + agingPeriods: number; + numberFormat: INumberFormatQuery; + branchesIds: number[]; + noneZero: boolean; +} + +export interface IAgingSummaryTotal { + current: IAgingAmount; + aging: IAgingPeriodTotal[]; + total: IAgingAmount; +} + +export interface IAgingSummaryData { + total: IAgingSummaryTotal; +} diff --git a/packages/server/src/services/FinancialStatements/AgingSummary/APAgingSummaryService.ts b/packages/server/src/services/FinancialStatements/AgingSummary/APAgingSummaryService.ts index ad85b1688..a2589745d 100644 --- a/packages/server/src/services/FinancialStatements/AgingSummary/APAgingSummaryService.ts +++ b/packages/server/src/services/FinancialStatements/AgingSummary/APAgingSummaryService.ts @@ -5,6 +5,7 @@ import TenancyService from '@/services/Tenancy/TenancyService'; import APAgingSummarySheet from './APAgingSummarySheet'; import { Tenant } from '@/system/models'; import { isEmpty } from 'lodash'; +import APAgingSummaryTable from './APAgingSummaryTable'; @Service() export default class PayableAgingSummaryService { @@ -84,7 +85,7 @@ export default class PayableAgingSummaryService { // Common query. const commonQuery = (query) => { - if (isEmpty(filter.branchesIds)) { + if (!isEmpty(filter.branchesIds)) { query.modify('filterByBranches', filter.branchesIds); } }; @@ -118,4 +119,22 @@ export default class PayableAgingSummaryService { meta: this.reportMetadata(tenantId), }; } + + /** + * + * @param {number} tenantId + * @param {IAPAgingSummaryQuery} query + * @returns + */ + async APAgingSummaryTable(tenantId: number, query: IAPAgingSummaryQuery) { + const report = await this.APAgingSummary(tenantId, query); + const table = new APAgingSummaryTable(report.data, query, {}); + + return { + columns: table.tableColumns(), + rows: table.tableRows(), + meta: report.meta, + query: report.query, + }; + } } diff --git a/packages/server/src/services/FinancialStatements/AgingSummary/APAgingSummaryTable.ts b/packages/server/src/services/FinancialStatements/AgingSummary/APAgingSummaryTable.ts new file mode 100644 index 000000000..b74e748d7 --- /dev/null +++ b/packages/server/src/services/FinancialStatements/AgingSummary/APAgingSummaryTable.ts @@ -0,0 +1,46 @@ +import { + IAPAgingSummaryData, + IAgingSummaryQuery, + ITableColumn, + ITableColumnAccessor, + ITableRow, +} from '@/interfaces'; +import AgingSummaryTable from './AgingSummaryTable'; + +export default class APAgingSummaryTable extends AgingSummaryTable { + readonly report: IAPAgingSummaryData; + + /** + * Constructor method. + * @param {IARAgingSummaryData} data + * @param {IAgingSummaryQuery} query + * @param {any} i18n + */ + constructor(data: IAPAgingSummaryData, query: IAgingSummaryQuery, i18n: any) { + super(data, query, i18n); + } + + /** + * Retrieves the contacts table rows. + * @returns {ITableRow[]} + */ + get contactsRows(): ITableRow[] { + return this.contactsNodes(this.report.vendors); + } + + /** + * Contact name node accessor. + * @returns {ITableColumnAccessor} + */ + get contactNameNodeAccessor(): ITableColumnAccessor { + return { key: 'vendor_name', accessor: 'vendorName' }; + } + + /** + * Retrieves the contact name table column. + * @returns {ITableColumn} + */ + contactNameTableColumn = (): ITableColumn => { + return { label: 'Vendor name', key: 'vendor_name' }; + }; +} diff --git a/packages/server/src/services/FinancialStatements/AgingSummary/ARAgingSummaryService.ts b/packages/server/src/services/FinancialStatements/AgingSummary/ARAgingSummaryService.ts index b1a5764af..b47bd50a4 100644 --- a/packages/server/src/services/FinancialStatements/AgingSummary/ARAgingSummaryService.ts +++ b/packages/server/src/services/FinancialStatements/AgingSummary/ARAgingSummaryService.ts @@ -5,6 +5,7 @@ import { IARAgingSummaryQuery, IARAgingSummaryMeta } from '@/interfaces'; import TenancyService from '@/services/Tenancy/TenancyService'; import ARAgingSummarySheet from './ARAgingSummarySheet'; import { Tenant } from '@/system/models'; +import ARAgingSummaryTable from './ARAgingSummaryTable'; @Service() export default class ARAgingSummaryService { @@ -89,12 +90,12 @@ export default class ARAgingSummaryService { }; // Retrieve all overdue sale invoices. const overdueSaleInvoices = await SaleInvoice.query() - .modify('dueInvoicesFromDate', filter.asDate) + .modify('overdueInvoicesFromDate', filter.asDate) .onBuild(commonQuery); // Retrieve all due sale invoices. const currentInvoices = await SaleInvoice.query() - .modify('overdueInvoicesFromDate', filter.asDate) + .modify('dueInvoicesFromDate', filter.asDate) .onBuild(commonQuery); // AR aging summary report instance. @@ -117,4 +118,22 @@ export default class ARAgingSummaryService { meta: this.reportMetadata(tenantId), }; } + + /** + * + * @param tenantId + * @param query + * @returns + */ + async ARAgingSummaryTable(tenantId: number, query: IARAgingSummaryQuery) { + const report = await this.ARAgingSummary(tenantId, query); + const table = new ARAgingSummaryTable(report.data, query, {}); + + return { + columns: table.tableColumns(), + rows: table.tableRows(), + meta: report.meta, + query, + }; + } } diff --git a/packages/server/src/services/FinancialStatements/AgingSummary/ARAgingSummarySheet.ts b/packages/server/src/services/FinancialStatements/AgingSummary/ARAgingSummarySheet.ts index 0dba17a1e..a9f1856c2 100644 --- a/packages/server/src/services/FinancialStatements/AgingSummary/ARAgingSummarySheet.ts +++ b/packages/server/src/services/FinancialStatements/AgingSummary/ARAgingSummarySheet.ts @@ -1,4 +1,4 @@ -import { groupBy, isEmpty, sum } from 'lodash'; +import { Dictionary, groupBy, isEmpty, sum } from 'lodash'; import * as R from 'ramda'; import { ICustomer, @@ -54,7 +54,6 @@ export default class ARAgingSummarySheet extends AgingSummaryReport { currentSaleInvoices, 'customerId' ); - // Initializes the aging periods. this.agingPeriods = this.agingRangePeriods( this.query.asDate, @@ -189,7 +188,7 @@ export default class ARAgingSummarySheet extends AgingSummaryReport { }; /** - * Retrieve AR aging summary report columns. + * Retrieve A/R aging summary report columns. * @return {IARAgingSummaryColumns} */ public reportColumns(): IARAgingSummaryColumns { diff --git a/packages/server/src/services/FinancialStatements/AgingSummary/ARAgingSummaryTable.ts b/packages/server/src/services/FinancialStatements/AgingSummary/ARAgingSummaryTable.ts new file mode 100644 index 000000000..5e0ad88b3 --- /dev/null +++ b/packages/server/src/services/FinancialStatements/AgingSummary/ARAgingSummaryTable.ts @@ -0,0 +1,38 @@ +import { + IARAgingSummaryData, + IAgingSummaryData, + IAgingSummaryQuery, + ITableColumnAccessor, + ITableRow, +} from '@/interfaces'; +import AgingSummaryTable from './AgingSummaryTable'; + +export default class ARAgingSummaryTable extends AgingSummaryTable { + readonly report: IARAgingSummaryData; + + /** + * Constructor method. + * @param {IARAgingSummaryData} data + * @param {IAgingSummaryQuery} query + * @param {any} i18n + */ + constructor(data: IARAgingSummaryData, query: IAgingSummaryQuery, i18n: any) { + super(data, query, i18n); + } + + /** + * Retrieves the contacts table rows. + * @returns {ITableRow[]} + */ + get contactsRows(): ITableRow[] { + return this.contactsNodes(this.report.customers); + } + + /** + * Contact name node accessor. + * @returns {ITableColumnAccessor} + */ + get contactNameNodeAccessor(): ITableColumnAccessor { + return { key: 'customer_name', accessor: 'customerName' }; + } +} diff --git a/packages/server/src/services/FinancialStatements/AgingSummary/AgingSummaryTable.ts b/packages/server/src/services/FinancialStatements/AgingSummary/AgingSummaryTable.ts new file mode 100644 index 000000000..a318cdfc4 --- /dev/null +++ b/packages/server/src/services/FinancialStatements/AgingSummary/AgingSummaryTable.ts @@ -0,0 +1,203 @@ +import * as R from 'ramda'; +import { + IAgingPeriod, + IAgingSummaryContact, + IAgingSummaryData, + IAgingSummaryQuery, + IAgingSummaryTotal, + ITableColumn, + ITableColumnAccessor, + ITableRow, +} from '@/interfaces'; +import { tableRowMapper } from '@/utils'; +import AgingReport from './AgingReport'; +import { AgingSummaryRowType } from './_constants'; + +export default abstract class AgingSummaryTable extends AgingReport { + protected readonly report: IAgingSummaryData; + protected readonly query: IAgingSummaryQuery; + protected readonly agingPeriods: IAgingPeriod[]; + protected readonly i18n: any; + + /** + * Constructor method. + * @param {IARAgingSummaryData} data + * @param {IAgingSummaryQuery} query + * @param {any} i18n + */ + constructor(data: IAgingSummaryData, query: IAgingSummaryQuery, i18n: any) { + super(); + + this.report = data; + this.i18n = i18n; + this.query = query; + + this.agingPeriods = this.agingRangePeriods( + this.query.asDate, + this.query.agingDaysBefore, + this.query.agingPeriods + ); + } + + // ------------------------- + // # Accessors. + // ------------------------- + /** + * Aging accessors of contact and total nodes. + * @param {IAgingSummaryContact | IAgingSummaryTotal} node + * @returns {ITableColumnAccessor[]} + */ + protected agingNodeAccessors = ( + node: IAgingSummaryContact | IAgingSummaryTotal + ): ITableColumnAccessor[] => { + return node.aging.map((aging, index) => ({ + key: 'aging', + accessor: `aging[${index}].total.formattedAmount`, + })); + }; + + /** + * Contact name node accessor. + * @returns {ITableColumnAccessor} + */ + protected get contactNameNodeAccessor(): ITableColumnAccessor { + return { key: 'customer_name', accessor: 'customerName' }; + } + + /** + * Retrieves the common columns for all report nodes. + * @param {IAgingSummaryContact} + * @returns {ITableColumnAccessor[]} + */ + protected contactNodeAccessors = ( + node: IAgingSummaryContact + ): ITableColumnAccessor[] => { + return R.compose( + R.concat([ + this.contactNameNodeAccessor, + { key: 'current', accessor: 'current.formattedAmount' }, + ...this.agingNodeAccessors(node), + { key: 'total', accessor: 'total.formattedAmount' }, + ]) + )([]); + }; + + /** + * Retrieves the contact name table row. + * @param {IAgingSummaryContact} node - + * @return {ITableRow} + */ + protected contactNameNode = (node: IAgingSummaryContact): ITableRow => { + const columns = this.contactNodeAccessors(node); + const meta = { + rowTypes: [AgingSummaryRowType.Contact], + }; + return tableRowMapper(node, columns, meta); + }; + + /** + * Maps the customers nodes to table rows. + * @param {IAgingSummaryContact[]} nodes + * @returns {ITableRow[]} + */ + protected contactsNodes = (nodes: IAgingSummaryContact[]): ITableRow[] => { + return nodes.map(this.contactNameNode); + }; + + /** + * Retrieves the common columns for all report nodes. + * @param {IAgingSummaryTotal} + * @returns {ITableColumnAccessor[]} + */ + protected totalNodeAccessors = ( + node: IAgingSummaryTotal + ): ITableColumnAccessor[] => { + return R.compose( + R.concat([ + { key: 'blank', value: '' }, + { key: 'current', accessor: 'current.formattedAmount' }, + ...this.agingNodeAccessors(node), + { key: 'total', accessor: 'total.formattedAmount' }, + ]) + )([]); + }; + + /** + * Retrieves the total row of the given report total node. + * @param {IAgingSummaryTotal} node + * @returns {ITableRow} + */ + protected totalNode = (node: IAgingSummaryTotal): ITableRow => { + const columns = this.totalNodeAccessors(node); + const meta = { + rowTypes: [AgingSummaryRowType.Total], + }; + return tableRowMapper(node, columns, meta); + }; + + // ------------------------- + // # Computed Rows. + // ------------------------- + /** + * Retrieves the contacts table rows. + * @returns {ITableRow[]} + */ + protected get contactsRows(): ITableRow[] { + return []; + } + + /** + * Table total row. + * @returns {ITableRow} + */ + protected get totalRow(): ITableRow { + return this.totalNode(this.report.total); + } + + /** + * Retrieves the table rows. + * @returns {ITableRow[]} + */ + public tableRows = (): ITableRow[] => { + return R.compose(R.concat(this.contactsRows), R.prepend(this.totalRow))([]); + }; + + // ------------------------- + // # Columns. + // ------------------------- + /** + * Retrieves the aging table columns. + * @returns {ITableColumn[]} + */ + protected agingTableColumns = (): ITableColumn[] => { + return this.agingPeriods.map((agingPeriod) => { + return { + label: `${agingPeriod.beforeDays} - ${ + agingPeriod.toDays || 'And Over' + }`, + key: 'aging_period', + }; + }); + }; + + /** + * Retrieves the contact name table column. + * @returns {ITableColumn} + */ + protected contactNameTableColumn = (): ITableColumn => { + return { label: 'Customer name', key: 'customer_name' }; + }; + + /** + * Retrieves the report columns. + * @returns {ITableColumn} + */ + public tableColumns = (): ITableColumn[] => { + return [ + this.contactNameTableColumn(), + { label: 'Current', key: 'current' }, + ...this.agingTableColumns(), + { label: 'Total', key: 'total' }, + ]; + }; +} diff --git a/packages/server/src/services/FinancialStatements/AgingSummary/_constants.ts b/packages/server/src/services/FinancialStatements/AgingSummary/_constants.ts new file mode 100644 index 000000000..961f0b7ed --- /dev/null +++ b/packages/server/src/services/FinancialStatements/AgingSummary/_constants.ts @@ -0,0 +1,4 @@ +export enum AgingSummaryRowType { + Contact = 'contact', + Total = 'total', +} diff --git a/packages/server/src/services/Purchases/VendorCredits/ApplyVendorCreditToBills/ApplyVendorCreditSyncBills.ts b/packages/server/src/services/Purchases/VendorCredits/ApplyVendorCreditToBills/ApplyVendorCreditSyncBills.ts index f9a86c663..c6f617c60 100644 --- a/packages/server/src/services/Purchases/VendorCredits/ApplyVendorCreditToBills/ApplyVendorCreditSyncBills.ts +++ b/packages/server/src/services/Purchases/VendorCredits/ApplyVendorCreditToBills/ApplyVendorCreditSyncBills.ts @@ -1,13 +1,13 @@ import { Service, Inject } from 'typedi'; -import Knex from 'knex'; +import { Knex } from 'knex'; +import Bluebird from 'bluebird'; import { IVendorCreditAppliedBill } from '@/interfaces'; import HasTenancyService from '@/services/Tenancy/TenancyService'; -import Bluebird from 'bluebird'; @Service() export default class ApplyVendorCreditSyncBills { @Inject() - tenancy: HasTenancyService; + private tenancy: HasTenancyService; /** * Increment bills credited amount. @@ -49,4 +49,4 @@ export default class ApplyVendorCreditSyncBills { .findById(vendorCreditAppliedBill.billId) .decrement('creditedAmount', vendorCreditAppliedBill.amount); }; -} \ No newline at end of file +} From 321de4d327e16c5f48bebec23e0cb03fac031d07 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Sat, 26 Aug 2023 01:54:33 +0200 Subject: [PATCH 2/5] refactor(webapp): AR/AP aging summary table columns --- .../AgingSummary/AgingSummaryTable.ts | 11 ++- .../FinancialStatements/FinancialTable.ts | 2 +- .../APAgingSummary/APAgingSummaryBody.tsx | 4 +- .../APAgingSummary/APAgingSummaryTable.tsx | 22 ++++- .../APAgingSummary/components.tsx | 93 +++++++++++-------- .../ARAgingSummary/ARAgingSummaryTable.tsx | 7 +- .../ARAgingSummary/components.tsx | 57 +----------- .../AgingSummary/dynamicColumns.ts | 74 +++++++++++++++ .../src/hooks/query/financialReports.tsx | 44 ++------- 9 files changed, 174 insertions(+), 140 deletions(-) create mode 100644 packages/webapp/src/containers/FinancialStatements/AgingSummary/dynamicColumns.ts diff --git a/packages/server/src/services/FinancialStatements/AgingSummary/AgingSummaryTable.ts b/packages/server/src/services/FinancialStatements/AgingSummary/AgingSummaryTable.ts index a318cdfc4..49a3f140b 100644 --- a/packages/server/src/services/FinancialStatements/AgingSummary/AgingSummaryTable.ts +++ b/packages/server/src/services/FinancialStatements/AgingSummary/AgingSummaryTable.ts @@ -12,8 +12,13 @@ import { import { tableRowMapper } from '@/utils'; import AgingReport from './AgingReport'; import { AgingSummaryRowType } from './_constants'; +import { FinancialTable } from '../FinancialTable'; +import { FinancialSheetStructure } from '../FinancialSheetStructure'; -export default abstract class AgingSummaryTable extends AgingReport { +export default abstract class AgingSummaryTable extends R.compose( + FinancialSheetStructure, + FinancialTable +)(AgingReport) { protected readonly report: IAgingSummaryData; protected readonly query: IAgingSummaryQuery; protected readonly agingPeriods: IAgingPeriod[]; @@ -193,11 +198,11 @@ export default abstract class AgingSummaryTable extends AgingReport { * @returns {ITableColumn} */ public tableColumns = (): ITableColumn[] => { - return [ + return R.compose(this.tableColumnsCellIndexing)([ this.contactNameTableColumn(), { label: 'Current', key: 'current' }, ...this.agingTableColumns(), { label: 'Total', key: 'total' }, - ]; + ]); }; } diff --git a/packages/server/src/services/FinancialStatements/FinancialTable.ts b/packages/server/src/services/FinancialStatements/FinancialTable.ts index 20ae528a2..0a7eafd01 100644 --- a/packages/server/src/services/FinancialStatements/FinancialTable.ts +++ b/packages/server/src/services/FinancialStatements/FinancialTable.ts @@ -12,7 +12,7 @@ export const FinancialTable = (Base) => * @param {ITableColumn[]} columns * @returns {ITableColumn[]} */ - protected tableColumnsCellIndexing = ( + public tableColumnsCellIndexing = ( columns: ITableColumn[] ): ITableColumn[] => { const cellIndex = increment(-1); diff --git a/packages/webapp/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryBody.tsx b/packages/webapp/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryBody.tsx index c49642106..366404561 100644 --- a/packages/webapp/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryBody.tsx +++ b/packages/webapp/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryBody.tsx @@ -18,11 +18,11 @@ function APAgingSummaryBodyJSX({ // #withCurrentOrganization organizationName, }) { - const { isLoading } = useAPAgingSummaryContext(); + const { isAPAgingLoading } = useAPAgingSummaryContext(); return ( - {isLoading ? ( + {isAPAgingLoading ? ( ) : ( diff --git a/packages/webapp/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryTable.tsx b/packages/webapp/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryTable.tsx index a54f25a09..96924e97b 100644 --- a/packages/webapp/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryTable.tsx +++ b/packages/webapp/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryTable.tsx @@ -20,7 +20,7 @@ export default function APAgingSummaryTable({ }) { // AP aging summary report content. const { - APAgingSummary: { tableRows }, + APAgingSummary: { table }, isAPAgingLoading, } = useAPAgingSummaryContext(); @@ -36,7 +36,7 @@ export default function APAgingSummaryTable({ > { const { - APAgingSummary: { tableRows, columns }, + APAgingSummary: { table }, } = useAPAgingSummaryContext(); - const agingColumns = React.useMemo(() => { - return columns.map( - (agingColumn) => - `${agingColumn.before_days} - ${ - agingColumn.to_days || intl.get('and_over') - }`, - ); - }, [columns]); - - return useMemo( - () => [ - { - Header: , - accessor: 'name', - className: 'vendor_name', - width: 240, - sticky: 'left', - textOverview: true, - }, - { - Header: , - accessor: 'current', - className: 'current', - width: getColumnWidth(tableRows, `current`, { minWidth: 120 }), - }, - ...agingColumns.map((agingColumn, index) => ({ - Header: agingColumn, - accessor: `aging-${index}`, - width: getColumnWidth(tableRows, `aging-${index}`, { minWidth: 120 }), - })), - { - Header: , - accessor: 'total', - width: getColumnWidth(tableRows, 'total', { minWidth: 120 }), - }, - ], - [tableRows, agingColumns], - ); + return agingSummaryDynamicColumns(table.columns, table.rows); }; +/** + * Retrieve AP aging summary columns. + */ +// export const useAPAgingSummaryColumns = () => { +// const { +// APAgingSummary: { tableRows, columns }, +// } = useAPAgingSummaryContext(); + +// const agingColumns = React.useMemo(() => { +// return columns.map( +// (agingColumn) => +// `${agingColumn.before_days} - ${ +// agingColumn.to_days || intl.get('and_over') +// }`, +// ); +// }, [columns]); + +// return useMemo( +// () => [ +// { +// Header: , +// accessor: 'name', +// className: 'vendor_name', +// width: 240, +// sticky: 'left', +// textOverview: true, +// }, +// { +// Header: , +// accessor: 'current', +// className: 'current', +// width: getColumnWidth(tableRows, `current`, { minWidth: 120 }), +// }, +// ...agingColumns.map((agingColumn, index) => ({ +// Header: agingColumn, +// accessor: `aging-${index}`, +// width: getColumnWidth(tableRows, `aging-${index}`, { minWidth: 120 }), +// })), +// { +// Header: , +// accessor: 'total', +// width: getColumnWidth(tableRows, 'total', { minWidth: 120 }), +// }, +// ], +// [tableRows, agingColumns], +// ); +// }; + /** * A/P aging summary sheet loading bar. */ diff --git a/packages/webapp/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummaryTable.tsx b/packages/webapp/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummaryTable.tsx index 11f1dfa19..d2cdf5baa 100644 --- a/packages/webapp/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummaryTable.tsx +++ b/packages/webapp/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummaryTable.tsx @@ -19,7 +19,10 @@ export default function ReceivableAgingSummaryTable({ organizationName, }) { // AR aging summary report context. - const { ARAgingSummary, isARAgingLoading } = useARAgingSummaryContext(); + const { + ARAgingSummary: { table }, + isARAgingLoading, + } = useARAgingSummaryContext(); // AR aging summary columns. const columns = useARAgingSummaryColumns(); @@ -33,7 +36,7 @@ export default function ReceivableAgingSummaryTable({ > { const { - ARAgingSummary: { tableRows, columns }, + ARAgingSummary: { table }, } = useARAgingSummaryContext(); - const agingColumns = React.useMemo(() => { - return columns.map( - (agingColumn) => - `${agingColumn.before_days} - ${ - agingColumn.to_days || intl.get('and_over') - }`, - ); - }, [columns]); - - return React.useMemo( - () => [ - { - Header: , - accessor: 'name', - className: 'customer_name', - sticky: 'left', - width: 240, - textOverview: true, - }, - { - Header: , - accessor: 'current', - className: 'current', - width: getColumnWidth(tableRows, `current`, { - minWidth: 120, - }), - align: Align.Right - }, - ...agingColumns.map((agingColumn, index) => ({ - Header: agingColumn, - accessor: `aging-${index}`, - width: getColumnWidth(tableRows, `aging-${index}`, { - minWidth: 120, - }), - align: Align.Right - })), - { - Header: , - id: 'total', - accessor: 'total', - className: 'total', - width: getColumnWidth(tableRows, 'total', { - minWidth: 120, - }), - align: Align.Right - }, - ], - [tableRows, agingColumns], - ); + return agingSummaryDynamicColumns(table.columns, table.rows); }; /** diff --git a/packages/webapp/src/containers/FinancialStatements/AgingSummary/dynamicColumns.ts b/packages/webapp/src/containers/FinancialStatements/AgingSummary/dynamicColumns.ts new file mode 100644 index 000000000..971bdadb7 --- /dev/null +++ b/packages/webapp/src/containers/FinancialStatements/AgingSummary/dynamicColumns.ts @@ -0,0 +1,74 @@ +// @ts-nocheck +import React, { useMemo } from 'react'; +import * as R from 'ramda'; +import { getColumnWidth } from '@/utils'; +import { Align } from '@/constants'; + +const getTableCellValueAccessor = (index) => `cells[${index}].value`; + +const contactNameAccessor = R.curry((data, column) => ({ + key: column.key, + Header: column.label, + accessor: getTableCellValueAccessor(column.cell_index), + sticky: 'left', + width: 240, + textOverview: true, +})); + +const currentAccessor = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + + return { + key: column.key, + Header: column.label, + accessor, + className: column.id, + width: getColumnWidth(data, accessor, { minWidth: 120 }), + align: Align.Right, + }; +}); + +const totalAccessor = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + + return { + Header: column.label, + id: column.key, + accessor: getTableCellValueAccessor(column.cell_index), + className: column.key, + width: getColumnWidth(data, accessor, { minWidth: 120 }), + align: Align.Right, + }; +}); + +const agingPeriodAccessor = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + + return { + Header: column.label, + id: `${column.key}-${column.cell_index}`, + accessor, + className: column.key, + width: getColumnWidth(data, accessor, { minWidth: 120 }), + align: Align.Right, + }; +}); + +const dynamicColumnMapper = R.curry((data, column) => { + const totalAccessorColumn = totalAccessor(data); + const currentAccessorColumn = currentAccessor(data); + const customerNameAccessorColumn = contactNameAccessor(data); + const agingPeriodAccessorColumn = agingPeriodAccessor(data); + + return R.compose( + R.when(R.pathEq(['key'], 'total'), totalAccessorColumn), + R.when(R.pathEq(['key'], 'current'), currentAccessorColumn), + R.when(R.pathEq(['key'], 'customer_name'), customerNameAccessorColumn), + R.when(R.pathEq(['key'], 'vendor_name'), customerNameAccessorColumn), + R.when(R.pathEq(['key'], 'aging_period'), agingPeriodAccessorColumn), + )(column); +}); + +export const agingSummaryDynamicColumns = (columns, data) => { + return R.map(dynamicColumnMapper(data), columns); +}; diff --git a/packages/webapp/src/hooks/query/financialReports.tsx b/packages/webapp/src/hooks/query/financialReports.tsx index 6a9f385a9..6941fb1d2 100644 --- a/packages/webapp/src/hooks/query/financialReports.tsx +++ b/packages/webapp/src/hooks/query/financialReports.tsx @@ -138,26 +138,12 @@ export function useARAgingSummaryReport(query, props) { method: 'get', url: '/financial_statements/receivable_aging_summary', params: query, + headers: { + Accept: 'application/json+table', + }, }, { - select: (res) => ({ - columns: res.data.columns, - data: res.data.data, - query: res.data.query, - tableRows: ARAgingSummaryTableRowsMapper({ - customers: res.data.data.customers, - total: res.data.data.total, - columns: res.data.columns, - }), - }), - defaultData: { - data: { - customers: [], - total: {}, - }, - columns: [], - tableRows: [], - }, + select: (res) => res.data, ...props, }, ); @@ -173,26 +159,12 @@ export function useAPAgingSummaryReport(query, props) { method: 'get', url: '/financial_statements/payable_aging_summary', params: query, + headers: { + Accept: 'application/json+table', + }, }, { - select: (res) => ({ - columns: res.data.columns, - data: res.data.data, - query: res.data.query, - tableRows: APAgingSummaryTableRowsMapper({ - vendors: res.data.data.vendors, - total: res.data.data.total, - columns: res.data.columns, - }), - }), - defaultData: { - data: { - vendors: [], - total: {}, - }, - columns: [], - tableRows: [], - }, + select: (res) => res.data, ...props, }, ); From b9be83dc2b6855f4c7c376cd0f8c6742a43a6013 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Sun, 27 Aug 2023 00:44:37 +0200 Subject: [PATCH 3/5] fix(webapp): add validation to aging summary customize form --- .../APAgingSummary/common.tsx | 12 +++-- .../APAgingSummary/components.tsx | 54 +------------------ .../ARAgingSummary/common.tsx | 8 ++- 3 files changed, 16 insertions(+), 58 deletions(-) diff --git a/packages/webapp/src/containers/FinancialStatements/APAgingSummary/common.tsx b/packages/webapp/src/containers/FinancialStatements/APAgingSummary/common.tsx index 87ed03627..a5619532b 100644 --- a/packages/webapp/src/containers/FinancialStatements/APAgingSummary/common.tsx +++ b/packages/webapp/src/containers/FinancialStatements/APAgingSummary/common.tsx @@ -36,19 +36,23 @@ export const getAPAgingSummaryQuerySchema = () => { .required() .integer() .positive() - .label('agingBeforeDays'), + .label('Aging days before') + .min(1) + .max(500), agingPeriods: Yup.number() .required() .integer() .positive() - .label('agingPeriods'), + .max(12) + .min(1) + .label('Aging periods'), }); }; /** * Parses the AP aging summary query state. - * @param locationQuery - * @returns + * @param locationQuery + * @returns */ const parseAPAgingSummaryQuery = (locationQuery) => { const defaultQuery = getDefaultAPAgingSummaryQuery(); diff --git a/packages/webapp/src/containers/FinancialStatements/APAgingSummary/components.tsx b/packages/webapp/src/containers/FinancialStatements/APAgingSummary/components.tsx index 17429cd1d..f6af9931a 100644 --- a/packages/webapp/src/containers/FinancialStatements/APAgingSummary/components.tsx +++ b/packages/webapp/src/containers/FinancialStatements/APAgingSummary/components.tsx @@ -1,11 +1,9 @@ // @ts-nocheck import React, { useMemo } from 'react'; -import intl from 'react-intl-universal'; -import * as R from 'ramda'; -import { If, FormattedMessage as T } from '@/components'; -import { useAPAgingSummaryContext } from './APAgingSummaryProvider'; +import { If } from '@/components'; import FinancialLoadingBar from '../FinancialLoadingBar'; +import { useAPAgingSummaryContext } from './APAgingSummaryProvider'; import { agingSummaryDynamicColumns } from '../AgingSummary/dynamicColumns'; /** @@ -19,54 +17,6 @@ export const useAPAgingSummaryColumns = () => { return agingSummaryDynamicColumns(table.columns, table.rows); }; -/** - * Retrieve AP aging summary columns. - */ -// export const useAPAgingSummaryColumns = () => { -// const { -// APAgingSummary: { tableRows, columns }, -// } = useAPAgingSummaryContext(); - -// const agingColumns = React.useMemo(() => { -// return columns.map( -// (agingColumn) => -// `${agingColumn.before_days} - ${ -// agingColumn.to_days || intl.get('and_over') -// }`, -// ); -// }, [columns]); - -// return useMemo( -// () => [ -// { -// Header: , -// accessor: 'name', -// className: 'vendor_name', -// width: 240, -// sticky: 'left', -// textOverview: true, -// }, -// { -// Header: , -// accessor: 'current', -// className: 'current', -// width: getColumnWidth(tableRows, `current`, { minWidth: 120 }), -// }, -// ...agingColumns.map((agingColumn, index) => ({ -// Header: agingColumn, -// accessor: `aging-${index}`, -// width: getColumnWidth(tableRows, `aging-${index}`, { minWidth: 120 }), -// })), -// { -// Header: , -// accessor: 'total', -// width: getColumnWidth(tableRows, 'total', { minWidth: 120 }), -// }, -// ], -// [tableRows, agingColumns], -// ); -// }; - /** * A/P aging summary sheet loading bar. */ diff --git a/packages/webapp/src/containers/FinancialStatements/ARAgingSummary/common.tsx b/packages/webapp/src/containers/FinancialStatements/ARAgingSummary/common.tsx index e70c6efb3..6152b25c2 100644 --- a/packages/webapp/src/containers/FinancialStatements/ARAgingSummary/common.tsx +++ b/packages/webapp/src/containers/FinancialStatements/ARAgingSummary/common.tsx @@ -35,12 +35,16 @@ export const getARAgingSummaryQuerySchema = () => { .required() .integer() .positive() - .label('agingDaysBefore'), + .label('Aging days before') + .min(1) + .max(500), agingPeriods: Yup.number() .required() .integer() .positive() - .label('agingPeriods'), + .max(12) + .min(1) + .label('Aging periods'), }); }; From 0d57ca88bf2bee543ecc989bfd494f39515e5813 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Sun, 27 Aug 2023 00:45:12 +0200 Subject: [PATCH 4/5] fix(server): avoid return total row on aging summary reports if no customers --- .../FinancialStatements/AgingSummary/AgingSummaryTable.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/server/src/services/FinancialStatements/AgingSummary/AgingSummaryTable.ts b/packages/server/src/services/FinancialStatements/AgingSummary/AgingSummaryTable.ts index 49a3f140b..3b74c76ac 100644 --- a/packages/server/src/services/FinancialStatements/AgingSummary/AgingSummaryTable.ts +++ b/packages/server/src/services/FinancialStatements/AgingSummary/AgingSummaryTable.ts @@ -164,7 +164,10 @@ export default abstract class AgingSummaryTable extends R.compose( * @returns {ITableRow[]} */ public tableRows = (): ITableRow[] => { - return R.compose(R.concat(this.contactsRows), R.prepend(this.totalRow))([]); + return R.compose( + R.unless(R.isEmpty, R.append(this.totalRow)), + R.concat(this.contactsRows) + )([]); }; // ------------------------- From 5dec4a7df023365266d49890c96e18ac6a9440ce Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Sun, 27 Aug 2023 00:56:14 +0200 Subject: [PATCH 5/5] chore(server): document methods --- .../api/controllers/FinancialStatements/APAgingSummary.ts | 4 ++-- .../AgingSummary/APAgingSummaryService.ts | 3 +-- .../AgingSummary/APAgingSummarySheet.ts | 2 +- .../AgingSummary/ARAgingSummaryService.ts | 7 +++---- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/server/src/api/controllers/FinancialStatements/APAgingSummary.ts b/packages/server/src/api/controllers/FinancialStatements/APAgingSummary.ts index cf84377f1..b0dc28841 100644 --- a/packages/server/src/api/controllers/FinancialStatements/APAgingSummary.ts +++ b/packages/server/src/api/controllers/FinancialStatements/APAgingSummary.ts @@ -34,8 +34,8 @@ export default class APAgingSummaryReportController extends BaseFinancialReportC ...this.sheetNumberFormatValidationSchema, query('as_date').optional().isISO8601(), - query('aging_days_before').default(30).isNumeric().toInt(), - query('aging_periods').default(3).isNumeric().toInt(), + query('aging_days_before').default(30).isInt({ max: 500 }).toInt(), + query('aging_periods').default(3).isInt({ max: 12 }).toInt(), query('vendors_ids').optional().isArray({ min: 1 }), query('vendors_ids.*').isInt({ min: 1 }).toInt(), diff --git a/packages/server/src/services/FinancialStatements/AgingSummary/APAgingSummaryService.ts b/packages/server/src/services/FinancialStatements/AgingSummary/APAgingSummaryService.ts index a2589745d..73860c9a5 100644 --- a/packages/server/src/services/FinancialStatements/AgingSummary/APAgingSummaryService.ts +++ b/packages/server/src/services/FinancialStatements/AgingSummary/APAgingSummaryService.ts @@ -121,10 +121,9 @@ export default class PayableAgingSummaryService { } /** - * + * Retrieves A/P aging summary in table format. * @param {number} tenantId * @param {IAPAgingSummaryQuery} query - * @returns */ async APAgingSummaryTable(tenantId: number, query: IAPAgingSummaryQuery) { const report = await this.APAgingSummary(tenantId, query); diff --git a/packages/server/src/services/FinancialStatements/AgingSummary/APAgingSummarySheet.ts b/packages/server/src/services/FinancialStatements/AgingSummary/APAgingSummarySheet.ts index dff23c4b6..ba42812ac 100644 --- a/packages/server/src/services/FinancialStatements/AgingSummary/APAgingSummarySheet.ts +++ b/packages/server/src/services/FinancialStatements/AgingSummary/APAgingSummarySheet.ts @@ -91,7 +91,7 @@ export default class APAgingSummarySheet extends AgingSummaryReport { return { vendorName: vendor.displayName, - current: this.formatTotalAmount(currentTotal), + current: this.formatAmount(currentTotal), aging: agingPeriods, total: this.formatTotalAmount(amount), }; diff --git a/packages/server/src/services/FinancialStatements/AgingSummary/ARAgingSummaryService.ts b/packages/server/src/services/FinancialStatements/AgingSummary/ARAgingSummaryService.ts index b47bd50a4..60bdb8675 100644 --- a/packages/server/src/services/FinancialStatements/AgingSummary/ARAgingSummaryService.ts +++ b/packages/server/src/services/FinancialStatements/AgingSummary/ARAgingSummaryService.ts @@ -120,10 +120,9 @@ export default class ARAgingSummaryService { } /** - * - * @param tenantId - * @param query - * @returns + * Retrieves A/R aging summary in table format. + * @param {number} tenantId + * @param {IARAgingSummaryQuery} query */ async ARAgingSummaryTable(tenantId: number, query: IARAgingSummaryQuery) { const report = await this.ARAgingSummary(tenantId, query);