diff --git a/packages/server/src/modules/Bills/queries/Bill.transformer.ts b/packages/server/src/modules/Bills/queries/Bill.transformer.ts index 9227d216f..340c4015e 100644 --- a/packages/server/src/modules/Bills/queries/Bill.transformer.ts +++ b/packages/server/src/modules/Bills/queries/Bill.transformer.ts @@ -1,8 +1,5 @@ import { Transformer } from '@/modules/Transformer/Transformer'; import { Bill } from '../models/Bill'; -import { AttachmentTransformer } from '@/modules/Attachments/Attachment.transformer'; -import { ItemEntryTransformer } from '@/modules/TransactionItemEntry/ItemEntry.transformer'; -import { SaleInvoiceTaxEntryTransformer } from '@/modules/SaleInvoices/queries/SaleInvoiceTaxEntry.transformer'; export class BillTransformer extends Transformer { /** @@ -23,6 +20,9 @@ export class BillTransformer extends Transformer { 'subtotalLocalFormatted', 'subtotalExcludingTaxFormatted', 'taxAmountWithheldLocalFormatted', + 'discountAmountFormatted', + 'discountPercentageFormatted', + 'adjustmentFormatted', 'totalFormatted', 'totalLocalFormatted', 'taxes', @@ -183,6 +183,37 @@ export class BillTransformer extends Transformer { }); }; + /** + * Retrieves the formatted discount amount. + * @param {IBill} bill + * @returns {string} + */ + protected discountAmountFormatted = (bill: Bill): string => { + return this.formatNumber(bill.discountAmount, { + currencyCode: bill.currencyCode, + }); + }; + + /** + * Retrieves the formatted discount percentage. + * @param {IBill} bill + * @returns {string} + */ + protected discountPercentageFormatted = (bill: Bill): string => { + return bill.discountPercentage ? `${bill.discountPercentage}%` : ''; + }; + + /** + * Retrieves the formatted adjustment amount. + * @param {IBill} bill + * @returns {string} + */ + protected adjustmentFormatted = (bill: Bill): string => { + return this.formatNumber(bill.adjustment, { + currencyCode: bill.currencyCode, + }); + }; + /** * Retrieve the taxes lines of bill. * @param {Bill} bill diff --git a/packages/server/src/modules/PaymentReceived/PaymentReceived.application.ts b/packages/server/src/modules/PaymentReceived/PaymentReceived.application.ts index 80a7f3a83..2a51c5155 100644 --- a/packages/server/src/modules/PaymentReceived/PaymentReceived.application.ts +++ b/packages/server/src/modules/PaymentReceived/PaymentReceived.application.ts @@ -17,6 +17,7 @@ import { EditPaymentReceivedDto, } from './dtos/PaymentReceived.dto'; import { PaymentsReceivedPagesService } from './queries/PaymentsReceivedPages.service'; +import { GetPaymentReceivedMailState } from './queries/GetPaymentReceivedMailState.service'; @Injectable() export class PaymentReceivesApplication { @@ -28,6 +29,7 @@ export class PaymentReceivesApplication { private getPaymentReceivedService: GetPaymentReceivedService, private getPaymentReceiveInvoicesService: GetPaymentReceivedInvoices, private sendPaymentReceiveMailNotification: SendPaymentReceiveMailNotification, + private getPaymentReceivedMailStateService: GetPaymentReceivedMailState, private getPaymentReceivePdfService: GetPaymentReceivedPdfService, private getPaymentReceivedStateService: GetPaymentReceivedStateService, private paymentsReceivedPagesService: PaymentsReceivedPagesService, @@ -125,7 +127,7 @@ export class PaymentReceivesApplication { * @returns {Promise} */ public getPaymentMailOptions(paymentReceiveId: number) { - return this.sendPaymentReceiveMailNotification.getMailOptions( + return this.getPaymentReceivedMailStateService.getMailOptions( paymentReceiveId, ); } diff --git a/packages/server/src/modules/PaymentReceived/PaymentsReceived.module.ts b/packages/server/src/modules/PaymentReceived/PaymentsReceived.module.ts index 4d611bcf2..8d062a461 100644 --- a/packages/server/src/modules/PaymentReceived/PaymentsReceived.module.ts +++ b/packages/server/src/modules/PaymentReceived/PaymentsReceived.module.ts @@ -37,6 +37,8 @@ import { SEND_PAYMENT_RECEIVED_MAIL_QUEUE } from './constants'; import { PaymentsReceivedExportable } from './commands/PaymentsReceivedExportable'; import { PaymentsReceivedImportable } from './commands/PaymentsReceivedImportable'; import { PaymentsReceivedPagesService } from './queries/PaymentsReceivedPages.service'; +import { GetPaymentReceivedMailTemplate } from './queries/GetPaymentReceivedMailTemplate.service'; +import { GetPaymentReceivedMailState } from './queries/GetPaymentReceivedMailState.service'; @Module({ controllers: [PaymentReceivesController], @@ -64,7 +66,9 @@ import { PaymentsReceivedPagesService } from './queries/PaymentsReceivedPages.se SendPaymentReceivedMailProcessor, PaymentsReceivedExportable, PaymentsReceivedImportable, - PaymentsReceivedPagesService + PaymentsReceivedPagesService, + GetPaymentReceivedMailTemplate, + GetPaymentReceivedMailState ], exports: [ PaymentReceivesApplication, diff --git a/packages/server/src/modules/PaymentReceived/commands/PaymentReceivedMailNotification.ts b/packages/server/src/modules/PaymentReceived/commands/PaymentReceivedMailNotification.ts index 7dca72f12..f5ae3b136 100644 --- a/packages/server/src/modules/PaymentReceived/commands/PaymentReceivedMailNotification.ts +++ b/packages/server/src/modules/PaymentReceived/commands/PaymentReceivedMailNotification.ts @@ -25,6 +25,7 @@ import { Mail } from '@/modules/Mail/Mail'; import { MailTransporter } from '@/modules/Mail/MailTransporter.service'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service'; +import { GetPaymentReceivedMailTemplate } from '../queries/GetPaymentReceivedMailTemplate.service'; @Injectable() export class SendPaymentReceiveMailNotification { @@ -34,6 +35,7 @@ export class SendPaymentReceiveMailNotification { private readonly eventEmitter: EventEmitter2, private readonly mailTransport: MailTransporter, private readonly tenancyContext: TenancyContext, + private readonly paymentMailTemplate: GetPaymentReceivedMailTemplate, @InjectQueue(SEND_PAYMENT_RECEIVED_MAIL_QUEUE) private readonly sendPaymentMailQueue: Queue, @@ -86,23 +88,27 @@ export class SendPaymentReceiveMailNotification { */ public getMailOptions = async ( paymentId: number, + defaultSubject: string = DEFAULT_PAYMENT_MAIL_SUBJECT, + defaultContent: string = DEFAULT_PAYMENT_MAIL_CONTENT, ): Promise => { - const paymentReceive = await this.paymentReceiveModel() + const paymentReceived = await this.paymentReceiveModel() .query() .findById(paymentId) .throwIfNotFound(); - const formatArgs = await this.textFormatter(paymentId); + const formatArgs = await this.textFormatter(paymentReceived.id); + // Retrieves the default mail options. const mailOptions = await this.contactMailNotification.getDefaultMailOptions( - paymentReceive.customerId, + paymentReceived.customerId, ); return { ...mailOptions, - subject: DEFAULT_PAYMENT_MAIL_SUBJECT, - message: DEFAULT_PAYMENT_MAIL_CONTENT, - ...formatArgs, + message: defaultContent, + subject: defaultSubject, + attachPdf: true, + formatArgs, }; }; @@ -115,7 +121,40 @@ export class SendPaymentReceiveMailNotification { invoiceId: number, ): Promise> => { const payment = await this.getPaymentService.getPaymentReceive(invoiceId); - return transformPaymentReceivedToMailDataArgs(payment); + const commonArgs = await this.contactMailNotification.getCommonFormatArgs(); + const paymentArgs = transformPaymentReceivedToMailDataArgs(payment); + + return { + ...commonArgs, + ...paymentArgs, + }; + }; + + /** + * Formats the mail options of the given payment receive. + * @param {number} paymentReceiveId + * @param {PaymentReceiveMailOpts} mailOptions + * @returns {Promise} + */ + public formattedMailOptions = async ( + paymentReceiveId: number, + mailOptions: PaymentReceiveMailOpts, + ): Promise => { + const formatterArgs = await this.textFormatter(paymentReceiveId); + const formattedOptions = + await this.contactMailNotification.formatMailOptions( + mailOptions, + formatterArgs, + ); + // Retrieves the mail template. + const message = await this.paymentMailTemplate.getMailTemplate( + paymentReceiveId, + { + message: formattedOptions.message, + preview: formattedOptions.message, + }, + ); + return { ...formattedOptions, message }; }; /** diff --git a/packages/server/src/modules/PaymentReceived/queries/GetPaymentReceivedMailState.service.ts b/packages/server/src/modules/PaymentReceived/queries/GetPaymentReceivedMailState.service.ts index a4b830f4e..da3e12c69 100644 --- a/packages/server/src/modules/PaymentReceived/queries/GetPaymentReceivedMailState.service.ts +++ b/packages/server/src/modules/PaymentReceived/queries/GetPaymentReceivedMailState.service.ts @@ -36,6 +36,7 @@ export class GetPaymentReceivedMailState { const mailOptions = await this.paymentReceivedMail.getMailOptions(paymentId); + const transformed = await this.transformer.transform( paymentReceive, new GetPaymentReceivedMailStateTransformer(), diff --git a/packages/server/src/modules/PaymentReceived/queries/GetPaymentReceivedMailTemplate.service.ts b/packages/server/src/modules/PaymentReceived/queries/GetPaymentReceivedMailTemplate.service.ts index 1dfdbac90..6c98dc3cd 100644 --- a/packages/server/src/modules/PaymentReceived/queries/GetPaymentReceivedMailTemplate.service.ts +++ b/packages/server/src/modules/PaymentReceived/queries/GetPaymentReceivedMailTemplate.service.ts @@ -42,8 +42,7 @@ export class GetPaymentReceivedMailTemplate { /** * Retrieves the mail template html content. - * @param {number} tenantId - * @param {number} paymentReceivedId + * @param {number} paymentReceivedId * @param {Partial} overrideAttributes * @returns */ diff --git a/packages/server/src/modules/SaleEstimates/models/SaleEstimate.ts b/packages/server/src/modules/SaleEstimates/models/SaleEstimate.ts index 727dba04a..ca5f10a5a 100644 --- a/packages/server/src/modules/SaleEstimates/models/SaleEstimate.ts +++ b/packages/server/src/modules/SaleEstimates/models/SaleEstimate.ts @@ -1,5 +1,6 @@ import * as moment from 'moment'; import { Model } from 'objection'; +import { defaultTo } from 'lodash'; import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel'; import { ExportableModel } from '@/modules/Export/decorators/ExportableModel.decorator'; import { ImportableModel } from '@/modules/Import/decorators/Import.decorator'; @@ -8,6 +9,7 @@ import { SaleEstimateMeta } from './SaleEstimate.meta'; import { ItemEntry } from '@/modules/TransactionItemEntry/models/ItemEntry'; import { Document } from '@/modules/ChromiumlyTenancy/models/Document'; import { Customer } from '@/modules/Customers/models/Customer'; +import { DiscountType } from '@/common/types/Discount'; @ExportableModel() @ImportableModel() @@ -42,6 +44,11 @@ export class SaleEstimate extends TenantBaseModel { branchId?: number; warehouseId?: number; + discount: number; + discountType: DiscountType; + + adjustment: number; + public entries!: ItemEntry[]; public attachments!: Document[]; public customer!: Customer; @@ -71,9 +78,69 @@ export class SaleEstimate extends TenantBaseModel { 'isConvertedToInvoice', 'isApproved', 'isRejected', + 'discountAmount', + 'discountPercentage', + 'total', + 'totalLocal', + 'subtotal', + 'subtotalLocal', ]; } + /** + * Estimate subtotal. + * @returns {number} + */ + get subtotal() { + return this.amount;; + } + + /** + * Estimate subtotal in local currency. + * @returns {number} + */ + get subtotalLocal() { + return this.localAmount; + } + + /** + * Discount amount. + * @returns {number} + */ + get discountAmount() { + return this.discountType === DiscountType.Amount + ? this.discount + : this.subtotal * (this.discount / 100); + } + + /** + * Discount percentage. + * @returns {number | null} + */ + get discountPercentage(): number | null { + return this.discountType === DiscountType.Percentage + ? this.discount + : null; + } + + /** + * Estimate total. + * @returns {number} + */ + get total() { + const adjustmentAmount = defaultTo(this.adjustment, 0); + + return this.subtotal - this.discountAmount - adjustmentAmount; + } + + /** + * Estimate total in local currency. + * @returns {number} + */ + get totalLocal() { + return this.total * this.exchangeRate; + } + /** * Estimate amount in local currency. * @returns {number} diff --git a/packages/server/src/modules/SaleEstimates/queries/GetEstimateMailTemplateAttributes.transformer.ts b/packages/server/src/modules/SaleEstimates/queries/GetEstimateMailTemplateAttributes.transformer.ts index 5c78ca4c8..a90d355ef 100644 --- a/packages/server/src/modules/SaleEstimates/queries/GetEstimateMailTemplateAttributes.transformer.ts +++ b/packages/server/src/modules/SaleEstimates/queries/GetEstimateMailTemplateAttributes.transformer.ts @@ -28,6 +28,12 @@ export class GetEstimateMailTemplateAttributesTransformer extends Transformer { 'dueAmount', 'dueAmountLabel', + 'discount', + 'discountLabel', + + 'adjustment', + 'adjustmentLabel', + 'viewEstimateButtonLabel', 'viewEstimateButtonUrl', @@ -103,7 +109,7 @@ export class GetEstimateMailTemplateAttributesTransformer extends Transformer { * Estimate total. */ public total(): string { - return this.options.estimate.formattedAmount; + return this.options.estimate.totalFormatted; } /** @@ -114,11 +120,43 @@ export class GetEstimateMailTemplateAttributesTransformer extends Transformer { return 'Total'; } + /** + * Estimate discount. + * @returns {string} + */ + public discount(): string { + return this.options.estimate?.discountAmountFormatted; + } + + /** + * Estimate discount label. + * @returns {string} + */ + public discountLabel(): string { + return 'Discount'; + } + + /** + * Estimate adjustment. + * @returns {string} + */ + public adjustment(): string { + return this.options.estimate?.adjustmentFormatted; + } + + /** + * Estimate adjustment label. + * @returns {string} + */ + public adjustmentLabel(): string { + return 'Adjustment'; + } + /** * Estimate subtotal. */ public subtotal(): string { - return this.options.estimate.formattedAmount; + return this.options.estimate.formattedSubtotal; } /** diff --git a/packages/server/src/modules/SaleEstimates/queries/GetSaleEstimateMailState.transformer.ts b/packages/server/src/modules/SaleEstimates/queries/GetSaleEstimateMailState.transformer.ts index fa0ff62a3..1da4b3d91 100644 --- a/packages/server/src/modules/SaleEstimates/queries/GetSaleEstimateMailState.transformer.ts +++ b/packages/server/src/modules/SaleEstimates/queries/GetSaleEstimateMailState.transformer.ts @@ -20,6 +20,16 @@ export class GetSaleEstimateMailStateTransformer extends SaleEstimateTransfromer 'subtotal', 'subtotalFormatted', + 'discountAmount', + 'discountAmountFormatted', + 'discountPercentage', + 'discountPercentageFormatted', + 'discountLabel', + + 'adjustment', + 'adjustmentFormatted', + 'adjustmentLabel', + 'estimateNumber', 'entries', @@ -98,15 +108,14 @@ export class GetSaleEstimateMailStateTransformer extends SaleEstimateTransfromer } /** - * Retrieves the formatted total of the estimate. + * Retrieves the discount label of the estimate. * @param estimate * @returns {string} */ - protected totalFormatted(estimate) { - return this.formatMoney(estimate.amount, { - currencyCode: estimate.currencyCode, - money: true, - }); + protected discountLabel(estimate) { + return estimate.discountType === 'percentage' + ? `Discount [${estimate.discountPercentageFormatted}]` + : 'Discount'; } /** @@ -115,7 +124,7 @@ export class GetSaleEstimateMailStateTransformer extends SaleEstimateTransfromer * @returns {string} */ protected subtotalFormatted = (estimate) => { - return this.formatNumber(estimate.amount, { money: false }); + return this.formattedSubtotal(estimate); }; /** diff --git a/packages/server/src/modules/SaleEstimates/queries/SaleEstimate.transformer.ts b/packages/server/src/modules/SaleEstimates/queries/SaleEstimate.transformer.ts index 8558625f3..053d3112d 100644 --- a/packages/server/src/modules/SaleEstimates/queries/SaleEstimate.transformer.ts +++ b/packages/server/src/modules/SaleEstimates/queries/SaleEstimate.transformer.ts @@ -17,6 +17,13 @@ export class SaleEstimateTransfromer extends Transformer { 'formattedDeliveredAtDate', 'formattedApprovedAtDate', 'formattedRejectedAtDate', + + 'discountAmountFormatted', + 'discountPercentageFormatted', + 'adjustmentFormatted', + 'totalFormatted', + 'totalLocalFormatted', + 'formattedCreatedAt', 'entries', 'attachments', @@ -97,6 +104,65 @@ export class SaleEstimateTransfromer extends Transformer { return this.formatNumber(estimate.amount, { money: false }); }; + + + /** + * Retrieves formatted discount amount. + * @param {SaleEstimate} estimate + * @returns {string} + */ + protected discountAmountFormatted = (estimate: SaleEstimate): string => { + return this.formatNumber(estimate.discountAmount, { + currencyCode: estimate.currencyCode, + excerptZero: true, + }); + }; + + /** + * Retrieves formatted discount percentage. + * @param estimate + * @returns {string} + */ + protected discountPercentageFormatted = (estimate: SaleEstimate): string => { + return estimate.discountPercentage + ? `${estimate.discountPercentage}%` + : ''; + }; + + /** + * Retrieves formatted adjustment amount. + * @param estimate + * @returns {string} + */ + protected adjustmentFormatted = (estimate: SaleEstimate): string => { + return this.formatMoney(estimate.adjustment, { + currencyCode: estimate.currencyCode, + excerptZero: true, + }); + }; + + /** + * Retrieves the formatted estimate total. + * @returns {string} + */ + protected totalFormatted = (estimate: SaleEstimate): string => { + return this.formatMoney(estimate.total, { + currencyCode: estimate.currencyCode, + }); + }; + + /** + * Retrieves the formatted estimate total in local currency. + * @param estimate + * @returns {string} + */ + protected totalLocalFormatted = (estimate: SaleEstimate): string => { + return this.formatMoney(estimate.totalLocal, { + currencyCode: estimate.currencyCode, + }); + }; + + /** * Retrieves the entries of the sale estimate. * @param {ISaleEstimate} estimate diff --git a/packages/server/src/modules/SaleReceipts/SaleReceiptApplication.service.ts b/packages/server/src/modules/SaleReceipts/SaleReceiptApplication.service.ts index 4dde26cbc..33a83ca6e 100644 --- a/packages/server/src/modules/SaleReceipts/SaleReceiptApplication.service.ts +++ b/packages/server/src/modules/SaleReceipts/SaleReceiptApplication.service.ts @@ -116,32 +116,6 @@ export class SaleReceiptApplication { return this.getSaleReceiptPdfService.saleReceiptPdf(saleReceiptId); } - /** - * Notify receipt customer by SMS of the given sale receipt. - * @param {number} tenantId - * @param {number} saleReceiptId - * @returns - */ - // public saleReceiptNotifyBySms(tenantId: number, saleReceiptId: number) { - // return this.saleReceiptNotifyBySmsService.notifyBySms( - // tenantId, - // saleReceiptId, - // ); - // } - - /** - * Retrieves sms details of the given sale receipt. - * @param {number} tenantId - * @param {number} saleReceiptId - * @returns - */ - // public getSaleReceiptSmsDetails(tenantId: number, saleReceiptId: number) { - // return this.saleReceiptNotifyBySmsService.smsDetails( - // tenantId, - // saleReceiptId, - // ); - // } - /** * Sends the receipt mail of the given sale receipt. * @param {number} tenantId @@ -159,17 +133,6 @@ export class SaleReceiptApplication { ); } - /** - * Retrieves the default mail options of the given sale receipt. - * @param {number} saleReceiptId - Sale receipt identifier. - * @returns {Promise} - */ - public getSaleReceiptMail( - saleReceiptId: number, - ): Promise { - return this.saleReceiptNotifyByMailService.getMailOptions(saleReceiptId); - } - /** * Retrieves the current state of the sale receipt. * @returns {Promise} - A promise resolving to the sale receipt state. @@ -191,7 +154,7 @@ export class SaleReceiptApplication { * Retrieves the mail state of the given sale receipt. * @param {number} saleReceiptId */ - public getSaleReceiptMailState( + public getSaleReceiptMail( saleReceiptId: number, ): Promise { return this.getSaleReceiptMailStateService.getMailState(saleReceiptId); diff --git a/packages/server/src/modules/SaleReceipts/queries/SaleReceiptTransformer.ts b/packages/server/src/modules/SaleReceipts/queries/SaleReceiptTransformer.ts index f339c4798..b7eddc152 100644 --- a/packages/server/src/modules/SaleReceipts/queries/SaleReceiptTransformer.ts +++ b/packages/server/src/modules/SaleReceipts/queries/SaleReceiptTransformer.ts @@ -19,6 +19,11 @@ export class SaleReceiptTransformer extends Transformer { 'subtotalLocalFormatted', 'totalFormatted', 'totalLocalFormatted', + + 'discountAmountFormatted', + 'discountPercentageFormatted', + 'adjustmentFormatted', + 'entries', 'attachments', ]; @@ -107,7 +112,6 @@ export class SaleReceiptTransformer extends Transformer { /** * Retrieves formatted discount amount. - * @param receipt * @returns {string} */ protected discountAmountFormatted = (receipt: SaleReceipt): string => { @@ -118,7 +122,6 @@ export class SaleReceiptTransformer extends Transformer { /** * Retrieves formatted discount percentage. - * @param receipt * @returns {string} */ protected discountPercentageFormatted = (receipt: SaleReceipt): string => { @@ -127,7 +130,6 @@ export class SaleReceiptTransformer extends Transformer { /** * Retrieves formatted adjustment amount. - * @param receipt * @returns {string} */ protected adjustmentFormatted = (receipt: SaleReceipt): string => {