mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 22:30:31 +00:00
refactor: notification mail services
This commit is contained in:
@@ -13,15 +13,19 @@ export const SALE_ESTIMATE_EDITED = 'Sale estimate edited';
|
|||||||
export const SALE_ESTIMATE_DELETED = 'Sale estimate deleted';
|
export const SALE_ESTIMATE_DELETED = 'Sale estimate deleted';
|
||||||
export const SALE_ESTIMATE_PDF_VIEWED = 'Sale estimate PDF viewed';
|
export const SALE_ESTIMATE_PDF_VIEWED = 'Sale estimate PDF viewed';
|
||||||
export const SALE_ESTIMATE_VIEWED = 'Sale estimate 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_CREATED = 'Payment received created';
|
||||||
export const PAYMENT_RECEIVED_EDITED = 'payment received edited';
|
export const PAYMENT_RECEIVED_EDITED = 'payment received edited';
|
||||||
export const PAYMENT_RECEIVED_DELETED = 'Payment received deleted';
|
export const PAYMENT_RECEIVED_DELETED = 'Payment received deleted';
|
||||||
export const PAYMENT_RECEIVED_PDF_VIEWED = 'Payment received PDF viewed';
|
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_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_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_CREATED = 'Bill created';
|
||||||
export const BILL_EDITED = 'Bill edited';
|
export const BILL_EDITED = 'Bill edited';
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export interface CommonMailOptions {
|
|||||||
to: Array<string>;
|
to: Array<string>;
|
||||||
cc?: Array<string>;
|
cc?: Array<string>;
|
||||||
bcc?: Array<string>;
|
bcc?: Array<string>;
|
||||||
data?: Record<string, any>;
|
formatArgs?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CommonMailOptionsDTO extends Partial<CommonMailOptions> {
|
export interface CommonMailOptionsDTO extends Partial<CommonMailOptions> {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
PAYMENT_RECEIVED_EDITED,
|
PAYMENT_RECEIVED_EDITED,
|
||||||
PAYMENT_RECEIVED_DELETED,
|
PAYMENT_RECEIVED_DELETED,
|
||||||
PAYMENT_RECEIVED_PDF_VIEWED,
|
PAYMENT_RECEIVED_PDF_VIEWED,
|
||||||
|
PAYMENT_RECEIVED_MAIL_SENT,
|
||||||
} from '@/constants/event-tracker';
|
} from '@/constants/event-tracker';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
@@ -39,6 +40,10 @@ export class PaymentReceivedEventsTracker extends EventSubscriber {
|
|||||||
events.paymentReceive.onPdfViewed,
|
events.paymentReceive.onPdfViewed,
|
||||||
this.handleTrackPdfViewedPaymentReceivedEvent
|
this.handleTrackPdfViewedPaymentReceivedEvent
|
||||||
);
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.paymentReceive.onMailSent,
|
||||||
|
this.handleTrackMailSentPaymentReceivedEvent
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleTrackPaymentReceivedCreatedEvent = ({
|
private handleTrackPaymentReceivedCreatedEvent = ({
|
||||||
@@ -80,4 +85,14 @@ export class PaymentReceivedEventsTracker extends EventSubscriber {
|
|||||||
properties: {},
|
properties: {},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private handleTrackMailSentPaymentReceivedEvent = ({
|
||||||
|
tenantId,
|
||||||
|
}: IPaymentReceivedDeletedPayload) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: PAYMENT_RECEIVED_MAIL_SENT,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
SALE_ESTIMATE_DELETED,
|
SALE_ESTIMATE_DELETED,
|
||||||
SALE_ESTIMATE_PDF_VIEWED,
|
SALE_ESTIMATE_PDF_VIEWED,
|
||||||
SALE_ESTIMATE_VIEWED,
|
SALE_ESTIMATE_VIEWED,
|
||||||
|
SALE_ESTIMATE_MAIL_SENT,
|
||||||
} from '@/constants/event-tracker';
|
} from '@/constants/event-tracker';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
@@ -44,6 +45,10 @@ export class SaleEstimateEventsTracker extends EventSubscriber {
|
|||||||
events.saleEstimate.onViewed,
|
events.saleEstimate.onViewed,
|
||||||
this.handleTrackViewedEstimateEvent
|
this.handleTrackViewedEstimateEvent
|
||||||
);
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.saleEstimate.onMailSent,
|
||||||
|
this.handleTrackMailSentEstimateEvent
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleTrackEstimateCreatedEvent = ({
|
private handleTrackEstimateCreatedEvent = ({
|
||||||
@@ -93,4 +98,12 @@ export class SaleEstimateEventsTracker extends EventSubscriber {
|
|||||||
properties: {},
|
properties: {},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private handleTrackMailSentEstimateEvent = ({ tenantId }) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: SALE_ESTIMATE_MAIL_SENT,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export class ContactMailNotification {
|
|||||||
* @param {number} tenantId - Tenant id.
|
* @param {number} tenantId - Tenant id.
|
||||||
* @returns {Promise<CommonMailOptions>}
|
* @returns {Promise<CommonMailOptions>}
|
||||||
*/
|
*/
|
||||||
public async parseMailOptions(
|
public async formatMailOptions(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
mailOptions: CommonMailOptions,
|
mailOptions: CommonMailOptions,
|
||||||
formatterArgs?: Record<string, any>
|
formatterArgs?: Record<string, any>
|
||||||
|
|||||||
@@ -44,3 +44,13 @@ export function validateRequiredMailOptions(
|
|||||||
throw new ServiceError(ERRORS.MAIL_BODY_NOT_FOUND);
|
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;
|
||||||
|
};
|
||||||
|
|||||||
@@ -13,9 +13,10 @@ import {
|
|||||||
SaleEstimateMailOptionsDTO,
|
SaleEstimateMailOptionsDTO,
|
||||||
} from '@/interfaces';
|
} from '@/interfaces';
|
||||||
import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification';
|
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 { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
import events from '@/subscribers/events';
|
import events from '@/subscribers/events';
|
||||||
|
import { transformEstimateToMailDataArgs } from './utils';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class SendSaleEstimateMail {
|
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} tenantId - Tenant id.
|
||||||
* @param {number} estimateId - Estimate id.
|
* @param {number} estimateId - Estimate id.
|
||||||
* @returns {Promise<Record<string, any>>}
|
* @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(
|
const estimate = await this.getSaleEstimateService.getEstimate(
|
||||||
tenantId,
|
tenantId,
|
||||||
estimateId
|
estimateId
|
||||||
);
|
);
|
||||||
return {
|
return transformEstimateToMailDataArgs(estimate);
|
||||||
CustomerName: estimate.customer.displayName,
|
|
||||||
EstimateNumber: estimate.estimateNumber,
|
|
||||||
EstimateDate: estimate.formattedEstimateDate,
|
|
||||||
EstimateAmount: estimate.formattedAmount,
|
|
||||||
EstimateExpirationDate: estimate.formattedExpirationDate,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,7 +87,9 @@ export class SendSaleEstimateMail {
|
|||||||
*/
|
*/
|
||||||
public getMailOptions = async (
|
public getMailOptions = async (
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
saleEstimateId: number
|
saleEstimateId: number,
|
||||||
|
defaultSubject: string = DEFAULT_ESTIMATE_REMINDER_MAIL_SUBJECT,
|
||||||
|
defaultMessage: string = DEFAULT_ESTIMATE_REMINDER_MAIL_CONTENT
|
||||||
): Promise<SaleEstimateMailOptions> => {
|
): Promise<SaleEstimateMailOptions> => {
|
||||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
@@ -100,22 +97,45 @@ export class SendSaleEstimateMail {
|
|||||||
.findById(saleEstimateId)
|
.findById(saleEstimateId)
|
||||||
.throwIfNotFound();
|
.throwIfNotFound();
|
||||||
|
|
||||||
const formatterData = await this.formatterData(tenantId, saleEstimateId);
|
const formatArgs = await this.formatterArgs(tenantId, saleEstimateId);
|
||||||
|
|
||||||
const mailOptions = await this.contactMailNotification.getMailOptions(
|
const mailOptions =
|
||||||
tenantId,
|
await this.contactMailNotification.getDefaultMailOptions(
|
||||||
saleEstimate.customerId,
|
tenantId,
|
||||||
DEFAULT_ESTIMATE_REMINDER_MAIL_SUBJECT,
|
saleEstimate.customerId
|
||||||
DEFAULT_ESTIMATE_REMINDER_MAIL_CONTENT,
|
);
|
||||||
formatterData
|
|
||||||
);
|
|
||||||
return {
|
return {
|
||||||
...mailOptions,
|
...mailOptions,
|
||||||
data: formatterData,
|
message: defaultMessage,
|
||||||
|
subject: defaultSubject,
|
||||||
attachEstimate: true,
|
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.
|
* Sends the mail notification of the given sale estimate.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
@@ -133,27 +153,52 @@ export class SendSaleEstimateMail {
|
|||||||
saleEstimateId
|
saleEstimateId
|
||||||
);
|
);
|
||||||
// Overrides and validates the given mail options.
|
// Overrides and validates the given mail options.
|
||||||
const messageOpts = parseAndValidateMailOptions(
|
const parsedMessageOptions = mergeAndValidateMailOptions(
|
||||||
localMessageOpts,
|
localMessageOpts,
|
||||||
messageOptions
|
messageOptions
|
||||||
|
) as SaleEstimateMailOptions;
|
||||||
|
|
||||||
|
const formattedOptions = await this.formatMailOptions(
|
||||||
|
tenantId,
|
||||||
|
saleEstimateId,
|
||||||
|
parsedMessageOptions
|
||||||
);
|
);
|
||||||
const mail = new Mail()
|
const mail = new Mail()
|
||||||
.setSubject(messageOpts.subject)
|
.setSubject(formattedOptions.subject)
|
||||||
.setTo(messageOpts.to)
|
.setTo(formattedOptions.to)
|
||||||
.setContent(messageOpts.body);
|
.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([
|
mail.setAttachments([
|
||||||
{
|
{
|
||||||
filename: messageOpts.data?.EstimateNumber || 'estimate.pdf',
|
filename: `${estimateFilename}.pdf`,
|
||||||
content: estimatePdfBuffer,
|
content: estimatePdfBuffer,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const eventPayload = {
|
||||||
|
tenantId,
|
||||||
|
saleEstimateId,
|
||||||
|
messageOptions,
|
||||||
|
formattedOptions,
|
||||||
|
};
|
||||||
|
// Triggers `onSaleEstimateMailSend` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.saleEstimate.onMailSend,
|
||||||
|
eventPayload as ISaleEstimateMailPresendEvent
|
||||||
|
);
|
||||||
await mail.send();
|
await mail.send();
|
||||||
|
|
||||||
|
// Triggers `onSaleEstimateMailSent` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.saleEstimate.onMailSent,
|
||||||
|
eventPayload as ISaleEstimateMailPresendEvent
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
export const DEFAULT_ESTIMATE_REMINDER_MAIL_SUBJECT =
|
export const DEFAULT_ESTIMATE_REMINDER_MAIL_SUBJECT =
|
||||||
'Estimate {EstimateNumber} is awaiting your approval';
|
'Estimate {Estimate Number} is awaiting your approval';
|
||||||
export const DEFAULT_ESTIMATE_REMINDER_MAIL_CONTENT = `<p>Dear {CustomerName}</p>
|
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>Thank you for your business, You can view or print your estimate from attachements.</p>
|
||||||
<p>
|
<p>
|
||||||
Estimate <strong>#{EstimateNumber}</strong><br />
|
Estimate <strong>#{Estimate Number}</strong><br />
|
||||||
Expiration Date : <strong>{EstimateExpirationDate}</strong><br />
|
Expiration Date : <strong>{Estimate Expiration Date}</strong><br />
|
||||||
Amount : <strong>{EstimateAmount}</strong></br />
|
Amount : <strong>{Estimate Amount}</strong></br />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<i>Regards</i><br />
|
<i>Regards</i><br />
|
||||||
<i>{CompanyName}</i>
|
<i>{Company Name}</i>
|
||||||
</p>
|
</p>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -22,3 +22,13 @@ export const transformEstimateToPdfTemplate = (
|
|||||||
customerAddress: contactAddressTextFormat(estimate.customer),
|
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,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export class GetInvoicePaymentMail {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the mail template attributes of the given invoice.
|
* 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} tenantId - Tenant id.
|
||||||
* @param {number} invoiceId - Invoice id.
|
* @param {number} invoiceId - Invoice id.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -75,20 +75,22 @@ export class SendSaleInvoiceMailCommon {
|
|||||||
tenantId,
|
tenantId,
|
||||||
invoiceId
|
invoiceId
|
||||||
);
|
);
|
||||||
const parsedOptions = await this.contactMailNotification.parseMailOptions(
|
const formattedOptions =
|
||||||
tenantId,
|
await this.contactMailNotification.formatMailOptions(
|
||||||
mailOptions,
|
tenantId,
|
||||||
formatterArgs
|
mailOptions,
|
||||||
);
|
formatterArgs
|
||||||
|
);
|
||||||
const message = await this.getInvoicePaymentMail.getMailTemplate(
|
const message = await this.getInvoicePaymentMail.getMailTemplate(
|
||||||
tenantId,
|
tenantId,
|
||||||
invoiceId,
|
invoiceId,
|
||||||
{
|
{
|
||||||
// # Invoice message
|
// # 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 {
|
return {
|
||||||
...commonArgs,
|
...commonArgs,
|
||||||
['Customer Name']: invoice.customer.displayName,
|
'Customer Name': invoice.customer.displayName,
|
||||||
['Invoice Number']: invoice.invoiceNo,
|
'Invoice Number': invoice.invoiceNo,
|
||||||
['Invoice DueAmount']: invoice.dueAmountFormatted,
|
'Invoice DueAmount': invoice.dueAmountFormatted,
|
||||||
['Invoice DueDate']: invoice.dueDateFormatted,
|
'Invoice DueDate': invoice.dueDateFormatted,
|
||||||
['Invoice Date']: invoice.invoiceDateFormatted,
|
'Invoice Date': invoice.invoiceDateFormatted,
|
||||||
['Invoice Amount']: invoice.totalFormatted,
|
'Invoice Amount': invoice.totalFormatted,
|
||||||
['Overdue Days']: invoice.overdueDays,
|
'Overdue Days': invoice.overdueDays,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,7 @@ import Mail from '@/lib/Mail';
|
|||||||
import { ISaleInvoiceMailSend, SendInvoiceMailDTO } from '@/interfaces';
|
import { ISaleInvoiceMailSend, SendInvoiceMailDTO } from '@/interfaces';
|
||||||
import { SaleInvoicePdf } from './SaleInvoicePdf';
|
import { SaleInvoicePdf } from './SaleInvoicePdf';
|
||||||
import { SendSaleInvoiceMailCommon } from './SendInvoiceInvoiceMailCommon';
|
import { SendSaleInvoiceMailCommon } from './SendInvoiceInvoiceMailCommon';
|
||||||
import {
|
import { mergeAndValidateMailOptions } from '@/services/MailNotification/utils';
|
||||||
parseMailOptions,
|
|
||||||
validateRequiredMailOptions,
|
|
||||||
} from '@/services/MailNotification/utils';
|
|
||||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
import events from '@/subscribers/events';
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
@@ -18,12 +15,12 @@ export class SendSaleInvoiceMail {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private invoiceMail: SendSaleInvoiceMailCommon;
|
private invoiceMail: SendSaleInvoiceMailCommon;
|
||||||
|
|
||||||
@Inject('agenda')
|
|
||||||
private agenda: any;
|
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private eventPublisher: EventPublisher;
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
|
@Inject('agenda')
|
||||||
|
private agenda: any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends the invoice mail of the given sale invoice.
|
* Sends the invoice mail of the given sale invoice.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
@@ -67,13 +64,10 @@ export class SendSaleInvoiceMail {
|
|||||||
saleInvoiceId
|
saleInvoiceId
|
||||||
);
|
);
|
||||||
// Merges message options with default options and parses the options values.
|
// Merges message options with default options and parses the options values.
|
||||||
const parsedMessageOptions = parseMailOptions(
|
const parsedMessageOptions = mergeAndValidateMailOptions(
|
||||||
defaultMessageOptions,
|
defaultMessageOptions,
|
||||||
messageOptions
|
messageOptions
|
||||||
);
|
);
|
||||||
// Validates the required mail options.
|
|
||||||
validateRequiredMailOptions(parsedMessageOptions);
|
|
||||||
|
|
||||||
const formattedMessageOptions =
|
const formattedMessageOptions =
|
||||||
await this.invoiceMail.formatInvoiceMailOptions(
|
await this.invoiceMail.formatInvoiceMailOptions(
|
||||||
tenantId,
|
tenantId,
|
||||||
|
|||||||
@@ -13,9 +13,10 @@ import {
|
|||||||
} from './constants';
|
} from './constants';
|
||||||
import { GetPaymentReceived } from './GetPaymentReceived';
|
import { GetPaymentReceived } from './GetPaymentReceived';
|
||||||
import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification';
|
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 { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
import events from '@/subscribers/events';
|
import events from '@/subscribers/events';
|
||||||
|
import { transformPaymentReceivedToMailDataArgs } from './utils';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class SendPaymentReceiveMailNotification {
|
export class SendPaymentReceiveMailNotification {
|
||||||
@@ -77,15 +78,19 @@ export class SendPaymentReceiveMailNotification {
|
|||||||
.findById(paymentId)
|
.findById(paymentId)
|
||||||
.throwIfNotFound();
|
.throwIfNotFound();
|
||||||
|
|
||||||
const formatterData = await this.textFormatter(tenantId, paymentId);
|
const formatArgs = await this.textFormatter(tenantId, paymentId);
|
||||||
|
|
||||||
return this.contactMailNotification.getMailOptions(
|
const mailOptions =
|
||||||
tenantId,
|
await this.contactMailNotification.getDefaultMailOptions(
|
||||||
paymentReceive.customerId,
|
tenantId,
|
||||||
DEFAULT_PAYMENT_MAIL_SUBJECT,
|
paymentReceive.customerId
|
||||||
DEFAULT_PAYMENT_MAIL_CONTENT,
|
);
|
||||||
formatterData
|
return {
|
||||||
);
|
...mailOptions,
|
||||||
|
subject: DEFAULT_PAYMENT_MAIL_SUBJECT,
|
||||||
|
message: DEFAULT_PAYMENT_MAIL_CONTENT,
|
||||||
|
...formatArgs,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -103,12 +108,7 @@ export class SendPaymentReceiveMailNotification {
|
|||||||
tenantId,
|
tenantId,
|
||||||
invoiceId
|
invoiceId
|
||||||
);
|
);
|
||||||
return {
|
return transformPaymentReceivedToMailDataArgs(payment);
|
||||||
CustomerName: payment.customer.displayName,
|
|
||||||
PaymentNumber: payment.payment_receive_no,
|
|
||||||
PaymentDate: payment.formattedPaymentDate,
|
|
||||||
PaymentAmount: payment.formattedAmount,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,14 +128,31 @@ export class SendPaymentReceiveMailNotification {
|
|||||||
paymentReceiveId
|
paymentReceiveId
|
||||||
);
|
);
|
||||||
// Parsed message opts with default options.
|
// Parsed message opts with default options.
|
||||||
const parsedMessageOpts = parseAndValidateMailOptions(
|
const parsedMessageOpts = mergeAndValidateMailOptions(
|
||||||
defaultMessageOpts,
|
defaultMessageOpts,
|
||||||
messageDTO
|
messageDTO
|
||||||
);
|
);
|
||||||
await new Mail()
|
const mail = new Mail()
|
||||||
.setSubject(parsedMessageOpts.subject)
|
.setSubject(parsedMessageOpts.subject)
|
||||||
.setTo(parsedMessageOpts.to)
|
.setTo(parsedMessageOpts.to)
|
||||||
.setContent(parsedMessageOpts.body)
|
.setContent(parsedMessageOpts.message);
|
||||||
.send();
|
|
||||||
|
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
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,3 +21,12 @@ export const transformPaymentReceivedToPdfTemplate = (
|
|||||||
customerAddress: contactAddressTextFormat(payment.customer),
|
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,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -13,9 +13,10 @@ import {
|
|||||||
SaleReceiptMailOptsDTO,
|
SaleReceiptMailOptsDTO,
|
||||||
} from '@/interfaces';
|
} from '@/interfaces';
|
||||||
import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification';
|
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 { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
import events from '@/subscribers/events';
|
import events from '@/subscribers/events';
|
||||||
|
import { transformReceiptToMailDataArgs } from './utils';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class SaleReceiptMailNotification {
|
export class SaleReceiptMailNotification {
|
||||||
@@ -79,18 +80,19 @@ export class SaleReceiptMailNotification {
|
|||||||
.findById(saleReceiptId)
|
.findById(saleReceiptId)
|
||||||
.throwIfNotFound();
|
.throwIfNotFound();
|
||||||
|
|
||||||
const formattedData = await this.textFormatter(tenantId, saleReceiptId);
|
const formatArgs = await this.textFormatterArgs(tenantId, saleReceiptId);
|
||||||
|
|
||||||
const mailOpts = await this.contactMailNotification.getMailOptions(
|
const mailOptions =
|
||||||
tenantId,
|
await this.contactMailNotification.getDefaultMailOptions(
|
||||||
saleReceipt.customerId,
|
tenantId,
|
||||||
DEFAULT_RECEIPT_MAIL_SUBJECT,
|
saleReceipt.customerId
|
||||||
DEFAULT_RECEIPT_MAIL_CONTENT,
|
);
|
||||||
formattedData
|
|
||||||
);
|
|
||||||
return {
|
return {
|
||||||
...mailOpts,
|
...mailOptions,
|
||||||
|
message: DEFAULT_RECEIPT_MAIL_CONTENT,
|
||||||
|
subject: DEFAULT_RECEIPT_MAIL_SUBJECT,
|
||||||
attachReceipt: true,
|
attachReceipt: true,
|
||||||
|
formatArgs,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +103,7 @@ export class SaleReceiptMailNotification {
|
|||||||
* @param {string} text - The given text.
|
* @param {string} text - The given text.
|
||||||
* @returns {Promise<string>}
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
public textFormatter = async (
|
public textFormatterArgs = async (
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
receiptId: number
|
receiptId: number
|
||||||
): Promise<Record<string, string>> => {
|
): Promise<Record<string, string>> => {
|
||||||
@@ -109,19 +111,14 @@ export class SaleReceiptMailNotification {
|
|||||||
tenantId,
|
tenantId,
|
||||||
receiptId
|
receiptId
|
||||||
);
|
);
|
||||||
return {
|
return transformReceiptToMailDataArgs(receipt);
|
||||||
CustomerName: receipt.customer.displayName,
|
|
||||||
ReceiptNumber: receipt.receiptNumber,
|
|
||||||
ReceiptDate: receipt.formattedReceiptDate,
|
|
||||||
ReceiptAmount: receipt.formattedAmount,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggers the mail notification of the given sale receipt.
|
* Triggers the mail notification of the given sale receipt.
|
||||||
* @param {number} tenantId - Tenant id.
|
* @param {number} tenantId - Tenant id.
|
||||||
* @param {number} saleReceiptId - Sale receipt id.
|
* @param {number} saleReceiptId - Sale receipt id.
|
||||||
* @param {SaleReceiptMailOpts} messageDTO - Overrided message options.
|
* @param {SaleReceiptMailOpts} messageDTO - message options.
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
public async sendMail(
|
public async sendMail(
|
||||||
@@ -129,30 +126,45 @@ export class SaleReceiptMailNotification {
|
|||||||
saleReceiptId: number,
|
saleReceiptId: number,
|
||||||
messageOpts: SaleReceiptMailOptsDTO
|
messageOpts: SaleReceiptMailOptsDTO
|
||||||
) {
|
) {
|
||||||
const defaultMessageOpts = await this.getMailOptions(
|
const defaultMessageOptions = await this.getMailOptions(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleReceiptId
|
saleReceiptId
|
||||||
);
|
);
|
||||||
// Merges message opts with default options.
|
// Merges message opts with default options.
|
||||||
const parsedMessageOpts = parseAndValidateMailOptions(
|
const parsedMessageOpts = mergeAndValidateMailOptions(
|
||||||
defaultMessageOpts,
|
defaultMessageOptions,
|
||||||
messageOpts
|
messageOpts
|
||||||
);
|
) as SaleReceiptMailOpts;
|
||||||
|
|
||||||
const mail = new Mail()
|
const mail = new Mail()
|
||||||
.setSubject(parsedMessageOpts.subject)
|
.setSubject(parsedMessageOpts.subject)
|
||||||
.setTo(parsedMessageOpts.to)
|
.setTo(parsedMessageOpts.to)
|
||||||
.setContent(parsedMessageOpts.body);
|
.setContent(parsedMessageOpts.message);
|
||||||
|
|
||||||
|
// Attaches the receipt pdf document.
|
||||||
if (parsedMessageOpts.attachReceipt) {
|
if (parsedMessageOpts.attachReceipt) {
|
||||||
// Retrieves document buffer of the receipt pdf document.
|
// Retrieves document buffer of the receipt pdf document.
|
||||||
const receiptPdfBuffer = await this.receiptPdfService.saleReceiptPdf(
|
const [receiptPdfBuffer, filename] =
|
||||||
tenantId,
|
await this.receiptPdfService.saleReceiptPdf(tenantId, saleReceiptId);
|
||||||
saleReceiptId
|
|
||||||
);
|
|
||||||
mail.setAttachments([
|
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 mail.send();
|
||||||
|
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.saleReceipt.onMailSent,
|
||||||
|
eventPayload
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,10 @@ export class SaleReceiptsPdf {
|
|||||||
* @param {number} saleInvoiceId -
|
* @param {number} saleInvoiceId -
|
||||||
* @returns {Promise<Buffer>}
|
* @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 filename = await this.getSaleReceiptFilename(tenantId, saleReceiptId);
|
||||||
|
|
||||||
const brandingAttributes = await this.getReceiptBrandingAttributes(
|
const brandingAttributes = await this.getReceiptBrandingAttributes(
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
export const DEFAULT_RECEIPT_MAIL_SUBJECT =
|
export const DEFAULT_RECEIPT_MAIL_SUBJECT =
|
||||||
'Receipt {ReceiptNumber} from {CompanyName}';
|
'Receipt {Receipt Number} from {Company Name}';
|
||||||
export const DEFAULT_RECEIPT_MAIL_CONTENT = `
|
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>Thank you for your business, You can view or print your receipt from attachements.</p>
|
||||||
<p>
|
<p>
|
||||||
Receipt <strong>#{ReceiptNumber}</strong><br />
|
Receipt <strong>#{Receipt Number}</strong><br />
|
||||||
Amount : <strong>{ReceiptAmount}</strong></br />
|
Amount : <strong>{Receipt Amount}</strong></br />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<i>Regards</i><br />
|
<i>Regards</i><br />
|
||||||
<i>{CompanyName}</i>
|
<i>{Company Name}</i>
|
||||||
</p>
|
</p>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import { ISaleReceipt, ISaleReceiptBrandingTemplateAttributes } from "@/interfaces";
|
import {
|
||||||
import { contactAddressTextFormat } from "@/utils/address-text-format";
|
ISaleReceipt,
|
||||||
|
ISaleReceiptBrandingTemplateAttributes,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import { contactAddressTextFormat } from '@/utils/address-text-format';
|
||||||
|
|
||||||
|
export const transformReceiptToBrandingTemplateAttributes = (
|
||||||
|
saleReceipt: ISaleReceipt
|
||||||
export const transformReceiptToBrandingTemplateAttributes = (saleReceipt: ISaleReceipt): Partial<ISaleReceiptBrandingTemplateAttributes> => {
|
): Partial<ISaleReceiptBrandingTemplateAttributes> => {
|
||||||
return {
|
return {
|
||||||
total: saleReceipt.formattedAmount,
|
total: saleReceipt.formattedAmount,
|
||||||
subtotal: saleReceipt.formattedSubtotal,
|
subtotal: saleReceipt.formattedSubtotal,
|
||||||
@@ -18,4 +21,13 @@ export const transformReceiptToBrandingTemplateAttributes = (saleReceipt: ISaleR
|
|||||||
receiptDate: saleReceipt.formattedReceiptDate,
|
receiptDate: saleReceipt.formattedReceiptDate,
|
||||||
customerAddress: contactAddressTextFormat(saleReceipt.customer),
|
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,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ export default {
|
|||||||
|
|
||||||
onPreMailSend: 'onSaleEstimatePreMailSend',
|
onPreMailSend: 'onSaleEstimatePreMailSend',
|
||||||
onMailSend: 'onSaleEstimateMailSend',
|
onMailSend: 'onSaleEstimateMailSend',
|
||||||
onMailSent: 'onSaleEstimateMailSend',
|
onMailSent: 'onSaleEstimateMailSent',
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user