fix: mail services

This commit is contained in:
Ahmed Bouhuolia
2024-11-09 22:23:52 +02:00
parent 94223b6ebf
commit aa7e5d4ae9
15 changed files with 168 additions and 64 deletions

View File

@@ -37,7 +37,8 @@ export interface CommonMailOptions {
cc?: Array<string>;
bcc?: Array<string>;
formatArgs?: Record<string, any>;
toOptions: Array<AddressItem>;
fromOptions: Array<AddressItem>;
}
export interface CommonMailOptionsDTO extends Partial<CommonMailOptions> {
}
export interface CommonMailOptionsDTO extends Partial<CommonMailOptions> {}

View File

@@ -58,7 +58,7 @@ export class PdfTemplate extends TenantModel {
* @returns {string}
*/
get companyLogoUri() {
return this.attributes.companyLogoKey
return this.attributes?.companyLogoKey
? getUploadedObjectUri(this.attributes.companyLogoKey)
: '';
}

View File

@@ -23,22 +23,24 @@ export class ContactMailNotification {
public async getDefaultMailOptions(
tenantId: number,
customerId: number
): Promise<Pick<CommonMailOptions, 'to' | 'from'>> {
): Promise<
Pick<CommonMailOptions, 'to' | 'from' | 'toOptions' | 'fromOptions'>
> {
const { Customer } = this.tenancy.models(tenantId);
const customer = await Customer.query()
.findById(customerId)
.throwIfNotFound();
const toAddresses = customer.contactAddresses;
const fromAddresses = await this.mailTenancy.senders(tenantId);
const toOptions = customer.contactAddresses;
const fromOptions = await this.mailTenancy.senders(tenantId);
const toAddress = toAddresses.find((a) => a.primary);
const fromAddress = fromAddresses.find((a) => a.primary);
const toAddress = toOptions.find((a) => a.primary);
const fromAddress = fromOptions.find((a) => a.primary);
const to = toAddress?.mail ? castArray(toAddress?.mail) : [];
const from = fromAddress?.mail ? castArray(fromAddress?.mail) : [];
return { to, from };
return { to, from, toOptions, fromOptions };
}
/**

View File

@@ -129,8 +129,8 @@ export class SendSaleInvoiceMailCommon {
...commonArgs,
'Customer Name': invoice.customer.displayName,
'Invoice Number': invoice.invoiceNo,
'Invoice DueAmount': invoice.dueAmountFormatted,
'Invoice DueDate': invoice.dueDateFormatted,
'Invoice Due Amount': invoice.dueAmountFormatted,
'Invoice Due Date': invoice.dueDateFormatted,
'Invoice Date': invoice.invoiceDateFormatted,
'Invoice Amount': invoice.totalFormatted,
'Overdue Days': invoice.overdueDays,

View File

@@ -1,6 +1,10 @@
import { Inject, Service } from 'typedi';
import Mail from '@/lib/Mail';
import { ISaleInvoiceMailSend, SendInvoiceMailDTO } from '@/interfaces';
import {
ISaleInvoiceMailSend,
SaleInvoiceMailOptions,
SendInvoiceMailDTO,
} from '@/interfaces';
import { SaleInvoicePdf } from './SaleInvoicePdf';
import { SendSaleInvoiceMailCommon } from './SendInvoiceInvoiceMailCommon';
import { mergeAndValidateMailOptions } from '@/services/MailNotification/utils';
@@ -47,6 +51,34 @@ export class SendSaleInvoiceMail {
} as ISaleInvoiceMailSend);
}
/**
* Retrieves the formatted mail options.
* @param {number} tenantId
* @param {number} saleInvoiceId
* @param {SendInvoiceMailDTO} messageOptions
* @returns {Promise<SaleInvoiceMailOptions>}
*/
async getFormattedMailOptions(
tenantId: number,
saleInvoiceId: number,
messageOptions: SendInvoiceMailDTO
): Promise<SaleInvoiceMailOptions> {
const defaultMessageOptions = await this.invoiceMail.getInvoiceMailOptions(
tenantId,
saleInvoiceId
);
// Merges message options with default options and parses the options values.
const parsedMessageOptions = mergeAndValidateMailOptions(
defaultMessageOptions,
messageOptions
);
return this.invoiceMail.formatInvoiceMailOptions(
tenantId,
saleInvoiceId,
parsedMessageOptions
);
}
/**
* Triggers the mail invoice.
* @param {number} tenantId
@@ -59,21 +91,11 @@ export class SendSaleInvoiceMail {
saleInvoiceId: number,
messageOptions: SendInvoiceMailDTO
) {
const defaultMessageOptions = await this.invoiceMail.getInvoiceMailOptions(
const formattedMessageOptions = await this.getFormattedMailOptions(
tenantId,
saleInvoiceId
);
// Merges message options with default options and parses the options values.
const parsedMessageOptions = mergeAndValidateMailOptions(
defaultMessageOptions,
saleInvoiceId,
messageOptions
);
const formattedMessageOptions =
await this.invoiceMail.formatInvoiceMailOptions(
tenantId,
saleInvoiceId,
parsedMessageOptions
);
const mail = new Mail()
.setSubject(formattedMessageOptions.subject)
.setTo(formattedMessageOptions.to)

View File

@@ -112,17 +112,20 @@ export class SendPaymentReceiveMailNotification {
};
/**
* Triggers the mail invoice.
* Retrieves the formatted mail options of the given payment receive.
* @param {number} tenantId
* @param {number} saleInvoiceId
* @param {number} paymentReceiveId
* @param {SendInvoiceMailDTO} messageDTO
* @returns {Promise<void>}
* @returns {Promise<PaymentReceiveMailOpts>}
*/
public async sendMail(
public getFormattedMailOptions = async (
tenantId: number,
paymentReceiveId: number,
messageDTO: SendInvoiceMailDTO
): Promise<void> {
) => {
const formatterArgs = await this.textFormatter(tenantId, paymentReceiveId);
// Default message options.
const defaultMessageOpts = await this.getMailOptions(
tenantId,
paymentReceiveId
@@ -132,17 +135,43 @@ export class SendPaymentReceiveMailNotification {
defaultMessageOpts,
messageDTO
);
// Formats the message options.
return this.contactMailNotification.formatMailOptions(
tenantId,
parsedMessageOpts,
formatterArgs
);
};
/**
* Triggers the mail invoice.
* @param {number} tenantId
* @param {number} saleInvoiceId - Invoice id.
* @param {SendInvoiceMailDTO} messageDTO - Message options.
* @returns {Promise<void>}
*/
public async sendMail(
tenantId: number,
paymentReceiveId: number,
messageDTO: SendInvoiceMailDTO
): Promise<void> {
// Retrieves the formatted mail options.
const formattedMessageOptions = await this.getFormattedMailOptions(
tenantId,
paymentReceiveId,
messageDTO
);
const mail = new Mail()
.setSubject(parsedMessageOpts.subject)
.setTo(parsedMessageOpts.to)
.setCC(parsedMessageOpts.cc)
.setBCC(parsedMessageOpts.bcc)
.setContent(parsedMessageOpts.message);
.setSubject(formattedMessageOptions.subject)
.setTo(formattedMessageOptions.to)
.setCC(formattedMessageOptions.cc)
.setBCC(formattedMessageOptions.bcc)
.setContent(formattedMessageOptions.message);
const eventPayload = {
tenantId,
paymentReceiveId,
messageOptions: parsedMessageOpts,
messageOptions: formattedMessageOptions,
};
// Triggers `onPaymentReceiveMailSend` event.
await this.eventPublisher.emitAsync(

View File

@@ -1,15 +1,16 @@
export const DEFAULT_PAYMENT_MAIL_SUBJECT = 'Payment Received by {CompanyName}';
export const DEFAULT_PAYMENT_MAIL_SUBJECT =
'Payment Received for {Customer Name} from {Company Name}';
export const DEFAULT_PAYMENT_MAIL_CONTENT = `
<p>Dear {CustomerName}</p>
<p>Dear {Customer Name}</p>
<p>Thank you for your payment. It was a pleasure doing business with you. We look forward to work together again!</p>
<p>
Payment Date : <strong>{PaymentDate}</strong><br />
Amount : <strong>{PaymentAmount}</strong></br />
Payment Date : <strong>{Payment Date}</strong><br />
Amount : <strong>{Payment Amount}</strong></br />
</p>
<p>
<i>Regards</i><br />
<i>{CompanyName}</i>
<i>{Company Name}</i>
</p>
`;

View File

@@ -114,6 +114,58 @@ export class SaleReceiptMailNotification {
return transformReceiptToMailDataArgs(receipt);
};
/**
* Formats the mail options of the given sale receipt.
* @param {number} tenantId
* @param {number} receiptId
* @param {SaleReceiptMailOpts} mailOptions
* @returns {Promise<SaleReceiptMailOpts>}
*/
public async formatEstimateMailOptions(
tenantId: number,
receiptId: number,
mailOptions: SaleReceiptMailOpts
): Promise<SaleReceiptMailOpts> {
const formatterArgs = await this.textFormatterArgs(tenantId, receiptId);
const formattedOptions =
(await this.contactMailNotification.formatMailOptions(
tenantId,
mailOptions,
formatterArgs
)) as SaleReceiptMailOpts;
return formattedOptions;
}
/**
* Retrieves the formatted mail options of the given sale receipt.
* @param {number} tenantId
* @param {number} saleReceiptId
* @param {SaleReceiptMailOptsDTO} messageOpts
* @returns {Promise<SaleReceiptMailOpts>}
*/
public getFormatMailOptions = async (
tenantId: number,
saleReceiptId: number,
messageOpts: SaleReceiptMailOptsDTO
): Promise<SaleReceiptMailOpts> => {
const defaultMessageOptions = await this.getMailOptions(
tenantId,
saleReceiptId
);
// Merges message opts with default options.
const parsedMessageOpts = mergeAndValidateMailOptions(
defaultMessageOptions,
messageOpts
) as SaleReceiptMailOpts;
// Formats the message options.
return this.formatEstimateMailOptions(
tenantId,
saleReceiptId,
parsedMessageOpts
);
};
/**
* Triggers the mail notification of the given sale receipt.
* @param {number} tenantId - Tenant id.
@@ -126,25 +178,21 @@ export class SaleReceiptMailNotification {
saleReceiptId: number,
messageOpts: SaleReceiptMailOptsDTO
) {
const defaultMessageOptions = await this.getMailOptions(
// Formats the message options.
const formattedMessageOptions = await this.getFormatMailOptions(
tenantId,
saleReceiptId
);
// Merges message opts with default options.
const parsedMessageOpts = mergeAndValidateMailOptions(
defaultMessageOptions,
saleReceiptId,
messageOpts
) as SaleReceiptMailOpts;
);
const mail = new Mail()
.setSubject(parsedMessageOpts.subject)
.setTo(parsedMessageOpts.to)
.setCC(parsedMessageOpts.cc)
.setBCC(parsedMessageOpts.bcc)
.setContent(parsedMessageOpts.message);
.setSubject(formattedMessageOptions.subject)
.setTo(formattedMessageOptions.to)
.setCC(formattedMessageOptions.cc)
.setBCC(formattedMessageOptions.bcc)
.setContent(formattedMessageOptions.message);
// Attaches the receipt pdf document.
if (parsedMessageOpts.attachReceipt) {
if (formattedMessageOptions.attachReceipt) {
// Retrieves document buffer of the receipt pdf document.
const [receiptPdfBuffer, filename] =
await this.receiptPdfService.saleReceiptPdf(tenantId, saleReceiptId);

View File

@@ -32,7 +32,7 @@ function EstimateMailDialogFormRoot({
closeDialog,
}) {
const { mutateAsync: sendEstimateMail } = useSendSaleEstimateMail();
const { mailOptions, saleEstimateId, redirectToEstimatesList } =
const { mailOptions, saleEstimateId, } =
useEstimateMailDialogBoot();
const initialValues = transformMailFormToInitialValues(

View File

@@ -25,8 +25,8 @@ export function EstimateMailDialogFormContent({
<Form>
<div className={Classes.DIALOG_BODY}>
<MailNotificationForm
fromAddresses={mailOptions.from_addresses}
toAddresses={mailOptions.to_addresses}
fromAddresses={mailOptions.from_options}
toAddresses={mailOptions.to_options}
/>
<AttachFormGroup name={'attachEstimate'} inline>
<FSwitch name={'attachEstimate'} label={'Attach Estimate'} />

View File

@@ -22,6 +22,7 @@ interface PaymentMailDialogBootProps {
*/
function PaymentMailDialogBoot({
paymentReceiveId,
redirectToPaymentsList,
...props
}: PaymentMailDialogBootProps) {
const { data: mailOptions, isLoading: isMailOptionsLoading } =

View File

@@ -25,8 +25,8 @@ export function PaymentMailDialogFormContent({
<Form>
<div className={Classes.DIALOG_BODY}>
<MailNotificationForm
fromAddresses={mailOptions.from_addresses}
toAddresses={mailOptions.to_addresses}
fromAddresses={mailOptions.from_options}
toAddresses={mailOptions.to_options}
/>
<AttachFormGroup name={'attachPayment'} inline>
<FSwitch name={'attachPayment'} label={'Attach Payment'} />

View File

@@ -25,8 +25,8 @@ export function ReceiptMailDialogFormContent({
<Form>
<div className={Classes.DIALOG_BODY}>
<MailNotificationForm
fromAddresses={mailOptions.from_addresses}
toAddresses={mailOptions.to_addresses}
fromAddresses={mailOptions.from_options}
toAddresses={mailOptions.to_options}
/>
<AttachFormGroup name={'attachReceipt:'} inline>
<FSwitch name={'attachReceipt:'} label={'Attach Receipt'} />

View File

@@ -66,7 +66,7 @@ export function MailNotificationForm({
</FFormGroup>
</HeaderBox>
<MailMessageEditor name={'body'} />
<MailMessageEditor name={'message'} />
</Box>
);
}

View File

@@ -5,7 +5,7 @@ export const initialMailNotificationValues = {
from: [],
to: [],
subject: '',
body: '',
message: '',
};
export interface MailNotificationFormValues {
@@ -26,7 +26,7 @@ export const transformMailFormToRequest = (
};
/**
* Transformes the mail options response values to form initial values.
* Transforms the mail options response values to form initial values.
* @param {any} mailOptions
* @param {MailNotificationFormValues} initialValues
* @returns {MailNotificationFormValues}