refactor: notification mail services

This commit is contained in:
Ahmed Bouhuolia
2024-11-02 14:59:57 +02:00
parent 0cc80bc179
commit d09aebcebb
19 changed files with 270 additions and 123 deletions

View File

@@ -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';

View File

@@ -36,7 +36,7 @@ export interface CommonMailOptions {
to: Array<string>;
cc?: Array<string>;
bcc?: Array<string>;
data?: Record<string, any>;
formatArgs?: Record<string, any>;
}
export interface CommonMailOptionsDTO extends Partial<CommonMailOptions> {

View File

@@ -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: {},
});
};
}

View File

@@ -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: {},
});
};
}

View File

@@ -46,7 +46,7 @@ export class ContactMailNotification {
* @param {number} tenantId - Tenant id.
* @returns {Promise<CommonMailOptions>}
*/
public async parseMailOptions(
public async formatMailOptions(
tenantId: number,
mailOptions: CommonMailOptions,
formatterArgs?: Record<string, any>

View File

@@ -44,3 +44,13 @@ export function validateRequiredMailOptions(
throw new ServiceError(ERRORS.MAIL_BODY_NOT_FOUND);
}
}
export const mergeAndValidateMailOptions = (
mailOptions: CommonMailOptions,
overridedOptions: Partial<CommonMailOptions>
): CommonMailOptions => {
const parsedMessageOptions = parseMailOptions(mailOptions, overridedOptions);
validateRequiredMailOptions(parsedMessageOptions);
return parsedMessageOptions;
};

View File

@@ -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<Record<string, any>>}
*/
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<SaleEstimateMailOptions> => {
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<SaleEstimateMailOptions>}
*/
public formatMailOptions = async (
tenantId: number,
saleEstimateId: number,
mailOptions: SaleEstimateMailOptions
): Promise<SaleEstimateMailOptions> => {
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
);
}
}

View File

@@ -1,16 +1,16 @@
export const DEFAULT_ESTIMATE_REMINDER_MAIL_SUBJECT =
'Estimate {EstimateNumber} is awaiting your approval';
export const DEFAULT_ESTIMATE_REMINDER_MAIL_CONTENT = `<p>Dear {CustomerName}</p>
'Estimate {Estimate Number} is awaiting your approval';
export const DEFAULT_ESTIMATE_REMINDER_MAIL_CONTENT = `<p>Dear {Customer Name}</p>
<p>Thank you for your business, You can view or print your estimate from attachements.</p>
<p>
Estimate <strong>#{EstimateNumber}</strong><br />
Expiration Date : <strong>{EstimateExpirationDate}</strong><br />
Amount : <strong>{EstimateAmount}</strong></br />
Estimate <strong>#{Estimate Number}</strong><br />
Expiration Date : <strong>{Estimate Expiration Date}</strong><br />
Amount : <strong>{Estimate Amount}</strong></br />
</p>
<p>
<i>Regards</i><br />
<i>{CompanyName}</i>
<i>{Company Name}</i>
</p>
`;

View File

@@ -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,
};
};

View File

@@ -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.
*/

View File

@@ -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,
};
};
}

View File

@@ -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,

View File

@@ -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
);
}
}

View File

@@ -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,
};
};

View File

@@ -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<string>}
*/
public textFormatter = async (
public textFormatterArgs = async (
tenantId: number,
receiptId: number
): Promise<Record<string, string>> => {
@@ -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<void>}
*/
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
);
}
}

View File

@@ -35,7 +35,10 @@ export class SaleReceiptsPdf {
* @param {number} saleInvoiceId -
* @returns {Promise<Buffer>}
*/
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(

View File

@@ -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 = `
<p>Dear {CustomerName}</p>
<p>Dear {Customer Name}</p>
<p>Thank you for your business, You can view or print your receipt from attachements.</p>
<p>
Receipt <strong>#{ReceiptNumber}</strong><br />
Amount : <strong>{ReceiptAmount}</strong></br />
Receipt <strong>#{Receipt Number}</strong><br />
Amount : <strong>{Receipt Amount}</strong></br />
</p>
<p>
<i>Regards</i><br />
<i>{CompanyName}</i>
<i>{Company Name}</i>
</p>
`;

View File

@@ -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<ISaleReceiptBrandingTemplateAttributes> => {
export const transformReceiptToBrandingTemplateAttributes = (
saleReceipt: ISaleReceipt
): Partial<ISaleReceiptBrandingTemplateAttributes> => {
return {
total: saleReceipt.formattedAmount,
subtotal: saleReceipt.formattedSubtotal,
@@ -18,4 +21,13 @@ export const transformReceiptToBrandingTemplateAttributes = (saleReceipt: ISaleR
receiptDate: saleReceipt.formattedReceiptDate,
customerAddress: contactAddressTextFormat(saleReceipt.customer),
};
}
};
export const transformReceiptToMailDataArgs = (saleReceipt: any) => {
return {
'Customer Name': saleReceipt.customer.displayName,
'Receipt Number': saleReceipt.receiptNumber,
'Receipt Date': saleReceipt.formattedReceiptDate,
'Receipt Amount': saleReceipt.formattedAmount,
};
};

View File

@@ -213,7 +213,7 @@ export default {
onPreMailSend: 'onSaleEstimatePreMailSend',
onMailSend: 'onSaleEstimateMailSend',
onMailSent: 'onSaleEstimateMailSend',
onMailSent: 'onSaleEstimateMailSent',
},
/**