refactor: wip to nestjs

This commit is contained in:
Ahmed Bouhuolia
2024-12-25 00:43:55 +02:00
parent 336171081e
commit a6932d76f3
249 changed files with 21314 additions and 1616 deletions

View File

@@ -0,0 +1,39 @@
import { Injectable } from '@nestjs/common';
import { ERRORS } from '../constants';
import { PaymentReceiveTransfromer } from './PaymentReceivedTransformer';
import { PaymentReceived } from '../models/PaymentReceived';
import { TransformerInjectable } from '../../Transformer/TransformerInjectable.service';
import { ServiceError } from '../../Items/ServiceError';
@Injectable()
export class GetPaymentReceived {
constructor(
private readonly paymentReceiveModel: typeof PaymentReceived,
private readonly transformer: TransformerInjectable,
) {}
/**
* Retrieve payment receive details.
* @param {number} paymentReceiveId - Payment receive id.
* @return {Promise<IPaymentReceived>}
*/
public async getPaymentReceive(
paymentReceiveId: number
): Promise<PaymentReceived> {
const paymentReceive = await this.paymentReceiveModel.query()
.withGraphFetched('customer')
.withGraphFetched('depositAccount')
.withGraphFetched('entries.invoice')
.withGraphFetched('transactions')
.withGraphFetched('branch')
.findById(paymentReceiveId);
if (!paymentReceive) {
throw new ServiceError(ERRORS.PAYMENT_RECEIVE_NOT_EXISTS);
}
return this.transformer.transform(
paymentReceive,
new PaymentReceiveTransfromer()
);
}
}

View File

@@ -0,0 +1,37 @@
import { Inject, Injectable } from '@nestjs/common';
import { PaymentReceivedValidators } from '../commands/PaymentReceivedValidators.service';
import { SaleInvoice } from '../../SaleInvoices/models/SaleInvoice';
import { PaymentReceived } from '../models/PaymentReceived';
@Injectable()
export class GetPaymentReceivedInvoices {
constructor(
@Inject(PaymentReceived.name)
private paymentReceiveModel: typeof PaymentReceived,
private validators: PaymentReceivedValidators,
) {}
/**
* Retrieve sale invoices that associated to the given payment receive.
* @param {number} paymentReceiveId - Payment receive id.
* @return {Promise<ISaleInvoice>}
*/
public async getPaymentReceiveInvoices(paymentReceiveId: number) {
const paymentReceive = await this.paymentReceiveModel
.query()
.findById(paymentReceiveId)
.withGraphFetched('entries');
// Validates the payment receive existence.
this.validators.validatePaymentExistance(paymentReceive);
const paymentReceiveInvoicesIds = paymentReceive.entries.map(
(entry) => entry.invoiceId,
);
const saleInvoices = await SaleInvoice.query().whereIn(
'id',
paymentReceiveInvoicesIds,
);
return saleInvoices;
}
}

View File

@@ -0,0 +1,106 @@
import { Inject, Injectable } from '@nestjs/common';
import { GetPaymentReceived } from './GetPaymentReceived.service';
import { PaymentReceivedBrandingTemplate } from './PaymentReceivedBrandingTemplate.service';
import { transformPaymentReceivedToPdfTemplate } from '../utils';
import { PaymentReceived } from '../models/PaymentReceived';
import { PdfTemplateModel } from '@/modules/PdfTemplate/models/PdfTemplate';
import { ChromiumlyTenancy } from '@/modules/ChromiumlyTenancy/ChromiumlyTenancy.service';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { TemplateInjectable } from '@/modules/TemplateInjectable/TemplateInjectable.service';
import { PaymentReceivedPdfTemplateAttributes } from '../types/PaymentReceived.types';
import { events } from '@/common/events/events';
@Injectable()
export default class GetPaymentReceivedPdf {
constructor(
private chromiumlyTenancy: ChromiumlyTenancy,
private templateInjectable: TemplateInjectable,
private getPaymentService: GetPaymentReceived,
private paymentBrandingTemplateService: PaymentReceivedBrandingTemplate,
private eventPublisher: EventEmitter2,
@Inject(PaymentReceived.name)
private paymentReceiveModel: typeof PaymentReceived,
@Inject(PdfTemplateModel.name)
private pdfTemplateModel: typeof PdfTemplateModel,
) {}
/**
* Retrieve sale invoice pdf content.
* @param {number} tenantId -
* @param {IPaymentReceived} paymentReceive -
* @returns {Promise<Buffer>}
*/
async getPaymentReceivePdf(
paymentReceivedId: number,
): Promise<[Buffer, string]> {
const brandingAttributes =
await this.getPaymentBrandingAttributes(paymentReceivedId);
const htmlContent = await this.templateInjectable.render(
'modules/payment-receive-standard',
brandingAttributes,
);
const filename = await this.getPaymentReceivedFilename(paymentReceivedId);
// Converts the given html content to pdf document.
const content =
await this.chromiumlyTenancy.convertHtmlContent(htmlContent);
const eventPayload = { paymentReceivedId };
// Triggers the `onCreditNotePdfViewed` event.
await this.eventPublisher.emitAsync(
events.paymentReceive.onPdfViewed,
eventPayload,
);
return [content, filename];
}
/**
* Retrieves the filename of the given payment.
* @param {number} tenantId
* @param {number} paymentReceivedId
* @returns {Promise<string>}
*/
private async getPaymentReceivedFilename(
paymentReceivedId: number,
): Promise<string> {
const payment = await this.paymentReceiveModel
.query()
.findById(paymentReceivedId);
return `Payment-${payment.paymentReceiveNo}`;
}
/**
* Retrieves the given payment received branding attributes.
* @param {number} paymentReceivedId - Payment received identifier.
* @returns {Promise<PaymentReceivedPdfTemplateAttributes>}
*/
async getPaymentBrandingAttributes(
paymentReceivedId: number,
): Promise<PaymentReceivedPdfTemplateAttributes> {
const paymentReceived =
await this.getPaymentService.getPaymentReceive(paymentReceivedId);
const templateId =
paymentReceived?.pdfTemplateId ??
(
await this.pdfTemplateModel.query().findOne({
resource: 'PaymentReceive',
default: true,
})
)?.id;
const brandingTemplate =
await this.paymentBrandingTemplateService.getPaymentReceivedPdfTemplate(
templateId,
);
return {
...brandingTemplate.attributes,
...transformPaymentReceivedToPdfTemplate(paymentReceived),
};
}
}

View File

@@ -0,0 +1,23 @@
import { PdfTemplateModel } from '@/modules/PdfTemplate/models/PdfTemplate';
import { Injectable } from '@nestjs/common';
import { IPaymentReceivedState } from '../types/PaymentReceived.types';
@Injectable()
export class GetPaymentReceivedState {
constructor(private pdfTemplateModel: typeof PdfTemplateModel) {}
/**
* Retrieves the create/edit initial state of the payment received.
* @returns {Promise<IPaymentReceivedState>} - A promise resolving to the payment received state.
*/
public async getPaymentReceivedState(): Promise<IPaymentReceivedState> {
const defaultPdfTemplate = await this.pdfTemplateModel
.query()
.findOne({ resource: 'PaymentReceive' })
.modify('default');
return {
defaultTemplateId: defaultPdfTemplate?.id,
};
}
}

View File

@@ -0,0 +1,79 @@
// import { Inject, Service } from 'typedi';
// import * as R from 'ramda';
// import {
// IFilterMeta,
// IPaginationMeta,
// IPaymentReceived,
// IPaymentsReceivedFilter,
// } from '@/interfaces';
// import { PaymentReceiveTransfromer } from './PaymentReceivedTransformer';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
// import DynamicListingService from '@/services/DynamicListing/DynamicListService';
// @Service()
// export class GetPaymentReceives {
// @Inject()
// private tenancy: HasTenancyService;
// @Inject()
// private dynamicListService: DynamicListingService;
// @Inject()
// private transformer: TransformerInjectable;
// /**
// * Retrieve payment receives paginated and filterable list.
// * @param {number} tenantId
// * @param {IPaymentsReceivedFilter} paymentReceivesFilter
// */
// public async getPaymentReceives(
// tenantId: number,
// filterDTO: IPaymentsReceivedFilter
// ): Promise<{
// paymentReceives: IPaymentReceived[];
// pagination: IPaginationMeta;
// filterMeta: IFilterMeta;
// }> {
// const { PaymentReceive } = this.tenancy.models(tenantId);
// // Parses filter DTO.
// const filter = this.parseListFilterDTO(filterDTO);
// // Dynamic list service.
// const dynamicList = await this.dynamicListService.dynamicList(
// tenantId,
// PaymentReceive,
// filter
// );
// const { results, pagination } = await PaymentReceive.query()
// .onBuild((builder) => {
// builder.withGraphFetched('customer');
// builder.withGraphFetched('depositAccount');
// dynamicList.buildQuery()(builder);
// filterDTO?.filterQuery && filterDTO.filterQuery(builder);
// })
// .pagination(filter.page - 1, filter.pageSize);
// // Transformer the payment receives models to POJO.
// const transformedPayments = await this.transformer.transform(
// tenantId,
// results,
// new PaymentReceiveTransfromer()
// );
// return {
// paymentReceives: transformedPayments,
// pagination,
// filterMeta: dynamicList.getResponseMeta(),
// };
// }
// /**
// * Parses payments receive list filter DTO.
// * @param filterDTO
// */
// private parseListFilterDTO(filterDTO) {
// return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
// }
// }

View File

@@ -0,0 +1,49 @@
import { Inject, Injectable } from '@nestjs/common';
import { defaultPaymentReceivedPdfTemplateAttributes } from '../constants';
import { GetPdfTemplateService } from '../../PdfTemplate/queries/GetPdfTemplate.service';
import { GetOrganizationBrandingAttributesService } from '../../PdfTemplate/queries/GetOrganizationBrandingAttributes.service';
import { PdfTemplateModel } from '../../PdfTemplate/models/PdfTemplate';
import { mergePdfTemplateWithDefaultAttributes } from '../../SaleInvoices/utils';
@Injectable()
export class PaymentReceivedBrandingTemplate {
constructor(
private readonly getPdfTemplateService: GetPdfTemplateService,
private readonly getOrgBrandingAttributes: GetOrganizationBrandingAttributesService,
@Inject(PdfTemplateModel.name)
private readonly pdfTemplateModel: typeof PdfTemplateModel,
) {}
/**
* Retrieves the payment received pdf template.
* @param {number} paymentTemplateId
* @returns
*/
public async getPaymentReceivedPdfTemplate(paymentTemplateId: number) {
const template = await this.getPdfTemplateService.getPdfTemplate(
paymentTemplateId
);
// Retrieves the organization branding attributes.
const commonOrgBrandingAttrs =
await this.getOrgBrandingAttributes.getOrganizationBrandingAttributes();
// Merges the default branding attributes with common organization branding attrs.
const organizationBrandingAttrs = {
...defaultPaymentReceivedPdfTemplateAttributes,
...commonOrgBrandingAttrs,
};
const brandingTemplateAttrs = {
...template.attributes,
companyLogoUri: template.companyLogoUri,
};
const attributes = mergePdfTemplateWithDefaultAttributes(
brandingTemplateAttrs,
organizationBrandingAttrs
);
return {
...template,
attributes,
};
}
}

View File

@@ -0,0 +1,29 @@
import { SaleInvoiceTransformer } from "@/modules/SaleInvoices/queries/SaleInvoice.transformer";
import { Transformer } from "@/modules/Transformer/Transformer";
export class PaymentReceivedEntryTransfromer extends Transformer {
/**
* Include these attributes to payment receive entry object.
* @returns {Array}
*/
public includeAttributes = (): string[] => {
return ['paymentAmountFormatted', 'invoice'];
};
/**
* Retreives the payment amount formatted.
* @param entry
* @returns {string}
*/
protected paymentAmountFormatted(entry) {
return this.formatNumber(entry.paymentAmount, { money: false });
}
/**
* Retreives the transformed invoice.
*/
protected invoice(entry) {
return this.item(entry.invoice, new SaleInvoiceTransformer());
}
}

View File

@@ -0,0 +1,80 @@
import { Transformer } from '../../Transformer/Transformer';
import { PaymentReceived } from '../models/PaymentReceived';
import { PaymentReceivedEntry } from '../models/PaymentReceivedEntry';
import { PaymentReceivedEntryTransfromer } from './PaymentReceivedEntryTransformer';
export class PaymentReceiveTransfromer extends Transformer {
/**
* Include these attributes to payment receive object.
* @returns {Array}
*/
public includeAttributes = (): string[] => {
return [
'subtotalFormatted',
'formattedPaymentDate',
'formattedCreatedAt',
'formattedAmount',
'formattedExchangeRate',
'entries',
];
};
/**
* Retrieve formatted payment receive date.
* @param {PaymentReceived} invoice
* @returns {String}
*/
protected formattedPaymentDate = (payment: PaymentReceived): string => {
return this.formatDate(payment.paymentDate);
};
/**
* Retrieves the formatted created at date.
* @param {PaymentReceived} payment
* @returns {string}
*/
protected formattedCreatedAt = (payment: PaymentReceived): string => {
return this.formatDate(payment.createdAt);
};
/**
* Retrieve the formatted payment subtotal.
* @param {PaymentReceived} payment
* @returns {string}
*/
protected subtotalFormatted = (payment: PaymentReceived): string => {
return this.formatNumber(payment.amount, {
currencyCode: payment.currencyCode,
money: false,
});
};
/**
* Retrieve formatted payment amount.
* @param {PaymentReceived} invoice
* @returns {string}
*/
protected formattedAmount = (payment: PaymentReceived): string => {
return this.formatNumber(payment.amount, {
currencyCode: payment.currencyCode,
});
};
/**
* Retrieve the formatted exchange rate.
* @param {PaymentReceived} payment
* @returns {string}
*/
protected formattedExchangeRate = (payment: PaymentReceived): string => {
return this.formatNumber(payment.exchangeRate, { money: false });
};
/**
* Retrieves the payment entries.
* @param {PaymentReceived} payment
* @returns {IPaymentReceivedEntry[]}
*/
protected entries = (payment: PaymentReceived): PaymentReceivedEntry[] => {
return this.item(payment.entries, new PaymentReceivedEntryTransfromer());
};
}

View File

@@ -0,0 +1,111 @@
import { Inject, Service } from 'typedi';
import { omit } from 'lodash';
import { IPaymentReceivePageEntry } from '../types/PaymentReceived.types';
import { ERRORS } from '../constants';
import { Injectable } from '@nestjs/common';
import { SaleInvoice } from '@/modules/SaleInvoices/models/SaleInvoice';
import { PaymentReceived } from '../models/PaymentReceived';
import { ServiceError } from '@/modules/Items/ServiceError';
/**
* Payment receives edit/new pages service.
*/
@Injectable()
export class PaymentsReceivedPagesService {
constructor(
@Inject(SaleInvoice.name)
private readonly saleInvoice: typeof SaleInvoice,
@Inject(PaymentReceived.name)
private readonly paymentReceived: typeof PaymentReceived,
) {}
/**
* Retrive page invoices entries from the given sale invoices models.
* @param {ISaleInvoice[]} invoices - Invoices.
* @return {IPaymentReceivePageEntry}
*/
private invoiceToPageEntry(invoice: SaleInvoice): IPaymentReceivePageEntry {
return {
entryType: 'invoice',
invoiceId: invoice.id,
invoiceNo: invoice.invoiceNo,
amount: invoice.balance,
dueAmount: invoice.dueAmount,
paymentAmount: invoice.paymentAmount,
totalPaymentAmount: invoice.paymentAmount,
currencyCode: invoice.currencyCode,
date: invoice.invoiceDate,
};
}
/**
* Retrieve payment receive new page receivable entries.
* @param {number} tenantId - Tenant id.
* @param {number} vendorId - Vendor id.
* @return {IPaymentReceivePageEntry[]}
*/
public async getNewPageEntries(tenantId: number, customerId: number) {
// Retrieve due invoices.
const entries = await this.saleInvoice
.query()
.modify('delivered')
.modify('dueInvoices')
.where('customer_id', customerId)
.orderBy('invoice_date', 'ASC');
return entries.map(this.invoiceToPageEntry);
}
/**
* Retrieve the payment receive details of the given id.
* @param {number} tenantId - Tenant id.
* @param {Integer} paymentReceiveId - Payment receive id.
*/
public async getPaymentReceiveEditPage(
tenantId: number,
paymentReceiveId: number,
): Promise<{
paymentReceive: Omit<PaymentReceived, 'entries'>;
entries: IPaymentReceivePageEntry[];
}> {
// Retrieve payment receive.
const paymentReceive = await this.paymentReceived
.query()
.findById(paymentReceiveId)
.withGraphFetched('entries.invoice')
.withGraphFetched('attachments');
// Throw not found the payment receive.
if (!paymentReceive) {
throw new ServiceError(ERRORS.PAYMENT_RECEIVE_NOT_EXISTS);
}
const paymentEntries = paymentReceive.entries.map((entry) => ({
...this.invoiceToPageEntry(entry.invoice),
dueAmount: entry.invoice.dueAmount + entry.paymentAmount,
paymentAmount: entry.paymentAmount,
index: entry.index,
}));
// Retrieves all receivable bills that associated to the payment receive transaction.
const restReceivableInvoices = await this.saleInvoice
.query()
.modify('delivered')
.modify('dueInvoices')
.where('customer_id', paymentReceive.customerId)
.whereNotIn(
'id',
paymentReceive.entries.map((entry) => entry.invoiceId),
)
.orderBy('invoice_date', 'ASC');
const restReceivableEntries = restReceivableInvoices.map(
this.invoiceToPageEntry,
);
const entries = [...paymentEntries, ...restReceivableEntries];
return {
paymentReceive: omit(paymentReceive, ['entries']),
entries,
};
}
}