diff --git a/packages/server/src/constants/event-tracker.ts b/packages/server/src/constants/event-tracker.ts index b033abe8c..12a89d6bd 100644 --- a/packages/server/src/constants/event-tracker.ts +++ b/packages/server/src/constants/event-tracker.ts @@ -13,15 +13,19 @@ export const SALE_ESTIMATE_EDITED = 'Sale estimate edited'; export const SALE_ESTIMATE_DELETED = 'Sale estimate deleted'; export const SALE_ESTIMATE_PDF_VIEWED = 'Sale estimate PDF viewed'; export const SALE_ESTIMATE_VIEWED = 'Sale estimate viewed'; +export const SALE_ESTIMATE_MAIL_SENT = 'Sale estimate mail sent'; export const PAYMENT_RECEIVED_CREATED = 'Payment received created'; export const PAYMENT_RECEIVED_EDITED = 'payment received edited'; export const PAYMENT_RECEIVED_DELETED = 'Payment received deleted'; export const PAYMENT_RECEIVED_PDF_VIEWED = 'Payment received PDF viewed'; +export const PAYMENT_RECEIVED_MAIL_SENT = 'Payment received mail sent'; export const SALE_RECEIPT_PDF_VIEWED = 'Sale credit PDF viewed'; +export const SALE_RECEIPT_MAIL_SENT = 'Sale credit mail sent'; export const CREDIT_NOTE_PDF_VIEWED = 'Credit note PDF viewed'; +export const CREDIT_NOTE_MAIL_SENT = 'Credit note mail sent'; export const BILL_CREATED = 'Bill created'; export const BILL_EDITED = 'Bill edited'; diff --git a/packages/server/src/interfaces/Mailable.ts b/packages/server/src/interfaces/Mailable.ts index 7412f2079..c77895225 100644 --- a/packages/server/src/interfaces/Mailable.ts +++ b/packages/server/src/interfaces/Mailable.ts @@ -36,7 +36,7 @@ export interface CommonMailOptions { to: Array; cc?: Array; bcc?: Array; - data?: Record; + formatArgs?: Record; } export interface CommonMailOptionsDTO extends Partial { diff --git a/packages/server/src/services/EventsTracker/events/PaymentReceivedEventsTracker.ts b/packages/server/src/services/EventsTracker/events/PaymentReceivedEventsTracker.ts index eb6d5dc39..9d45c3dde 100644 --- a/packages/server/src/services/EventsTracker/events/PaymentReceivedEventsTracker.ts +++ b/packages/server/src/services/EventsTracker/events/PaymentReceivedEventsTracker.ts @@ -12,6 +12,7 @@ import { PAYMENT_RECEIVED_EDITED, PAYMENT_RECEIVED_DELETED, PAYMENT_RECEIVED_PDF_VIEWED, + PAYMENT_RECEIVED_MAIL_SENT, } from '@/constants/event-tracker'; @Service() @@ -39,6 +40,10 @@ export class PaymentReceivedEventsTracker extends EventSubscriber { events.paymentReceive.onPdfViewed, this.handleTrackPdfViewedPaymentReceivedEvent ); + bus.subscribe( + events.paymentReceive.onMailSent, + this.handleTrackMailSentPaymentReceivedEvent + ); } private handleTrackPaymentReceivedCreatedEvent = ({ @@ -80,4 +85,14 @@ export class PaymentReceivedEventsTracker extends EventSubscriber { properties: {}, }); }; + + private handleTrackMailSentPaymentReceivedEvent = ({ + tenantId, + }: IPaymentReceivedDeletedPayload) => { + this.posthog.trackEvent({ + distinctId: `tenant-${tenantId}`, + event: PAYMENT_RECEIVED_MAIL_SENT, + properties: {}, + }); + }; } diff --git a/packages/server/src/services/EventsTracker/events/SaleEstimateEventsTracker.ts b/packages/server/src/services/EventsTracker/events/SaleEstimateEventsTracker.ts index 263044bff..25c97e4a3 100644 --- a/packages/server/src/services/EventsTracker/events/SaleEstimateEventsTracker.ts +++ b/packages/server/src/services/EventsTracker/events/SaleEstimateEventsTracker.ts @@ -13,6 +13,7 @@ import { SALE_ESTIMATE_DELETED, SALE_ESTIMATE_PDF_VIEWED, SALE_ESTIMATE_VIEWED, + SALE_ESTIMATE_MAIL_SENT, } from '@/constants/event-tracker'; @Service() @@ -44,6 +45,10 @@ export class SaleEstimateEventsTracker extends EventSubscriber { events.saleEstimate.onViewed, this.handleTrackViewedEstimateEvent ); + bus.subscribe( + events.saleEstimate.onMailSent, + this.handleTrackMailSentEstimateEvent + ); } private handleTrackEstimateCreatedEvent = ({ @@ -93,4 +98,12 @@ export class SaleEstimateEventsTracker extends EventSubscriber { properties: {}, }); }; + + private handleTrackMailSentEstimateEvent = ({ tenantId }) => { + this.posthog.trackEvent({ + distinctId: `tenant-${tenantId}`, + event: SALE_ESTIMATE_MAIL_SENT, + properties: {}, + }); + }; } diff --git a/packages/server/src/services/MailNotification/ContactMailNotification.ts b/packages/server/src/services/MailNotification/ContactMailNotification.ts index 725e28821..d798adce5 100644 --- a/packages/server/src/services/MailNotification/ContactMailNotification.ts +++ b/packages/server/src/services/MailNotification/ContactMailNotification.ts @@ -46,7 +46,7 @@ export class ContactMailNotification { * @param {number} tenantId - Tenant id. * @returns {Promise} */ - public async parseMailOptions( + public async formatMailOptions( tenantId: number, mailOptions: CommonMailOptions, formatterArgs?: Record diff --git a/packages/server/src/services/MailNotification/utils.ts b/packages/server/src/services/MailNotification/utils.ts index 115a01313..63e9550ba 100644 --- a/packages/server/src/services/MailNotification/utils.ts +++ b/packages/server/src/services/MailNotification/utils.ts @@ -44,3 +44,13 @@ export function validateRequiredMailOptions( throw new ServiceError(ERRORS.MAIL_BODY_NOT_FOUND); } } + +export const mergeAndValidateMailOptions = ( + mailOptions: CommonMailOptions, + overridedOptions: Partial +): CommonMailOptions => { + const parsedMessageOptions = parseMailOptions(mailOptions, overridedOptions); + validateRequiredMailOptions(parsedMessageOptions); + + return parsedMessageOptions; +}; diff --git a/packages/server/src/services/Sales/Estimates/SendSaleEstimateMail.ts b/packages/server/src/services/Sales/Estimates/SendSaleEstimateMail.ts index 0ac580b90..697a4993e 100644 --- a/packages/server/src/services/Sales/Estimates/SendSaleEstimateMail.ts +++ b/packages/server/src/services/Sales/Estimates/SendSaleEstimateMail.ts @@ -13,9 +13,10 @@ import { SaleEstimateMailOptionsDTO, } from '@/interfaces'; import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification'; -import { parseAndValidateMailOptions } from '@/services/MailNotification/utils'; +import { mergeAndValidateMailOptions } from '@/services/MailNotification/utils'; import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; import events from '@/subscribers/events'; +import { transformEstimateToMailDataArgs } from './utils'; @Service() export class SendSaleEstimateMail { @@ -65,23 +66,17 @@ export class SendSaleEstimateMail { } /** - * Formates the text of the mail. + * Formate the text of the mail. * @param {number} tenantId - Tenant id. * @param {number} estimateId - Estimate id. * @returns {Promise>} */ - public formatterData = async (tenantId: number, estimateId: number) => { + public formatterArgs = async (tenantId: number, estimateId: number) => { const estimate = await this.getSaleEstimateService.getEstimate( tenantId, estimateId ); - return { - CustomerName: estimate.customer.displayName, - EstimateNumber: estimate.estimateNumber, - EstimateDate: estimate.formattedEstimateDate, - EstimateAmount: estimate.formattedAmount, - EstimateExpirationDate: estimate.formattedExpirationDate, - }; + return transformEstimateToMailDataArgs(estimate); }; /** @@ -92,7 +87,9 @@ export class SendSaleEstimateMail { */ public getMailOptions = async ( tenantId: number, - saleEstimateId: number + saleEstimateId: number, + defaultSubject: string = DEFAULT_ESTIMATE_REMINDER_MAIL_SUBJECT, + defaultMessage: string = DEFAULT_ESTIMATE_REMINDER_MAIL_CONTENT ): Promise => { const { SaleEstimate } = this.tenancy.models(tenantId); @@ -100,22 +97,45 @@ export class SendSaleEstimateMail { .findById(saleEstimateId) .throwIfNotFound(); - const formatterData = await this.formatterData(tenantId, saleEstimateId); + const formatArgs = await this.formatterArgs(tenantId, saleEstimateId); - const mailOptions = await this.contactMailNotification.getMailOptions( - tenantId, - saleEstimate.customerId, - DEFAULT_ESTIMATE_REMINDER_MAIL_SUBJECT, - DEFAULT_ESTIMATE_REMINDER_MAIL_CONTENT, - formatterData - ); + const mailOptions = + await this.contactMailNotification.getDefaultMailOptions( + tenantId, + saleEstimate.customerId + ); return { ...mailOptions, - data: formatterData, + message: defaultMessage, + subject: defaultSubject, attachEstimate: true, + formatArgs, }; }; + /** + * Formats the given mail options. + * @param {number} tenantId + * @param {number} saleEstimateId + * @param {SaleEstimateMailOptions} mailOptions + * @returns {Promise} + */ + public formatMailOptions = async ( + tenantId: number, + saleEstimateId: number, + mailOptions: SaleEstimateMailOptions + ): Promise => { + const formatterArgs = await this.formatterArgs(tenantId, saleEstimateId); + + const formattedOptions = + await this.contactMailNotification.formatMailOptions( + tenantId, + mailOptions, + formatterArgs + ); + return { ...formattedOptions }; + }; + /** * Sends the mail notification of the given sale estimate. * @param {number} tenantId @@ -133,27 +153,52 @@ export class SendSaleEstimateMail { saleEstimateId ); // Overrides and validates the given mail options. - const messageOpts = parseAndValidateMailOptions( + const parsedMessageOptions = mergeAndValidateMailOptions( localMessageOpts, messageOptions + ) as SaleEstimateMailOptions; + + const formattedOptions = await this.formatMailOptions( + tenantId, + saleEstimateId, + parsedMessageOptions ); const mail = new Mail() - .setSubject(messageOpts.subject) - .setTo(messageOpts.to) - .setContent(messageOpts.body); + .setSubject(formattedOptions.subject) + .setTo(formattedOptions.to) + .setContent(formattedOptions.message); + + // Attaches the estimate pdf to the mail. + if (formattedOptions.attachEstimate) { + // Retrieves the estimate pdf and attaches it to the mail. + const [estimatePdfBuffer, estimateFilename] = + await this.estimatePdf.getSaleEstimatePdf(tenantId, saleEstimateId); - if (messageOpts.attachEstimate) { - const estimatePdfBuffer = await this.estimatePdf.getSaleEstimatePdf( - tenantId, - saleEstimateId - ); mail.setAttachments([ { - filename: messageOpts.data?.EstimateNumber || 'estimate.pdf', + filename: `${estimateFilename}.pdf`, content: estimatePdfBuffer, }, ]); } + + const eventPayload = { + tenantId, + saleEstimateId, + messageOptions, + formattedOptions, + }; + // Triggers `onSaleEstimateMailSend` event. + await this.eventPublisher.emitAsync( + events.saleEstimate.onMailSend, + eventPayload as ISaleEstimateMailPresendEvent + ); await mail.send(); + + // Triggers `onSaleEstimateMailSent` event. + await this.eventPublisher.emitAsync( + events.saleEstimate.onMailSent, + eventPayload as ISaleEstimateMailPresendEvent + ); } } diff --git a/packages/server/src/services/Sales/Estimates/constants.ts b/packages/server/src/services/Sales/Estimates/constants.ts index b42d66763..798e8f090 100644 --- a/packages/server/src/services/Sales/Estimates/constants.ts +++ b/packages/server/src/services/Sales/Estimates/constants.ts @@ -1,16 +1,16 @@ export const DEFAULT_ESTIMATE_REMINDER_MAIL_SUBJECT = - 'Estimate {EstimateNumber} is awaiting your approval'; -export const DEFAULT_ESTIMATE_REMINDER_MAIL_CONTENT = `

Dear {CustomerName}

+ 'Estimate {Estimate Number} is awaiting your approval'; +export const DEFAULT_ESTIMATE_REMINDER_MAIL_CONTENT = `

Dear {Customer Name}

Thank you for your business, You can view or print your estimate from attachements.

-Estimate #{EstimateNumber}
-Expiration Date : {EstimateExpirationDate}
-Amount : {EstimateAmount}
+Estimate #{Estimate Number}
+Expiration Date : {Estimate Expiration Date}
+Amount : {Estimate Amount}

Regards
-{CompanyName} +{Company Name}

`; diff --git a/packages/server/src/services/Sales/Estimates/utils.ts b/packages/server/src/services/Sales/Estimates/utils.ts index 9c7d92de5..efd301af3 100644 --- a/packages/server/src/services/Sales/Estimates/utils.ts +++ b/packages/server/src/services/Sales/Estimates/utils.ts @@ -22,3 +22,13 @@ export const transformEstimateToPdfTemplate = ( customerAddress: contactAddressTextFormat(estimate.customer), }; }; + +export const transformEstimateToMailDataArgs = (estimate: any) => { + return { + 'Customer Name': estimate.customer.displayName, + 'Estimate Number': estimate.estimateNumber, + 'Estimate Date': estimate.formattedEstimateDate, + 'Estimate Amount': estimate.formattedAmount, + 'Estimate Expiration Date': estimate.formattedExpirationDate, + }; +}; diff --git a/packages/server/src/services/Sales/Invoices/GetInvoicePaymentMail.ts b/packages/server/src/services/Sales/Invoices/GetInvoicePaymentMail.ts index 4a41b3f26..4e13e7d02 100644 --- a/packages/server/src/services/Sales/Invoices/GetInvoicePaymentMail.ts +++ b/packages/server/src/services/Sales/Invoices/GetInvoicePaymentMail.ts @@ -21,6 +21,7 @@ export class GetInvoicePaymentMail { /** * Retrieves the mail template attributes of the given invoice. + * Invoice template attributes are composed of the invoice and branding template attributes. * @param {number} tenantId - Tenant id. * @param {number} invoiceId - Invoice id. */ diff --git a/packages/server/src/services/Sales/Invoices/SendInvoiceInvoiceMailCommon.ts b/packages/server/src/services/Sales/Invoices/SendInvoiceInvoiceMailCommon.ts index 16ee5bd9f..4edfe3464 100644 --- a/packages/server/src/services/Sales/Invoices/SendInvoiceInvoiceMailCommon.ts +++ b/packages/server/src/services/Sales/Invoices/SendInvoiceInvoiceMailCommon.ts @@ -75,20 +75,22 @@ export class SendSaleInvoiceMailCommon { tenantId, invoiceId ); - const parsedOptions = await this.contactMailNotification.parseMailOptions( - tenantId, - mailOptions, - formatterArgs - ); + const formattedOptions = + await this.contactMailNotification.formatMailOptions( + tenantId, + mailOptions, + formatterArgs + ); const message = await this.getInvoicePaymentMail.getMailTemplate( tenantId, invoiceId, { // # Invoice message - invoiceMessage: parsedOptions.message, + invoiceMessage: formattedOptions.message, + preview: formattedOptions.message, } ); - return { ...parsedOptions, message }; + return { ...formattedOptions, message }; } /** @@ -111,13 +113,13 @@ export class SendSaleInvoiceMailCommon { ); return { ...commonArgs, - ['Customer Name']: invoice.customer.displayName, - ['Invoice Number']: invoice.invoiceNo, - ['Invoice DueAmount']: invoice.dueAmountFormatted, - ['Invoice DueDate']: invoice.dueDateFormatted, - ['Invoice Date']: invoice.invoiceDateFormatted, - ['Invoice Amount']: invoice.totalFormatted, - ['Overdue Days']: invoice.overdueDays, + 'Customer Name': invoice.customer.displayName, + 'Invoice Number': invoice.invoiceNo, + 'Invoice DueAmount': invoice.dueAmountFormatted, + 'Invoice DueDate': invoice.dueDateFormatted, + 'Invoice Date': invoice.invoiceDateFormatted, + 'Invoice Amount': invoice.totalFormatted, + 'Overdue Days': invoice.overdueDays, }; }; } diff --git a/packages/server/src/services/Sales/Invoices/SendSaleInvoiceMail.ts b/packages/server/src/services/Sales/Invoices/SendSaleInvoiceMail.ts index f95f68fb3..cee271fcd 100644 --- a/packages/server/src/services/Sales/Invoices/SendSaleInvoiceMail.ts +++ b/packages/server/src/services/Sales/Invoices/SendSaleInvoiceMail.ts @@ -3,10 +3,7 @@ import Mail from '@/lib/Mail'; import { ISaleInvoiceMailSend, SendInvoiceMailDTO } from '@/interfaces'; import { SaleInvoicePdf } from './SaleInvoicePdf'; import { SendSaleInvoiceMailCommon } from './SendInvoiceInvoiceMailCommon'; -import { - parseMailOptions, - validateRequiredMailOptions, -} from '@/services/MailNotification/utils'; +import { mergeAndValidateMailOptions } from '@/services/MailNotification/utils'; import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; import events from '@/subscribers/events'; @@ -18,12 +15,12 @@ export class SendSaleInvoiceMail { @Inject() private invoiceMail: SendSaleInvoiceMailCommon; - @Inject('agenda') - private agenda: any; - @Inject() private eventPublisher: EventPublisher; + @Inject('agenda') + private agenda: any; + /** * Sends the invoice mail of the given sale invoice. * @param {number} tenantId @@ -67,13 +64,10 @@ export class SendSaleInvoiceMail { saleInvoiceId ); // Merges message options with default options and parses the options values. - const parsedMessageOptions = parseMailOptions( + const parsedMessageOptions = mergeAndValidateMailOptions( defaultMessageOptions, messageOptions ); - // Validates the required mail options. - validateRequiredMailOptions(parsedMessageOptions); - const formattedMessageOptions = await this.invoiceMail.formatInvoiceMailOptions( tenantId, diff --git a/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedMailNotification.ts b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedMailNotification.ts index 5429b4441..49276f8c1 100644 --- a/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedMailNotification.ts +++ b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedMailNotification.ts @@ -13,9 +13,10 @@ import { } from './constants'; import { GetPaymentReceived } from './GetPaymentReceived'; import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification'; -import { parseAndValidateMailOptions } from '@/services/MailNotification/utils'; +import { mergeAndValidateMailOptions } from '@/services/MailNotification/utils'; import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; import events from '@/subscribers/events'; +import { transformPaymentReceivedToMailDataArgs } from './utils'; @Service() export class SendPaymentReceiveMailNotification { @@ -77,15 +78,19 @@ export class SendPaymentReceiveMailNotification { .findById(paymentId) .throwIfNotFound(); - const formatterData = await this.textFormatter(tenantId, paymentId); + const formatArgs = await this.textFormatter(tenantId, paymentId); - return this.contactMailNotification.getMailOptions( - tenantId, - paymentReceive.customerId, - DEFAULT_PAYMENT_MAIL_SUBJECT, - DEFAULT_PAYMENT_MAIL_CONTENT, - formatterData - ); + const mailOptions = + await this.contactMailNotification.getDefaultMailOptions( + tenantId, + paymentReceive.customerId + ); + return { + ...mailOptions, + subject: DEFAULT_PAYMENT_MAIL_SUBJECT, + message: DEFAULT_PAYMENT_MAIL_CONTENT, + ...formatArgs, + }; }; /** @@ -103,12 +108,7 @@ export class SendPaymentReceiveMailNotification { tenantId, invoiceId ); - return { - CustomerName: payment.customer.displayName, - PaymentNumber: payment.payment_receive_no, - PaymentDate: payment.formattedPaymentDate, - PaymentAmount: payment.formattedAmount, - }; + return transformPaymentReceivedToMailDataArgs(payment); }; /** @@ -128,14 +128,31 @@ export class SendPaymentReceiveMailNotification { paymentReceiveId ); // Parsed message opts with default options. - const parsedMessageOpts = parseAndValidateMailOptions( + const parsedMessageOpts = mergeAndValidateMailOptions( defaultMessageOpts, messageDTO ); - await new Mail() + const mail = new Mail() .setSubject(parsedMessageOpts.subject) .setTo(parsedMessageOpts.to) - .setContent(parsedMessageOpts.body) - .send(); + .setContent(parsedMessageOpts.message); + + const eventPayload = { + tenantId, + paymentReceiveId, + messageOptions: parsedMessageOpts, + }; + // Triggers `onPaymentReceiveMailSend` event. + await this.eventPublisher.emitAsync( + events.paymentReceive.onMailSend, + eventPayload + ); + await mail.send(); + + // Triggers `onPaymentReceiveMailSent` event. + await this.eventPublisher.emitAsync( + events.paymentReceive.onMailSent, + eventPayload + ); } } diff --git a/packages/server/src/services/Sales/PaymentReceived/utils.ts b/packages/server/src/services/Sales/PaymentReceived/utils.ts index bfd1ad8ea..533783b77 100644 --- a/packages/server/src/services/Sales/PaymentReceived/utils.ts +++ b/packages/server/src/services/Sales/PaymentReceived/utils.ts @@ -21,3 +21,12 @@ export const transformPaymentReceivedToPdfTemplate = ( customerAddress: contactAddressTextFormat(payment.customer), }; }; + +export const transformPaymentReceivedToMailDataArgs = (payment: any) => { + return { + 'Customer Name': payment.customer.displayName, + 'Payment Number': payment.paymentReceiveNo, + 'Payment Date': payment.formattedPaymentDate, + 'Payment Amount': payment.formattedAmount, + }; +}; diff --git a/packages/server/src/services/Sales/Receipts/SaleReceiptMailNotification.ts b/packages/server/src/services/Sales/Receipts/SaleReceiptMailNotification.ts index 24add40cc..7683c6c81 100644 --- a/packages/server/src/services/Sales/Receipts/SaleReceiptMailNotification.ts +++ b/packages/server/src/services/Sales/Receipts/SaleReceiptMailNotification.ts @@ -13,9 +13,10 @@ import { SaleReceiptMailOptsDTO, } from '@/interfaces'; import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification'; -import { parseAndValidateMailOptions } from '@/services/MailNotification/utils'; +import { mergeAndValidateMailOptions } from '@/services/MailNotification/utils'; import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; import events from '@/subscribers/events'; +import { transformReceiptToMailDataArgs } from './utils'; @Service() export class SaleReceiptMailNotification { @@ -79,18 +80,19 @@ export class SaleReceiptMailNotification { .findById(saleReceiptId) .throwIfNotFound(); - const formattedData = await this.textFormatter(tenantId, saleReceiptId); + const formatArgs = await this.textFormatterArgs(tenantId, saleReceiptId); - const mailOpts = await this.contactMailNotification.getMailOptions( - tenantId, - saleReceipt.customerId, - DEFAULT_RECEIPT_MAIL_SUBJECT, - DEFAULT_RECEIPT_MAIL_CONTENT, - formattedData - ); + const mailOptions = + await this.contactMailNotification.getDefaultMailOptions( + tenantId, + saleReceipt.customerId + ); return { - ...mailOpts, + ...mailOptions, + message: DEFAULT_RECEIPT_MAIL_CONTENT, + subject: DEFAULT_RECEIPT_MAIL_SUBJECT, attachReceipt: true, + formatArgs, }; } @@ -101,7 +103,7 @@ export class SaleReceiptMailNotification { * @param {string} text - The given text. * @returns {Promise} */ - public textFormatter = async ( + public textFormatterArgs = async ( tenantId: number, receiptId: number ): Promise> => { @@ -109,19 +111,14 @@ export class SaleReceiptMailNotification { tenantId, receiptId ); - return { - CustomerName: receipt.customer.displayName, - ReceiptNumber: receipt.receiptNumber, - ReceiptDate: receipt.formattedReceiptDate, - ReceiptAmount: receipt.formattedAmount, - }; + return transformReceiptToMailDataArgs(receipt); }; /** * Triggers the mail notification of the given sale receipt. * @param {number} tenantId - Tenant id. * @param {number} saleReceiptId - Sale receipt id. - * @param {SaleReceiptMailOpts} messageDTO - Overrided message options. + * @param {SaleReceiptMailOpts} messageDTO - message options. * @returns {Promise} */ public async sendMail( @@ -129,30 +126,45 @@ export class SaleReceiptMailNotification { saleReceiptId: number, messageOpts: SaleReceiptMailOptsDTO ) { - const defaultMessageOpts = await this.getMailOptions( + const defaultMessageOptions = await this.getMailOptions( tenantId, saleReceiptId ); // Merges message opts with default options. - const parsedMessageOpts = parseAndValidateMailOptions( - defaultMessageOpts, + const parsedMessageOpts = mergeAndValidateMailOptions( + defaultMessageOptions, messageOpts - ); + ) as SaleReceiptMailOpts; + const mail = new Mail() .setSubject(parsedMessageOpts.subject) .setTo(parsedMessageOpts.to) - .setContent(parsedMessageOpts.body); + .setContent(parsedMessageOpts.message); + // Attaches the receipt pdf document. if (parsedMessageOpts.attachReceipt) { // Retrieves document buffer of the receipt pdf document. - const receiptPdfBuffer = await this.receiptPdfService.saleReceiptPdf( - tenantId, - saleReceiptId - ); + const [receiptPdfBuffer, filename] = + await this.receiptPdfService.saleReceiptPdf(tenantId, saleReceiptId); + mail.setAttachments([ - { filename: 'receipt.pdf', content: receiptPdfBuffer }, + { filename: `${filename}.pdf`, content: receiptPdfBuffer }, ]); } + const eventPayload = { + tenantId, + saleReceiptId, + messageOptions: {}, + }; + await this.eventPublisher.emitAsync( + events.saleReceipt.onMailSend, + eventPayload + ); await mail.send(); + + await this.eventPublisher.emitAsync( + events.saleReceipt.onMailSent, + eventPayload + ); } } diff --git a/packages/server/src/services/Sales/Receipts/SaleReceiptsPdfService.ts b/packages/server/src/services/Sales/Receipts/SaleReceiptsPdfService.ts index 3ea9f7566..9c9c6132b 100644 --- a/packages/server/src/services/Sales/Receipts/SaleReceiptsPdfService.ts +++ b/packages/server/src/services/Sales/Receipts/SaleReceiptsPdfService.ts @@ -35,7 +35,10 @@ export class SaleReceiptsPdf { * @param {number} saleInvoiceId - * @returns {Promise} */ - public async saleReceiptPdf(tenantId: number, saleReceiptId: number) { + public async saleReceiptPdf( + tenantId: number, + saleReceiptId: number + ): Promise<[Buffer, string]> { const filename = await this.getSaleReceiptFilename(tenantId, saleReceiptId); const brandingAttributes = await this.getReceiptBrandingAttributes( diff --git a/packages/server/src/services/Sales/Receipts/constants.ts b/packages/server/src/services/Sales/Receipts/constants.ts index 547d91a56..b1a1c3898 100644 --- a/packages/server/src/services/Sales/Receipts/constants.ts +++ b/packages/server/src/services/Sales/Receipts/constants.ts @@ -1,16 +1,16 @@ export const DEFAULT_RECEIPT_MAIL_SUBJECT = - 'Receipt {ReceiptNumber} from {CompanyName}'; + 'Receipt {Receipt Number} from {Company Name}'; export const DEFAULT_RECEIPT_MAIL_CONTENT = ` -

Dear {CustomerName}

+

Dear {Customer Name}

Thank you for your business, You can view or print your receipt from attachements.

-Receipt #{ReceiptNumber}
-Amount : {ReceiptAmount}
+Receipt #{Receipt Number}
+Amount : {Receipt Amount}

Regards
-{CompanyName} +{Company Name}

`; diff --git a/packages/server/src/services/Sales/Receipts/utils.ts b/packages/server/src/services/Sales/Receipts/utils.ts index 933de982a..b075aa637 100644 --- a/packages/server/src/services/Sales/Receipts/utils.ts +++ b/packages/server/src/services/Sales/Receipts/utils.ts @@ -1,9 +1,12 @@ -import { ISaleReceipt, ISaleReceiptBrandingTemplateAttributes } from "@/interfaces"; -import { contactAddressTextFormat } from "@/utils/address-text-format"; +import { + ISaleReceipt, + ISaleReceiptBrandingTemplateAttributes, +} from '@/interfaces'; +import { contactAddressTextFormat } from '@/utils/address-text-format'; - - -export const transformReceiptToBrandingTemplateAttributes = (saleReceipt: ISaleReceipt): Partial => { +export const transformReceiptToBrandingTemplateAttributes = ( + saleReceipt: ISaleReceipt +): Partial => { return { total: saleReceipt.formattedAmount, subtotal: saleReceipt.formattedSubtotal, @@ -18,4 +21,13 @@ export const transformReceiptToBrandingTemplateAttributes = (saleReceipt: ISaleR receiptDate: saleReceipt.formattedReceiptDate, customerAddress: contactAddressTextFormat(saleReceipt.customer), }; -} \ No newline at end of file +}; + +export const transformReceiptToMailDataArgs = (saleReceipt: any) => { + return { + 'Customer Name': saleReceipt.customer.displayName, + 'Receipt Number': saleReceipt.receiptNumber, + 'Receipt Date': saleReceipt.formattedReceiptDate, + 'Receipt Amount': saleReceipt.formattedAmount, + }; +}; diff --git a/packages/server/src/subscribers/events.ts b/packages/server/src/subscribers/events.ts index d735dd1a9..cd1044500 100644 --- a/packages/server/src/subscribers/events.ts +++ b/packages/server/src/subscribers/events.ts @@ -213,7 +213,7 @@ export default { onPreMailSend: 'onSaleEstimatePreMailSend', onMailSend: 'onSaleEstimateMailSend', - onMailSent: 'onSaleEstimateMailSend', + onMailSent: 'onSaleEstimateMailSent', }, /**