mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 12:50:38 +00:00
fix: AR/AP aging report
This commit is contained in:
@@ -1,26 +1,55 @@
|
||||
import { Type } from "class-transformer";
|
||||
import { IsBoolean, IsEnum, IsNumber, IsOptional, IsPositive } from "class-validator";
|
||||
import { Type } from 'class-transformer';
|
||||
import {
|
||||
IsBoolean,
|
||||
IsEnum,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsPositive,
|
||||
} from 'class-validator';
|
||||
import { ApiPropertyOptional } from '@nestjs/swagger';
|
||||
|
||||
export class NumberFormatQueryDto {
|
||||
@ApiPropertyOptional({
|
||||
description: 'Number of decimal places to display',
|
||||
example: 2,
|
||||
})
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
@IsPositive()
|
||||
@IsOptional()
|
||||
readonly precision: number;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Whether to divide the number by 1000',
|
||||
example: false,
|
||||
})
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
readonly divideOn1000: boolean;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Whether to show zero values',
|
||||
example: true,
|
||||
})
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
readonly showZero: boolean;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'How to format money values',
|
||||
example: 'total',
|
||||
enum: ['total', 'always', 'none'],
|
||||
})
|
||||
@IsEnum(['total', 'always', 'none'])
|
||||
@IsOptional()
|
||||
readonly formatMoney: 'total' | 'always' | 'none';
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'How to format negative numbers',
|
||||
example: 'parentheses',
|
||||
enum: ['parentheses', 'mines'],
|
||||
})
|
||||
@IsEnum(['parentheses', 'mines'])
|
||||
@IsOptional()
|
||||
readonly negativeFormat: 'parentheses' | 'mines';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Response } from 'express';
|
||||
import { Controller, Get, Headers, Query, Res } from '@nestjs/common';
|
||||
import { APAgingSummaryApplication } from './APAgingSummaryApplication';
|
||||
import { IAPAgingSummaryQuery } from './APAgingSummary.types';
|
||||
import { AcceptType } from '@/constants/accept-type';
|
||||
import { Response } from 'express';
|
||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { APAgingSummaryQueryDto } from './APAgingSummaryQuery.dto';
|
||||
|
||||
|
||||
@@ -7,10 +7,7 @@ import {
|
||||
IAgingSummaryContact,
|
||||
IAgingSummaryData,
|
||||
} from '../AgingSummary/AgingSummary.types';
|
||||
|
||||
export interface IAPAgingSummaryQuery extends IAgingSummaryQuery {
|
||||
vendorsIds: number[];
|
||||
}
|
||||
import { APAgingSummaryQueryDto } from './APAgingSummaryQuery.dto';
|
||||
|
||||
export interface IAPAgingSummaryVendor extends IAgingSummaryContact {
|
||||
vendorName: string;
|
||||
@@ -33,13 +30,13 @@ export interface IAPAgingSummaryMeta extends IFinancialSheetCommonMeta {
|
||||
}
|
||||
|
||||
export interface IAPAgingSummaryTable extends IFinancialTable {
|
||||
query: IAPAgingSummaryQuery;
|
||||
query: APAgingSummaryQueryDto;
|
||||
meta: IAPAgingSummaryMeta;
|
||||
}
|
||||
|
||||
export interface IAPAgingSummarySheet {
|
||||
data: IAPAgingSummaryData;
|
||||
meta: IAPAgingSummaryMeta;
|
||||
query: IAPAgingSummaryQuery;
|
||||
query: APAgingSummaryQueryDto;
|
||||
columns: any;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { APAgingSummaryExportInjectable } from './APAgingSummaryExportInjectable';
|
||||
import { APAgingSummaryTableInjectable } from './APAgingSummaryTableInjectable';
|
||||
import { IAPAgingSummaryQuery } from './APAgingSummary.types';
|
||||
import { APAgingSummaryService } from './APAgingSummaryService';
|
||||
import { APAgingSummaryPdfInjectable } from './APAgingSummaryPdfInjectable';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { APAgingSummaryQueryDto } from './APAgingSummaryQuery.dto';
|
||||
|
||||
@Injectable()
|
||||
export class APAgingSummaryApplication {
|
||||
@@ -16,42 +16,42 @@ export class APAgingSummaryApplication {
|
||||
|
||||
/**
|
||||
* Retrieve the A/P aging summary in sheet format.
|
||||
* @param {IAPAgingSummaryQuery} query
|
||||
* @param {APAgingSummaryQueryDto} query
|
||||
*/
|
||||
public sheet(query: IAPAgingSummaryQuery) {
|
||||
public sheet(query: APAgingSummaryQueryDto) {
|
||||
return this.APAgingSummarySheet.APAgingSummary(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the A/P aging summary in table format.
|
||||
* @param {IAPAgingSummaryQuery} query
|
||||
* @param {APAgingSummaryQueryDto} query
|
||||
*/
|
||||
public table(query: IAPAgingSummaryQuery) {
|
||||
public table(query: APAgingSummaryQueryDto) {
|
||||
return this.APAgingSummaryTable.table(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the A/P aging summary in CSV format.
|
||||
* @param {IAPAgingSummaryQuery} query
|
||||
* @param {APAgingSummaryQueryDto} query
|
||||
*/
|
||||
public csv(query: IAPAgingSummaryQuery) {
|
||||
public csv(query: APAgingSummaryQueryDto) {
|
||||
return this.APAgingSummaryExport.csv(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the A/P aging summary in XLSX format.
|
||||
* @param {IAPAgingSummaryQuery} query
|
||||
* @param {APAgingSummaryQueryDto} query
|
||||
*/
|
||||
public xlsx(query: IAPAgingSummaryQuery) {
|
||||
public xlsx(query: APAgingSummaryQueryDto) {
|
||||
return this.APAgingSummaryExport.xlsx(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the A/P aging summary in pdf format.
|
||||
* @param {IAPAgingSummaryQuery} query
|
||||
* @param {APAgingSummaryQueryDto} query
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
public pdf(query: IAPAgingSummaryQuery) {
|
||||
public pdf(query: APAgingSummaryQueryDto) {
|
||||
return this.APAgingSumaryPdf.pdf(query);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { TableSheet } from '../../common/TableSheet';
|
||||
import { IAPAgingSummaryQuery } from './APAgingSummary.types';
|
||||
import { APAgingSummaryTableInjectable } from './APAgingSummaryTableInjectable';
|
||||
import { APAgingSummaryQueryDto } from './APAgingSummaryQuery.dto';
|
||||
|
||||
@Injectable()
|
||||
export class APAgingSummaryExportInjectable {
|
||||
@@ -11,10 +11,10 @@ export class APAgingSummaryExportInjectable {
|
||||
|
||||
/**
|
||||
* Retrieves the A/P aging summary sheet in XLSX format.
|
||||
* @param {IAPAgingSummaryQuery} query
|
||||
* @param {APAgingSummaryQueryDto} query
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
public async xlsx(query: IAPAgingSummaryQuery) {
|
||||
public async xlsx(query: APAgingSummaryQueryDto) {
|
||||
const table = await this.APAgingSummaryTable.table(query);
|
||||
|
||||
const tableSheet = new TableSheet(table.table);
|
||||
@@ -25,10 +25,10 @@ export class APAgingSummaryExportInjectable {
|
||||
|
||||
/**
|
||||
* Retrieves the A/P aging summary sheet in CSV format.
|
||||
* @param {IAPAgingSummaryQuery} query
|
||||
* @param {APAgingSummaryQueryDto} query
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
public async csv(query: IAPAgingSummaryQuery): Promise<string> {
|
||||
public async csv(query: APAgingSummaryQueryDto): Promise<string> {
|
||||
const table = await this.APAgingSummaryTable.table(query);
|
||||
|
||||
const tableSheet = new TableSheet(table.table);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { TableSheetPdf } from '../../common/TableSheetPdf';
|
||||
import { HtmlTableCss } from '../AgingSummary/_constants';
|
||||
import { IAPAgingSummaryQuery } from './APAgingSummary.types';
|
||||
import { APAgingSummaryQueryDto } from './APAgingSummaryQuery.dto';
|
||||
import { APAgingSummaryTableInjectable } from './APAgingSummaryTableInjectable';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class APAgingSummaryPdfInjectable {
|
||||
@@ -13,10 +13,10 @@ export class APAgingSummaryPdfInjectable {
|
||||
|
||||
/**
|
||||
* Converts the given A/P aging summary sheet table to pdf.
|
||||
* @param {IAPAgingSummaryQuery} query - Balance sheet query.
|
||||
* @param {APAgingSummaryQueryDto} query - Balance sheet query.
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
public async pdf(query: IAPAgingSummaryQuery): Promise<Buffer> {
|
||||
public async pdf(query: APAgingSummaryQueryDto): Promise<Buffer> {
|
||||
const table = await this.APAgingSummaryTable.table(query);
|
||||
|
||||
return this.tableSheetPdf.convertToPdf(
|
||||
|
||||
@@ -1,36 +1,74 @@
|
||||
import { Transform, Type } from 'class-transformer';
|
||||
import { IsNumber, IsOptional, IsDateString, IsBoolean, ValidateNested, IsArray } from 'class-validator';
|
||||
import {
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsDateString,
|
||||
IsBoolean,
|
||||
ValidateNested,
|
||||
IsArray,
|
||||
} from 'class-validator';
|
||||
import { FinancialSheetBranchesQueryDto } from '../../dtos/FinancialSheetBranchesQuery.dto';
|
||||
import { parseBoolean } from '@/utils/parse-boolean';
|
||||
import { ToNumber } from '@/common/decorators/Validators';
|
||||
import { NumberFormatQueryDto } from '@/modules/BankingTransactions/dtos/NumberFormatQuery.dto';
|
||||
import { ApiPropertyOptional } from '@nestjs/swagger';
|
||||
|
||||
export class APAgingSummaryQueryDto extends FinancialSheetBranchesQueryDto {
|
||||
@IsDateString()
|
||||
@IsOptional()
|
||||
@ApiPropertyOptional({
|
||||
description: 'The date as of which the AP aging summary is calculated',
|
||||
example: '2024-06-01',
|
||||
})
|
||||
asDate: Date | string;
|
||||
|
||||
@ToNumber()
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@ApiPropertyOptional({
|
||||
description: 'Number of days before the aging period starts',
|
||||
example: 30,
|
||||
})
|
||||
agingDaysBefore: number;
|
||||
|
||||
@ToNumber()
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@ApiPropertyOptional({
|
||||
description: 'Number of aging periods to calculate',
|
||||
example: 4,
|
||||
})
|
||||
agingPeriods: number;
|
||||
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => NumberFormatQueryDto)
|
||||
@ApiPropertyOptional({
|
||||
description: 'Number format configuration',
|
||||
example: {
|
||||
precision: 2,
|
||||
divideOn1000: false,
|
||||
showZero: true,
|
||||
formatMoney: 'total',
|
||||
negativeFormat: 'parentheses',
|
||||
},
|
||||
})
|
||||
numberFormat: NumberFormatQueryDto;
|
||||
|
||||
@Transform(({ value }) => parseBoolean(value, false))
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@ApiPropertyOptional({
|
||||
description: 'Whether to exclude zero values',
|
||||
example: false,
|
||||
})
|
||||
noneZero: boolean;
|
||||
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
@ApiPropertyOptional({
|
||||
description: 'Array of vendor IDs to include',
|
||||
example: [1, 2, 3],
|
||||
})
|
||||
vendorsIds: number[];
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { Inject } from '@nestjs/common';
|
||||
import { Inject, Injectable, Scope } from '@nestjs/common';
|
||||
import { isEmpty, groupBy } from 'lodash';
|
||||
import { ModelObject } from 'objection';
|
||||
import { Bill } from '@/modules/Bills/models/Bill';
|
||||
import { Vendor } from '@/modules/Vendors/models/Vendor';
|
||||
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
||||
import { IAPAgingSummaryQuery } from './APAgingSummary.types';
|
||||
import { ModelObject } from 'objection';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { APAgingSummaryQueryDto } from './APAgingSummaryQuery.dto';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class APAgingSummaryRepository {
|
||||
@Inject(Vendor.name)
|
||||
private readonly vendorModel: TenantModelProxy<typeof Vendor>;
|
||||
@@ -18,10 +19,10 @@ export class APAgingSummaryRepository {
|
||||
private readonly tenancyContext: TenancyContext;
|
||||
|
||||
/**
|
||||
* Filter.
|
||||
* @param {IAPAgingSummaryQuery} filter
|
||||
* A/P aging filter.
|
||||
* @param {APAgingSummaryQueryDto} filter
|
||||
*/
|
||||
filter: IAPAgingSummaryQuery;
|
||||
filter: APAgingSummaryQueryDto;
|
||||
|
||||
/**
|
||||
* Due bills.
|
||||
@@ -45,7 +46,7 @@ export class APAgingSummaryRepository {
|
||||
* Overdue bills by vendor id.
|
||||
* @param {Record<string, Bill[]>} overdueBillsByVendorId - Overdue bills by vendor id.
|
||||
*/
|
||||
overdueBillsByVendorId: ModelObject<Bill>[];
|
||||
overdueBillsByVendorId: Record<string, Array<ModelObject<Bill>>>;
|
||||
|
||||
/**
|
||||
* Vendors.
|
||||
@@ -61,9 +62,9 @@ export class APAgingSummaryRepository {
|
||||
|
||||
/**
|
||||
* Set the filter.
|
||||
* @param {IAPAgingSummaryQuery} filter
|
||||
* @param {APAgingSummaryQueryDto} filter
|
||||
*/
|
||||
setFilter(filter: IAPAgingSummaryQuery) {
|
||||
setFilter(filter: APAgingSummaryQueryDto) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { events } from '@/common/events/events';
|
||||
import {
|
||||
IAPAgingSummaryQuery,
|
||||
IAPAgingSummarySheet,
|
||||
} from './APAgingSummary.types';
|
||||
import { IAPAgingSummarySheet } from './APAgingSummary.types';
|
||||
import { APAgingSummarySheet } from './APAgingSummarySheet';
|
||||
import { APAgingSummaryMeta } from './APAgingSummaryMeta';
|
||||
import { getAPAgingSummaryDefaultQuery } from './utils';
|
||||
import { APAgingSummaryRepository } from './APAgingSummaryRepository';
|
||||
import { APAgingSummaryQueryDto } from './APAgingSummaryQuery.dto';
|
||||
|
||||
@Injectable()
|
||||
export class APAgingSummaryService {
|
||||
@@ -20,13 +18,12 @@ export class APAgingSummaryService {
|
||||
|
||||
/**
|
||||
* Retrieve A/P aging summary report.
|
||||
* @param {IAPAgingSummaryQuery} query - A/P aging summary query.
|
||||
* @param {APAgingSummaryQueryDto} query - A/P aging summary query.
|
||||
* @returns {Promise<IAPAgingSummarySheet>}
|
||||
*/
|
||||
public async APAgingSummary(
|
||||
query: IAPAgingSummaryQuery,
|
||||
query: APAgingSummaryQueryDto,
|
||||
): Promise<IAPAgingSummarySheet> {
|
||||
|
||||
const filter = {
|
||||
...getAPAgingSummaryDefaultQuery(),
|
||||
...query,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { sum, isEmpty } from 'lodash';
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
IAPAgingSummaryQuery,
|
||||
IAPAgingSummaryData,
|
||||
IAPAgingSummaryVendor,
|
||||
IAPAgingSummaryColumns,
|
||||
@@ -13,21 +12,24 @@ import { ModelObject } from 'objection';
|
||||
import { Vendor } from '@/modules/Vendors/models/Vendor';
|
||||
import { allPassedConditionsPass } from '@/utils/all-conditions-passed';
|
||||
import { APAgingSummaryRepository } from './APAgingSummaryRepository';
|
||||
import { Bill } from '@/modules/Bills/models/Bill';
|
||||
import { APAgingSummaryQueryDto } from './APAgingSummaryQuery.dto';
|
||||
|
||||
export class APAgingSummarySheet extends AgingSummaryReport {
|
||||
readonly repository: APAgingSummaryRepository;
|
||||
readonly query: IAPAgingSummaryQuery;
|
||||
readonly query: APAgingSummaryQueryDto;
|
||||
readonly agingPeriods: IAgingPeriod[];
|
||||
|
||||
readonly overdueInvoicesByContactId: Record<string, Array<ModelObject<Bill>>>;
|
||||
readonly currentInvoicesByContactId: Record<number, Array<ModelObject<Bill>>>;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {IAPAgingSummaryQuery} query - Report query.
|
||||
* @param {ModelObject<Vendor>[]} vendors - Unpaid bills.
|
||||
* @param {string} baseCurrency - Base currency of the organization.
|
||||
* @param {APAgingSummaryQueryDto} query - Report query.
|
||||
* @param {APAgingSummaryRepository} repository - Repository
|
||||
*/
|
||||
constructor(
|
||||
query: IAPAgingSummaryQuery,
|
||||
query: APAgingSummaryQueryDto,
|
||||
repository: APAgingSummaryRepository,
|
||||
) {
|
||||
super();
|
||||
@@ -36,6 +38,9 @@ export class APAgingSummarySheet extends AgingSummaryReport {
|
||||
this.repository = repository;
|
||||
this.numberFormat = this.query.numberFormat;
|
||||
|
||||
this.overdueInvoicesByContactId = this.repository.overdueBillsByVendorId;
|
||||
this.currentInvoicesByContactId = this.repository.dueBillsByVendorId;
|
||||
|
||||
// Initializes the aging periods.
|
||||
this.agingPeriods = this.agingRangePeriods(
|
||||
this.query.asDate,
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { I18nService } from 'nestjs-i18n';
|
||||
import {
|
||||
IAPAgingSummaryQuery,
|
||||
IAPAgingSummaryTable,
|
||||
} from './APAgingSummary.types';
|
||||
import { IAPAgingSummaryTable } from './APAgingSummary.types';
|
||||
import { APAgingSummaryService } from './APAgingSummaryService';
|
||||
import { APAgingSummaryTable } from './APAgingSummaryTable';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { APAgingSummaryQueryDto } from './APAgingSummaryQuery.dto';
|
||||
|
||||
@Injectable()
|
||||
export class APAgingSummaryTableInjectable {
|
||||
@@ -16,11 +14,11 @@ export class APAgingSummaryTableInjectable {
|
||||
|
||||
/**
|
||||
* Retrieves A/P aging summary in table format.
|
||||
* @param {IAPAgingSummaryQuery} query -
|
||||
* @param {APAgingSummaryQueryDto} query -
|
||||
* @returns {Promise<IAPAgingSummaryTable>}
|
||||
*/
|
||||
public async table(
|
||||
query: IAPAgingSummaryQuery,
|
||||
query: APAgingSummaryQueryDto,
|
||||
): Promise<IAPAgingSummaryTable> {
|
||||
const report = await this.APAgingSummarySheet.APAgingSummary(query);
|
||||
const table = new APAgingSummaryTable(report.data, query, this.i18nService);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import * as moment from 'moment';
|
||||
|
||||
export const getAPAgingSummaryDefaultQuery = () => {
|
||||
return {
|
||||
asDate: moment().format('YYYY-MM-DD'),
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { IARAgingSummaryQuery } from './ARAgingSummary.types';
|
||||
import { Controller, Get, Headers } from '@nestjs/common';
|
||||
import { Query, Res } from '@nestjs/common';
|
||||
import { ARAgingSummaryApplication } from './ARAgingSummaryApplication';
|
||||
import { AcceptType } from '@/constants/accept-type';
|
||||
import { Response } from 'express';
|
||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { ARAgingSummaryQueryDto } from './ARAgingSummaryQuery.dto';
|
||||
|
||||
@Controller('reports/receivable-aging-summary')
|
||||
@ApiTags('Reports')
|
||||
@@ -14,7 +14,7 @@ export class ARAgingSummaryController {
|
||||
@Get()
|
||||
@ApiOperation({ summary: 'Get receivable aging summary' })
|
||||
public async get(
|
||||
@Query() filter: IARAgingSummaryQuery,
|
||||
@Query() filter: ARAgingSummaryQueryDto,
|
||||
@Res({ passthrough: true }) res: Response,
|
||||
@Headers('accept') acceptHeader: string,
|
||||
) {
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
import {
|
||||
IAgingPeriod,
|
||||
IAgingSummaryQuery,
|
||||
IAgingSummaryTotal,
|
||||
IAgingSummaryContact,
|
||||
IAgingSummaryData,
|
||||
IAgingSummaryMeta,
|
||||
} from '../AgingSummary/AgingSummary.types';
|
||||
import { IFinancialTable } from '../../types/Table.types';
|
||||
|
||||
export interface IARAgingSummaryQuery extends IAgingSummaryQuery {
|
||||
customersIds: number[];
|
||||
}
|
||||
import { ARAgingSummaryQueryDto } from './ARAgingSummaryQuery.dto';
|
||||
|
||||
export interface IARAgingSummaryCustomer extends IAgingSummaryContact {
|
||||
customerName: string;
|
||||
@@ -31,13 +27,12 @@ export interface IARAgingSummaryMeta extends IAgingSummaryMeta {
|
||||
|
||||
export interface IARAgingSummaryTable extends IFinancialTable {
|
||||
meta: IARAgingSummaryMeta;
|
||||
query: IARAgingSummaryQuery;
|
||||
query: ARAgingSummaryQueryDto;
|
||||
}
|
||||
|
||||
export interface IARAgingSummarySheet {
|
||||
data: IARAgingSummaryData;
|
||||
meta: IARAgingSummaryMeta;
|
||||
query: IARAgingSummaryQuery;
|
||||
query: ARAgingSummaryQueryDto;
|
||||
columns: IARAgingSummaryColumns;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ARAgingSummaryTableInjectable } from './ARAgingSummaryTableInjectable';
|
||||
import { ARAgingSummaryExportInjectable } from './ARAgingSummaryExportInjectable';
|
||||
import { ARAgingSummaryService } from './ARAgingSummaryService';
|
||||
import { ARAgingSummaryPdfInjectable } from './ARAgingSummaryPdfInjectable';
|
||||
import { IARAgingSummaryQuery } from './ARAgingSummary.types';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ARAgingSummaryQueryDto } from './ARAgingSummaryQuery.dto';
|
||||
|
||||
@Injectable()
|
||||
export class ARAgingSummaryApplication {
|
||||
@@ -16,42 +16,42 @@ export class ARAgingSummaryApplication {
|
||||
|
||||
/**
|
||||
* Retrieve the A/R aging summary sheet.
|
||||
* @param {IARAgingSummaryQuery} query
|
||||
* @param {ARAgingSummaryQueryDto} query
|
||||
*/
|
||||
public sheet(query: IARAgingSummaryQuery) {
|
||||
public sheet(query: ARAgingSummaryQueryDto) {
|
||||
return this.ARAgingSummarySheet.ARAgingSummary(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the A/R aging summary in table format.
|
||||
* @param {IAPAgingSummaryQuery} query
|
||||
* @param {ARAgingSummaryQueryDto} query
|
||||
*/
|
||||
public table(query: IARAgingSummaryQuery) {
|
||||
public table(query: ARAgingSummaryQueryDto) {
|
||||
return this.ARAgingSummaryTable.table(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the A/R aging summary in XLSX format.
|
||||
* @param {IAPAgingSummaryQuery} query
|
||||
* @param {ARAgingSummaryQueryDto} query
|
||||
*/
|
||||
public xlsx(query: IARAgingSummaryQuery) {
|
||||
public xlsx(query: ARAgingSummaryQueryDto) {
|
||||
return this.ARAgingSummaryExport.xlsx(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the A/R aging summary in CSV format.
|
||||
* @param {IAPAgingSummaryQuery} query
|
||||
* @param {ARAgingSummaryQueryDto} query
|
||||
*/
|
||||
public csv(query: IARAgingSummaryQuery) {
|
||||
public csv(query: ARAgingSummaryQueryDto) {
|
||||
return this.ARAgingSummaryExport.csv(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the A/R aging summary in pdf format.
|
||||
* @param {IARAgingSummaryQuery} query
|
||||
* @param {ARAgingSummaryQueryDto} query
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
public pdf(query: IARAgingSummaryQuery) {
|
||||
public pdf(query: ARAgingSummaryQueryDto) {
|
||||
return this.ARAgingSummaryPdf.pdf(query);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ARAgingSummaryTableInjectable } from './ARAgingSummaryTableInjectable';
|
||||
import { IARAgingSummaryQuery } from './ARAgingSummary.types';
|
||||
import { TableSheet } from '../../common/TableSheet';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ARAgingSummaryQueryDto } from './ARAgingSummaryQuery.dto';
|
||||
|
||||
@Injectable()
|
||||
export class ARAgingSummaryExportInjectable {
|
||||
@@ -11,12 +11,10 @@ export class ARAgingSummaryExportInjectable {
|
||||
|
||||
/**
|
||||
* Retrieves the A/R aging summary sheet in XLSX format.
|
||||
* @param {IARAgingSummaryQuery} query - A/R aging summary query.
|
||||
* @param {ARAgingSummaryQueryDto} query - A/R aging summary query.
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
public async xlsx(
|
||||
query: IARAgingSummaryQuery
|
||||
): Promise<Buffer> {
|
||||
public async xlsx(query: ARAgingSummaryQueryDto): Promise<Buffer> {
|
||||
const table = await this.ARAgingSummaryTable.table(query);
|
||||
|
||||
const tableSheet = new TableSheet(table.table);
|
||||
@@ -27,12 +25,10 @@ export class ARAgingSummaryExportInjectable {
|
||||
|
||||
/**
|
||||
* Retrieves the A/R aging summary sheet in CSV format.
|
||||
* @param {IARAgingSummaryQuery} query - A/R aging summary query.
|
||||
* @param {ARAgingSummaryQueryDto} query - A/R aging summary query.
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
public async csv(
|
||||
query: IARAgingSummaryQuery
|
||||
): Promise<string> {
|
||||
public async csv(query: ARAgingSummaryQueryDto): Promise<string> {
|
||||
const table = await this.ARAgingSummaryTable.table(query);
|
||||
|
||||
const tableSheet = new TableSheet(table.table);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ARAgingSummaryTableInjectable } from './ARAgingSummaryTableInjectable';
|
||||
import { IARAgingSummaryQuery } from './ARAgingSummary.types';
|
||||
import { TableSheetPdf } from '../../common/TableSheetPdf';
|
||||
import { HtmlTableCss } from '../AgingSummary/_constants';
|
||||
import { ARAgingSummaryQueryDto } from './ARAgingSummaryQuery.dto';
|
||||
|
||||
@Injectable()
|
||||
export class ARAgingSummaryPdfInjectable {
|
||||
@@ -16,7 +16,7 @@ export class ARAgingSummaryPdfInjectable {
|
||||
* @param {IBalanceSheetQuery} query - Balance sheet query.
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
public async pdf(query: IARAgingSummaryQuery): Promise<Buffer> {
|
||||
public async pdf(query: ARAgingSummaryQueryDto): Promise<Buffer> {
|
||||
const table = await this.ARAgingSummaryTable.table(query);
|
||||
|
||||
return this.tableSheetPdf.convertToPdf(
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import { ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { Transform, Type } from 'class-transformer';
|
||||
import {
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsDateString,
|
||||
IsBoolean,
|
||||
ValidateNested,
|
||||
IsArray,
|
||||
} from 'class-validator';
|
||||
import { FinancialSheetBranchesQueryDto } from '../../dtos/FinancialSheetBranchesQuery.dto';
|
||||
import { ToNumber } from '@/common/decorators/Validators';
|
||||
import { NumberFormatQueryDto } from '@/modules/BankingTransactions/dtos/NumberFormatQuery.dto';
|
||||
import { parseBoolean } from '@/utils/parse-boolean';
|
||||
|
||||
export class ARAgingSummaryQueryDto extends FinancialSheetBranchesQueryDto {
|
||||
@IsDateString()
|
||||
@IsOptional()
|
||||
@ApiPropertyOptional({
|
||||
description: 'The date as of which the A/R aging summary is calculated',
|
||||
example: '2024-06-01',
|
||||
})
|
||||
asDate: Date | string;
|
||||
|
||||
@ToNumber()
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@ApiPropertyOptional({
|
||||
description: 'Number of days before the aging period starts',
|
||||
example: 30,
|
||||
})
|
||||
agingDaysBefore: number;
|
||||
|
||||
@ToNumber()
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@ApiPropertyOptional({
|
||||
description: 'Number of aging periods to calculate',
|
||||
example: 4,
|
||||
})
|
||||
agingPeriods: number;
|
||||
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => NumberFormatQueryDto)
|
||||
@ApiPropertyOptional({
|
||||
description: 'Number format configuration',
|
||||
example: {
|
||||
precision: 2,
|
||||
divideOn1000: false,
|
||||
showZero: true,
|
||||
formatMoney: 'total',
|
||||
negativeFormat: 'parentheses',
|
||||
},
|
||||
})
|
||||
numberFormat: NumberFormatQueryDto;
|
||||
|
||||
@Transform(({ value }) => parseBoolean(value, false))
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@ApiPropertyOptional({
|
||||
description: 'Whether to exclude zero values',
|
||||
example: false,
|
||||
})
|
||||
noneZero: boolean;
|
||||
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
@ApiPropertyOptional({
|
||||
description: 'Array of customer IDs to include',
|
||||
example: [1, 2, 3],
|
||||
})
|
||||
customersIds: number[];
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { Inject } from '@nestjs/common';
|
||||
import { Inject, Injectable, Scope } from '@nestjs/common';
|
||||
import { isEmpty, groupBy } from 'lodash';
|
||||
import { Customer } from '@/modules/Customers/models/Customer';
|
||||
import { SaleInvoice } from '@/modules/SaleInvoices/models/SaleInvoice';
|
||||
import { ModelObject } from 'objection';
|
||||
import { IARAgingSummaryQuery } from './ARAgingSummary.types';
|
||||
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { ARAgingSummaryQueryDto } from './ARAgingSummaryQuery.dto';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class ARAgingSummaryRepository {
|
||||
@Inject(TenancyContext)
|
||||
private tenancyContext: TenancyContext;
|
||||
@@ -19,9 +20,9 @@ export class ARAgingSummaryRepository {
|
||||
|
||||
/**
|
||||
* Filter.
|
||||
* @param {IARAgingSummaryQuery} filter
|
||||
* @param {ARAgingSummaryQueryDto} filter
|
||||
*/
|
||||
filter: IARAgingSummaryQuery;
|
||||
filter: ARAgingSummaryQueryDto;
|
||||
|
||||
/**
|
||||
* Base currency.
|
||||
@@ -61,9 +62,9 @@ export class ARAgingSummaryRepository {
|
||||
|
||||
/**
|
||||
* Set the filter.
|
||||
* @param {IARAgingSummaryQuery} filter
|
||||
* @param {ARAgingSummaryQueryDto} filter
|
||||
*/
|
||||
setFilter(filter: IARAgingSummaryQuery) {
|
||||
setFilter(filter: ARAgingSummaryQueryDto) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@@ -139,9 +140,6 @@ export class ARAgingSummaryRepository {
|
||||
.onBuild(commonQuery);
|
||||
|
||||
this.currentInvoices = currentInvoices;
|
||||
this.currentInvoicesByContactId = groupBy(
|
||||
currentInvoices,
|
||||
'customerId',
|
||||
);
|
||||
this.currentInvoicesByContactId = groupBy(currentInvoices, 'customerId');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ import { ARAgingSummaryMeta } from './ARAgingSummaryMeta';
|
||||
import { getARAgingSummaryDefaultQuery } from './utils';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { IARAgingSummaryQuery } from './ARAgingSummary.types';
|
||||
import { events } from '@/common/events/events';
|
||||
import { ARAgingSummaryRepository } from './ARAgingSummaryRepository';
|
||||
import { ARAgingSummaryQueryDto } from './ARAgingSummaryQuery.dto';
|
||||
|
||||
@Injectable()
|
||||
export class ARAgingSummaryService {
|
||||
@@ -17,9 +17,9 @@ export class ARAgingSummaryService {
|
||||
|
||||
/**
|
||||
* Retrieve A/R aging summary report.
|
||||
* @param {IARAgingSummaryQuery} query -
|
||||
* @param {ARAgingSummaryQueryDto} query -
|
||||
*/
|
||||
async ARAgingSummary(query: IARAgingSummaryQuery) {
|
||||
async ARAgingSummary(query: ARAgingSummaryQueryDto) {
|
||||
const filter = {
|
||||
...getARAgingSummaryDefaultQuery(),
|
||||
...query,
|
||||
@@ -28,12 +28,12 @@ export class ARAgingSummaryService {
|
||||
this.ARAgingSummaryRepository.setFilter(filter);
|
||||
await this.ARAgingSummaryRepository.load();
|
||||
|
||||
// AR aging summary report instance.
|
||||
// A/R aging summary report instance.
|
||||
const ARAgingSummaryReport = new ARAgingSummarySheet(
|
||||
filter,
|
||||
this.ARAgingSummaryRepository,
|
||||
);
|
||||
// AR aging summary report data and columns.
|
||||
// A/R aging summary report data and columns.
|
||||
const data = ARAgingSummaryReport.reportData();
|
||||
const columns = ARAgingSummaryReport.reportColumns();
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import * as R from 'ramda';
|
||||
import { isEmpty, sum } from 'lodash';
|
||||
import { IAgingPeriod } from '../AgingSummary/AgingSummary.types';
|
||||
import {
|
||||
IARAgingSummaryQuery,
|
||||
IARAgingSummaryCustomer,
|
||||
IARAgingSummaryData,
|
||||
IARAgingSummaryColumns,
|
||||
@@ -11,23 +10,31 @@ import {
|
||||
import { AgingSummaryReport } from '../AgingSummary/AgingSummary';
|
||||
import { allPassedConditionsPass } from '@/utils/all-conditions-passed';
|
||||
import { ModelObject } from 'objection';
|
||||
import { Customer } from '@/modules/Customers/models/Customer';
|
||||
import { ARAgingSummaryRepository } from './ARAgingSummaryRepository';
|
||||
import { Customer } from '@/modules/Customers/models/Customer';
|
||||
import { SaleInvoice } from '@/modules/SaleInvoices/models/SaleInvoice';
|
||||
import { ARAgingSummaryQueryDto } from './ARAgingSummaryQuery.dto';
|
||||
|
||||
export class ARAgingSummarySheet extends AgingSummaryReport {
|
||||
readonly query: IARAgingSummaryQuery;
|
||||
readonly query: ARAgingSummaryQueryDto;
|
||||
readonly agingPeriods: IAgingPeriod[];
|
||||
readonly repository: ARAgingSummaryRepository;
|
||||
readonly overdueInvoicesByContactId: Record<
|
||||
string,
|
||||
ModelObject<SaleInvoice>[]
|
||||
>;
|
||||
readonly currentInvoicesByContactId: Record<
|
||||
number,
|
||||
Array<ModelObject<SaleInvoice>>
|
||||
>;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {number} tenantId
|
||||
* @param {IARAgingSummaryQuery} query
|
||||
* @param {ICustomer[]} customers
|
||||
* @param {IJournalPoster} journal
|
||||
* @param {ARAgingSummaryQueryDto} query - Query
|
||||
* @param {ARAgingSummaryRepository} repository - Repository.
|
||||
*/
|
||||
constructor(
|
||||
query: IARAgingSummaryQuery,
|
||||
query: ARAgingSummaryQueryDto,
|
||||
repository: ARAgingSummaryRepository,
|
||||
) {
|
||||
super();
|
||||
@@ -36,6 +43,11 @@ export class ARAgingSummarySheet extends AgingSummaryReport {
|
||||
this.repository = repository;
|
||||
this.numberFormat = this.query.numberFormat;
|
||||
|
||||
this.overdueInvoicesByContactId =
|
||||
this.repository.overdueInvoicesByContactId;
|
||||
this.currentInvoicesByContactId =
|
||||
this.repository.currentInvoicesByContactId;
|
||||
|
||||
// Initializes the aging periods.
|
||||
this.agingPeriods = this.agingRangePeriods(
|
||||
this.query.asDate,
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { ARAgingSummaryTable } from './ARAgingSummaryTable';
|
||||
import { ARAgingSummaryService } from './ARAgingSummaryService';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import {
|
||||
IARAgingSummaryQuery,
|
||||
IARAgingSummaryTable,
|
||||
} from './ARAgingSummary.types';
|
||||
import { IARAgingSummaryTable } from './ARAgingSummary.types';
|
||||
import { ARAgingSummaryQueryDto } from './ARAgingSummaryQuery.dto';
|
||||
|
||||
@Injectable()
|
||||
export class ARAgingSummaryTableInjectable {
|
||||
@@ -12,11 +10,11 @@ export class ARAgingSummaryTableInjectable {
|
||||
|
||||
/**
|
||||
* Retrieves A/R aging summary in table format.
|
||||
* @param {IARAgingSummaryQuery} query - Aging summary query.
|
||||
* @param {ARAgingSummaryQueryDto} query - Aging summary query.
|
||||
* @returns {Promise<IARAgingSummaryTable>}
|
||||
*/
|
||||
public async table(
|
||||
query: IARAgingSummaryQuery,
|
||||
query: ARAgingSummaryQueryDto,
|
||||
): Promise<IARAgingSummaryTable> {
|
||||
const report = await this.ARAgingSummarySheet.ARAgingSummary(query);
|
||||
const table = new ARAgingSummaryTable(report.data, query, {});
|
||||
|
||||
@@ -13,7 +13,7 @@ export class CustomerBalanceSummaryPdf {
|
||||
|
||||
/**
|
||||
* Converts the given customer balance summary sheet table to pdf.
|
||||
* @param {IAPAgingSummaryQuery} query - Balance sheet query.
|
||||
* @param {ICustomerBalanceSummaryQuery} query - Balance sheet query.
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
public async pdf(query: ICustomerBalanceSummaryQuery): Promise<Buffer> {
|
||||
|
||||
Reference in New Issue
Block a user