mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 13:20:31 +00:00
feat: mail notifications of sales transactions
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import * as R from 'ramda';
|
||||
import { SaleInvoiceMailOptions } from '@/interfaces';
|
||||
import { CommonMailOptions } from '@/interfaces';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { MailTenancy } from '@/services/MailTenancy/MailTenancy';
|
||||
import { formatSmsMessage } from '@/utils';
|
||||
import { Tenant } from '@/system/models';
|
||||
|
||||
@Service()
|
||||
export class ContactMailNotification {
|
||||
@@ -15,8 +15,10 @@ export class ContactMailNotification {
|
||||
|
||||
/**
|
||||
* Parses the default message options.
|
||||
* @param {number} tenantId
|
||||
* @param {number} invoiceId
|
||||
* @param {number} tenantId -
|
||||
* @param {number} invoiceId -
|
||||
* @param {string} subject -
|
||||
* @param {string} body -
|
||||
* @returns {Promise<SaleInvoiceMailOptions>}
|
||||
*/
|
||||
public async getDefaultMailOptions(
|
||||
@@ -24,9 +26,11 @@ export class ContactMailNotification {
|
||||
contactId: number,
|
||||
subject: string = '',
|
||||
body: string = ''
|
||||
): Promise<any> {
|
||||
const { Contact, Customer } = this.tenancy.models(tenantId);
|
||||
const contact = await Customer.query().findById(contactId).throwIfNotFound();
|
||||
): Promise<CommonMailOptions> {
|
||||
const { Customer } = this.tenancy.models(tenantId);
|
||||
const contact = await Customer.query()
|
||||
.findById(contactId)
|
||||
.throwIfNotFound();
|
||||
|
||||
const toAddresses = contact.contactAddresses;
|
||||
const fromAddresses = await this.mailTenancy.senders(tenantId);
|
||||
@@ -48,10 +52,12 @@ export class ContactMailNotification {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the mail options.
|
||||
* @param {number}
|
||||
* @param {number} invoiceId
|
||||
* @returns {}
|
||||
* Retrieves the mail options of the given contact.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} invoiceId - Invoice id.
|
||||
* @param {string} defaultSubject - Default subject text.
|
||||
* @param {string} defaultBody - Default body text.
|
||||
* @returns {Promise<CommonMailOptions>}
|
||||
*/
|
||||
public async getMailOptions(
|
||||
tenantId: number,
|
||||
@@ -59,15 +65,20 @@ export class ContactMailNotification {
|
||||
defaultSubject?: string,
|
||||
defaultBody?: string,
|
||||
formatterData?: Record<string, any>
|
||||
): Promise<SaleInvoiceMailOptions> {
|
||||
): Promise<CommonMailOptions> {
|
||||
const mailOpts = await this.getDefaultMailOptions(
|
||||
tenantId,
|
||||
contactId,
|
||||
defaultSubject,
|
||||
defaultBody
|
||||
);
|
||||
const subject = formatSmsMessage(mailOpts.subject, formatterData);
|
||||
const body = formatSmsMessage(mailOpts.body, formatterData);
|
||||
const commonFormatArgs = await this.getCommonFormatArgs(tenantId);
|
||||
const formatArgs = {
|
||||
...commonFormatArgs,
|
||||
...formatterData,
|
||||
};
|
||||
const subject = formatSmsMessage(mailOpts.subject, formatArgs);
|
||||
const body = formatSmsMessage(mailOpts.body, formatArgs);
|
||||
|
||||
return {
|
||||
...mailOpts,
|
||||
@@ -75,4 +86,21 @@ export class ContactMailNotification {
|
||||
body,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the common format args.
|
||||
* @param {number} tenantId
|
||||
* @returns {Promise<Record<string, string>>}
|
||||
*/
|
||||
public async getCommonFormatArgs(
|
||||
tenantId: number
|
||||
): Promise<Record<string, string>> {
|
||||
const organization = await Tenant.query()
|
||||
.findById(tenantId)
|
||||
.withGraphFetched('metadata');
|
||||
|
||||
return {
|
||||
CompanyName: organization.metadata.name,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
export const ERRORS = {
|
||||
MAIL_FROM_NOT_FOUND: 'Mail from address not found',
|
||||
MAIL_TO_NOT_FOUND: 'Mail to address not found',
|
||||
MAIL_SUBJECT_NOT_FOUND: 'Mail subject not found',
|
||||
MAIL_BODY_NOT_FOUND: 'Mail body not found',
|
||||
};
|
||||
33
packages/server/src/services/MailNotification/utils.ts
Normal file
33
packages/server/src/services/MailNotification/utils.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { isEmpty } from 'lodash';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { CommonMailOptions, CommonMailOptionsDTO } from '@/interfaces';
|
||||
import { ERRORS } from './constants';
|
||||
|
||||
/**
|
||||
* Merges the mail options with incoming options.
|
||||
* @param {Partial<SaleInvoiceMailOptions>} mailOptions
|
||||
* @param {Partial<SendInvoiceMailDTO>} overridedOptions
|
||||
* @throws {ServiceError}
|
||||
*/
|
||||
export function parseAndValidateMailOptions(
|
||||
mailOptions: Partial<CommonMailOptions>,
|
||||
overridedOptions: Partial<CommonMailOptionsDTO>
|
||||
) {
|
||||
const mergedMessageOptions = {
|
||||
...mailOptions,
|
||||
...overridedOptions,
|
||||
};
|
||||
if (isEmpty(mergedMessageOptions.from)) {
|
||||
throw new ServiceError(ERRORS.MAIL_FROM_NOT_FOUND);
|
||||
}
|
||||
if (isEmpty(mergedMessageOptions.to)) {
|
||||
throw new ServiceError(ERRORS.MAIL_TO_NOT_FOUND);
|
||||
}
|
||||
if (isEmpty(mergedMessageOptions.subject)) {
|
||||
throw new ServiceError(ERRORS.MAIL_SUBJECT_NOT_FOUND);
|
||||
}
|
||||
if (isEmpty(mergedMessageOptions.body)) {
|
||||
throw new ServiceError(ERRORS.MAIL_BODY_NOT_FOUND);
|
||||
}
|
||||
return mergedMessageOptions;
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
ISaleEstimateDTO,
|
||||
ISalesEstimatesFilter,
|
||||
SaleEstimateMailOptions,
|
||||
SaleEstimateMailOptionsDTO,
|
||||
} from '@/interfaces';
|
||||
import { EditSaleEstimate } from './EditSaleEstimate';
|
||||
import { DeleteSaleEstimate } from './DeleteSaleEstimate';
|
||||
@@ -224,8 +225,8 @@ export class SaleEstimatesApplication {
|
||||
public sendSaleEstimateMail(
|
||||
tenantId: number,
|
||||
saleEstimateId: number,
|
||||
saleEstimateMailOpts: SaleEstimateMailOptions
|
||||
) {
|
||||
saleEstimateMailOpts: SaleEstimateMailOptionsDTO
|
||||
): Promise<void> {
|
||||
return this.sendEstimateMailService.triggerMail(
|
||||
tenantId,
|
||||
saleEstimateId,
|
||||
@@ -235,11 +236,14 @@ export class SaleEstimatesApplication {
|
||||
|
||||
/**
|
||||
* Retrieves the default mail options of the given sale estimate.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleEstimateId
|
||||
* @returns {}
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleEstimateId
|
||||
* @returns {Promise<SaleEstimateMailOptions>}
|
||||
*/
|
||||
public getSaleEstimateMail(tenantId: number, saleEstimateId: number) {
|
||||
public getSaleEstimateMail(
|
||||
tenantId: number,
|
||||
saleEstimateId: number
|
||||
): Promise<SaleEstimateMailOptions> {
|
||||
return this.sendEstimateMailService.getMailOptions(
|
||||
tenantId,
|
||||
saleEstimateId
|
||||
|
||||
@@ -7,8 +7,12 @@ import {
|
||||
} from './constants';
|
||||
import { SaleEstimatesPdf } from './SaleEstimatesPdf';
|
||||
import { GetSaleEstimate } from './GetSaleEstimate';
|
||||
import { SaleEstimateMailOptions } from '@/interfaces';
|
||||
import {
|
||||
SaleEstimateMailOptions,
|
||||
SaleEstimateMailOptionsDTO,
|
||||
} from '@/interfaces';
|
||||
import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification';
|
||||
import { parseAndValidateMailOptions } from '@/services/MailNotification/utils';
|
||||
|
||||
@Service()
|
||||
export class SendSaleEstimateMail {
|
||||
@@ -31,13 +35,14 @@ export class SendSaleEstimateMail {
|
||||
* Triggers the reminder mail of the given sale estimate.
|
||||
* @param {number} tenantId -
|
||||
* @param {number} saleEstimateId -
|
||||
* @param {SaleEstimateMailOptions} messageOptions -
|
||||
* @param {SaleEstimateMailOptionsDTO} messageOptions -
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async triggerMail(
|
||||
tenantId: number,
|
||||
saleEstimateId: number,
|
||||
messageOptions: SaleEstimateMailOptions
|
||||
) {
|
||||
messageOptions: SaleEstimateMailOptionsDTO
|
||||
): Promise<void> {
|
||||
const payload = {
|
||||
tenantId,
|
||||
saleEstimateId,
|
||||
@@ -48,9 +53,9 @@ export class SendSaleEstimateMail {
|
||||
|
||||
/**
|
||||
* Formates the text of the mail.
|
||||
* @param {number} tenantId
|
||||
* @param {number} estimateId
|
||||
* @param {string} text
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} estimateId - Estimate id.
|
||||
* @returns {Promise<Record<string, any>>}
|
||||
*/
|
||||
public formatterData = async (tenantId: number, estimateId: number) => {
|
||||
const estimate = await this.getSaleEstimateService.getEstimate(
|
||||
@@ -70,9 +75,12 @@ export class SendSaleEstimateMail {
|
||||
* Retrieves the mail options.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleEstimateId
|
||||
* @returns
|
||||
* @returns {Promise<SaleEstimateMailOptions>}
|
||||
*/
|
||||
public getMailOptions = async (tenantId: number, saleEstimateId: number) => {
|
||||
public getMailOptions = async (
|
||||
tenantId: number,
|
||||
saleEstimateId: number
|
||||
): Promise<SaleEstimateMailOptions> => {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
|
||||
const saleEstimate = await SaleEstimate.query()
|
||||
@@ -91,6 +99,7 @@ export class SendSaleEstimateMail {
|
||||
return {
|
||||
...mailOptions,
|
||||
data: formatterData,
|
||||
attachEstimate: true
|
||||
};
|
||||
};
|
||||
|
||||
@@ -99,26 +108,28 @@ export class SendSaleEstimateMail {
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleEstimateId
|
||||
* @param {SaleEstimateMailOptions} messageOptions
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async sendMail(
|
||||
tenantId: number,
|
||||
saleEstimateId: number,
|
||||
messageOptions: SaleEstimateMailOptions
|
||||
) {
|
||||
messageOptions: SaleEstimateMailOptionsDTO
|
||||
): Promise<void> {
|
||||
const localMessageOpts = await this.getMailOptions(
|
||||
tenantId,
|
||||
saleEstimateId
|
||||
);
|
||||
const messageOpts = {
|
||||
...localMessageOpts,
|
||||
...messageOptions,
|
||||
};
|
||||
// Overrides and validates the given mail options.
|
||||
const messageOpts = parseAndValidateMailOptions(
|
||||
localMessageOpts,
|
||||
messageOptions
|
||||
);
|
||||
const mail = new Mail()
|
||||
.setSubject(messageOpts.subject)
|
||||
.setTo(messageOpts.to)
|
||||
.setContent(messageOpts.body);
|
||||
|
||||
if (messageOpts.to) {
|
||||
if (messageOpts.attachEstimate) {
|
||||
const estimatePdfBuffer = await this.estimatePdf.getSaleEstimatePdf(
|
||||
tenantId,
|
||||
saleEstimateId
|
||||
|
||||
@@ -300,7 +300,10 @@ export class SaleInvoiceApplication {
|
||||
* @returns {}
|
||||
*/
|
||||
public getSaleInvoiceMailReminder(tenantId: number, saleInvoiceId: number) {
|
||||
return this.sendInvoiceReminderService.getMailOpts(tenantId, saleInvoiceId);
|
||||
return this.sendInvoiceReminderService.getMailOption(
|
||||
tenantId,
|
||||
saleInvoiceId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -347,6 +350,9 @@ export class SaleInvoiceApplication {
|
||||
* @returns {Promise<SendInvoiceMailDTO>}
|
||||
*/
|
||||
public getSaleInvoiceMail(tenantId: number, saleInvoiceid: number) {
|
||||
return this.sendSaleInvoiceMailService.getMailOpts(tenantId, saleInvoiceid);
|
||||
return this.sendSaleInvoiceMailService.getMailOption(
|
||||
tenantId,
|
||||
saleInvoiceid
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { SaleInvoiceMailOptions } from '@/interfaces';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { GetSaleInvoice } from './GetSaleInvoice';
|
||||
import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification';
|
||||
import {
|
||||
DEFAULT_INVOICE_MAIL_CONTENT,
|
||||
DEFAULT_INVOICE_MAIL_SUBJECT,
|
||||
} from './constants';
|
||||
import { GetSaleInvoice } from './GetSaleInvoice';
|
||||
import { Tenant } from '@/system/models';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification';
|
||||
|
||||
@Service()
|
||||
export class SendSaleInvoiceMailCommon {
|
||||
@@ -28,9 +25,9 @@ export class SendSaleInvoiceMailCommon {
|
||||
* @param {number} invoiceId - Invoice id.
|
||||
* @param {string} defaultSubject - Subject text.
|
||||
* @param {string} defaultBody - Subject body.
|
||||
* @returns {}
|
||||
* @returns {Promise<SaleInvoiceMailOptions>}
|
||||
*/
|
||||
public async getMailOpts(
|
||||
public async getMailOption(
|
||||
tenantId: number,
|
||||
invoiceId: number,
|
||||
defaultSubject: string = DEFAULT_INVOICE_MAIL_SUBJECT,
|
||||
@@ -44,13 +41,17 @@ export class SendSaleInvoiceMailCommon {
|
||||
|
||||
const formatterData = await this.formatText(tenantId, invoiceId);
|
||||
|
||||
return this.contactMailNotification.getMailOptions(
|
||||
const mailOptions = await this.contactMailNotification.getMailOptions(
|
||||
tenantId,
|
||||
saleInvoice.customerId,
|
||||
defaultSubject,
|
||||
defaultBody,
|
||||
formatterData
|
||||
);
|
||||
return {
|
||||
...mailOptions,
|
||||
attachInvoice: true,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,12 +69,8 @@ export class SendSaleInvoiceMailCommon {
|
||||
tenantId,
|
||||
invoiceId
|
||||
);
|
||||
const organization = await Tenant.query()
|
||||
.findById(tenantId)
|
||||
.withGraphFetched('metadata');
|
||||
|
||||
return {
|
||||
CompanyName: organization.metadata.name,
|
||||
CustomerName: invoice.customer.displayName,
|
||||
InvoiceNumber: invoice.invoiceNo,
|
||||
InvoiceDueAmount: invoice.dueAmountFormatted,
|
||||
@@ -83,33 +80,4 @@ export class SendSaleInvoiceMailCommon {
|
||||
OverdueDays: invoice.overdueDays,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates the mail notification options before sending it.
|
||||
* @param {Partial<SaleInvoiceMailOptions>} mailNotificationOpts
|
||||
* @throws {ServiceError}
|
||||
*/
|
||||
public validateMailNotification(
|
||||
mailNotificationOpts: Partial<SaleInvoiceMailOptions>
|
||||
) {
|
||||
if (isEmpty(mailNotificationOpts.from)) {
|
||||
throw new ServiceError(ERRORS.MAIL_FROM_NOT_FOUND);
|
||||
}
|
||||
if (isEmpty(mailNotificationOpts.to)) {
|
||||
throw new ServiceError(ERRORS.MAIL_TO_NOT_FOUND);
|
||||
}
|
||||
if (isEmpty(mailNotificationOpts.subject)) {
|
||||
throw new ServiceError(ERRORS.MAIL_SUBJECT_NOT_FOUND);
|
||||
}
|
||||
if (isEmpty(mailNotificationOpts.body)) {
|
||||
throw new ServiceError(ERRORS.MAIL_BODY_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ERRORS = {
|
||||
MAIL_FROM_NOT_FOUND: 'Mail from address not found',
|
||||
MAIL_TO_NOT_FOUND: 'Mail to address not found',
|
||||
MAIL_SUBJECT_NOT_FOUND: 'Mail subject not found',
|
||||
MAIL_BODY_NOT_FOUND: 'Mail body not found',
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
DEFAULT_INVOICE_MAIL_CONTENT,
|
||||
DEFAULT_INVOICE_MAIL_SUBJECT,
|
||||
} from './constants';
|
||||
import { parseAndValidateMailOptions } from '@/services/MailNotification/utils';
|
||||
|
||||
@Service()
|
||||
export class SendSaleInvoiceMail {
|
||||
@@ -44,8 +45,8 @@ export class SendSaleInvoiceMail {
|
||||
* @param {number} saleInvoiceId
|
||||
* @returns {Promise<SaleInvoiceMailOptions>}
|
||||
*/
|
||||
public async getMailOpts(tenantId: number, saleInvoiceId: number) {
|
||||
return this.invoiceMail.getMailOpts(
|
||||
public async getMailOption(tenantId: number, saleInvoiceId: number) {
|
||||
return this.invoiceMail.getMailOption(
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
DEFAULT_INVOICE_MAIL_SUBJECT,
|
||||
@@ -65,15 +66,15 @@ export class SendSaleInvoiceMail {
|
||||
saleInvoiceId: number,
|
||||
messageDTO: SendInvoiceMailDTO
|
||||
) {
|
||||
const defaultMessageOpts = await this.getMailOpts(tenantId, saleInvoiceId);
|
||||
|
||||
// Parsed message opts with default options.
|
||||
const messageOpts = {
|
||||
...defaultMessageOpts,
|
||||
...messageDTO,
|
||||
};
|
||||
this.invoiceMail.validateMailNotification(messageOpts);
|
||||
|
||||
const defaultMessageOpts = await this.getMailOption(
|
||||
tenantId,
|
||||
saleInvoiceId
|
||||
);
|
||||
// Merge message opts with default options and validate the incoming options.
|
||||
const messageOpts = parseAndValidateMailOptions(
|
||||
defaultMessageOpts,
|
||||
messageDTO
|
||||
);
|
||||
const mail = new Mail()
|
||||
.setSubject(messageOpts.subject)
|
||||
.setTo(messageOpts.to)
|
||||
|
||||
@@ -43,8 +43,8 @@ export class SendInvoiceMailReminder {
|
||||
* @param {number} saleInvoiceId
|
||||
* @returns {Promise<SaleInvoiceMailOptions>}
|
||||
*/
|
||||
public async getMailOpts(tenantId: number, saleInvoiceId: number) {
|
||||
return this.invoiceCommonMail.getMailOpts(
|
||||
public async getMailOption(tenantId: number, saleInvoiceId: number) {
|
||||
return this.invoiceCommonMail.getMailOption(
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
DEFAULT_INVOICE_REMINDER_MAIL_SUBJECT,
|
||||
@@ -64,7 +64,7 @@ export class SendInvoiceMailReminder {
|
||||
saleInvoiceId: number,
|
||||
messageOptions: SendInvoiceMailDTO
|
||||
) {
|
||||
const localMessageOpts = await this.getMailOpts(tenantId, saleInvoiceId);
|
||||
const localMessageOpts = await this.getMailOption(tenantId, saleInvoiceId);
|
||||
|
||||
const messageOpts = {
|
||||
...localMessageOpts,
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { IPaymentReceiveMailOpts, SendInvoiceMailDTO } from '@/interfaces';
|
||||
import {
|
||||
PaymentReceiveMailOpts,
|
||||
PaymentReceiveMailOptsDTO,
|
||||
SendInvoiceMailDTO,
|
||||
} from '@/interfaces';
|
||||
import Mail from '@/lib/Mail';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import {
|
||||
DEFAULT_PAYMENT_MAIL_CONTENT,
|
||||
DEFAULT_PAYMENT_MAIL_SUBJECT,
|
||||
} from './constants';
|
||||
import { Tenant } from '@/system/models';
|
||||
import { GetPaymentReceive } from './GetPaymentReceive';
|
||||
import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification';
|
||||
import { parseAndValidateMailOptions } from '@/services/MailNotification/utils';
|
||||
|
||||
@Service()
|
||||
export class SendPaymentReceiveMailNotification {
|
||||
@@ -28,13 +32,14 @@ export class SendPaymentReceiveMailNotification {
|
||||
* Sends the mail of the given payment receive.
|
||||
* @param {number} tenantId
|
||||
* @param {number} paymentReceiveId
|
||||
* @param {SendInvoiceMailDTO} messageDTO
|
||||
* @param {PaymentReceiveMailOptsDTO} messageDTO
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async triggerMail(
|
||||
tenantId: number,
|
||||
paymentReceiveId: number,
|
||||
messageDTO: IPaymentReceiveMailOpts
|
||||
) {
|
||||
messageDTO: PaymentReceiveMailOptsDTO
|
||||
): Promise<void> {
|
||||
const payload = {
|
||||
tenantId,
|
||||
paymentReceiveId,
|
||||
@@ -45,18 +50,21 @@ export class SendPaymentReceiveMailNotification {
|
||||
|
||||
/**
|
||||
* Retrieves the default payment mail options.
|
||||
* @param {number} tenantId
|
||||
* @param {number} invoiceId
|
||||
* @returns {Promise<SendInvoiceMailDTO>}
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} paymentReceiveId - Payment receive id.
|
||||
* @returns {Promise<PaymentReceiveMailOpts>}
|
||||
*/
|
||||
public getMailOptions = async (tenantId: number, invoiceId: number) => {
|
||||
public getMailOptions = async (
|
||||
tenantId: number,
|
||||
paymentId: number
|
||||
): Promise<PaymentReceiveMailOpts> => {
|
||||
const { PaymentReceive } = this.tenancy.models(tenantId);
|
||||
|
||||
const paymentReceive = await PaymentReceive.query()
|
||||
.findById(invoiceId)
|
||||
.findById(paymentId)
|
||||
.throwIfNotFound();
|
||||
|
||||
const formatterData = await this.textFormatter(tenantId, invoiceId);
|
||||
const formatterData = await this.textFormatter(tenantId, paymentId);
|
||||
|
||||
return this.contactMailNotification.getMailOptions(
|
||||
tenantId,
|
||||
@@ -82,12 +90,7 @@ export class SendPaymentReceiveMailNotification {
|
||||
tenantId,
|
||||
invoiceId
|
||||
);
|
||||
const organization = await Tenant.query()
|
||||
.findById(tenantId)
|
||||
.withGraphFetched('metadata');
|
||||
|
||||
return {
|
||||
CompanyName: organization.metadata.name,
|
||||
CustomerName: payment.customer.displayName,
|
||||
PaymentNumber: payment.payment_receive_no,
|
||||
PaymentDate: payment.formattedPaymentDate,
|
||||
@@ -112,10 +115,10 @@ export class SendPaymentReceiveMailNotification {
|
||||
paymentReceiveId
|
||||
);
|
||||
// Parsed message opts with default options.
|
||||
const parsedMessageOpts = {
|
||||
...defaultMessageOpts,
|
||||
...messageDTO,
|
||||
};
|
||||
const parsedMessageOpts = parseAndValidateMailOptions(
|
||||
defaultMessageOpts,
|
||||
messageDTO
|
||||
);
|
||||
await new Mail()
|
||||
.setSubject(parsedMessageOpts.subject)
|
||||
.setTo(parsedMessageOpts.to)
|
||||
|
||||
@@ -4,10 +4,10 @@ import {
|
||||
IPaymentReceive,
|
||||
IPaymentReceiveCreateDTO,
|
||||
IPaymentReceiveEditDTO,
|
||||
IPaymentReceiveMailOpts,
|
||||
IPaymentReceiveSmsDetails,
|
||||
IPaymentReceivesFilter,
|
||||
ISystemUser,
|
||||
PaymentReceiveMailOptsDTO,
|
||||
} from '@/interfaces';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { CreatePaymentReceive } from './CreatePaymentReceive';
|
||||
@@ -189,8 +189,8 @@ export class PaymentReceivesApplication {
|
||||
public notifyPaymentByMail(
|
||||
tenantId: number,
|
||||
paymentReceiveId: number,
|
||||
messageOpts: IPaymentReceiveMailOpts
|
||||
) {
|
||||
messageOpts: PaymentReceiveMailOptsDTO
|
||||
): Promise<void> {
|
||||
return this.paymentMailNotify.triggerMail(
|
||||
tenantId,
|
||||
paymentReceiveId,
|
||||
@@ -204,7 +204,7 @@ export class PaymentReceivesApplication {
|
||||
* @param {number} paymentReceiveId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public getPaymentDefaultMail(tenantId: number, paymentReceiveId: number) {
|
||||
public getPaymentMailOptions(tenantId: number, paymentReceiveId: number) {
|
||||
return this.paymentMailNotify.getMailOptions(tenantId, paymentReceiveId);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
ISaleReceipt,
|
||||
ISalesReceiptsFilter,
|
||||
SaleReceiptMailOpts,
|
||||
SaleReceiptMailOptsDTO,
|
||||
} from '@/interfaces';
|
||||
import { EditSaleReceipt } from './EditSaleReceipt';
|
||||
import { GetSaleReceipt } from './GetSaleReceipt';
|
||||
@@ -176,12 +177,13 @@ export class SaleReceiptApplication {
|
||||
* Sends the receipt mail of the given sale receipt.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleReceiptId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public sendSaleReceiptMail(
|
||||
tenantId: number,
|
||||
saleReceiptId: number,
|
||||
messageOpts: SaleReceiptMailOpts
|
||||
) {
|
||||
messageOpts: SaleReceiptMailOptsDTO
|
||||
): Promise<void> {
|
||||
return this.saleReceiptNotifyByMailService.triggerMail(
|
||||
tenantId,
|
||||
saleReceiptId,
|
||||
@@ -193,9 +195,12 @@ export class SaleReceiptApplication {
|
||||
* Retrieves the default mail options of the given sale receipt.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleReceiptId
|
||||
* @returns
|
||||
* @returns {Promise<SaleReceiptMailOpts>}
|
||||
*/
|
||||
public getSaleReceiptMail(tenantId: number, saleReceiptId: number) {
|
||||
public getSaleReceiptMail(
|
||||
tenantId: number,
|
||||
saleReceiptId: number
|
||||
): Promise<SaleReceiptMailOpts> {
|
||||
return this.saleReceiptNotifyByMailService.getMailOptions(
|
||||
tenantId,
|
||||
saleReceiptId
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import * as R from 'ramda';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Tenant } from '@/system/models';
|
||||
import Mail from '@/lib/Mail';
|
||||
import { GetSaleReceipt } from './GetSaleReceipt';
|
||||
import { SaleReceiptsPdf } from './SaleReceiptsPdfService';
|
||||
@@ -9,8 +7,9 @@ import {
|
||||
DEFAULT_RECEIPT_MAIL_CONTENT,
|
||||
DEFAULT_RECEIPT_MAIL_SUBJECT,
|
||||
} from './constants';
|
||||
import { SaleReceiptMailOpts } from '@/interfaces';
|
||||
import { SaleReceiptMailOpts, SaleReceiptMailOptsDTO } from '@/interfaces';
|
||||
import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification';
|
||||
import { parseAndValidateMailOptions } from '@/services/MailNotification/utils';
|
||||
|
||||
@Service()
|
||||
export class SaleReceiptMailNotification {
|
||||
@@ -32,13 +31,13 @@ export class SaleReceiptMailNotification {
|
||||
/**
|
||||
* Sends the receipt mail of the given sale receipt.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleInvoiceId
|
||||
* @param {SendInvoiceMailDTO} messageDTO
|
||||
* @param {number} saleReceiptId
|
||||
* @param {SaleReceiptMailOptsDTO} messageDTO
|
||||
*/
|
||||
public async triggerMail(
|
||||
tenantId: number,
|
||||
saleReceiptId: number,
|
||||
messageOpts: SaleReceiptMailOpts
|
||||
messageOpts: SaleReceiptMailOptsDTO
|
||||
) {
|
||||
const payload = {
|
||||
tenantId,
|
||||
@@ -52,9 +51,12 @@ export class SaleReceiptMailNotification {
|
||||
* Retrieves the mail options of the given sale receipt.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleReceiptId
|
||||
* @returns
|
||||
* @returns {Promise<SaleReceiptMailOptsDTO>}
|
||||
*/
|
||||
public async getMailOptions(tenantId: number, saleReceiptId: number) {
|
||||
public async getMailOptions(
|
||||
tenantId: number,
|
||||
saleReceiptId: number
|
||||
): Promise<SaleReceiptMailOpts> {
|
||||
const { SaleReceipt } = this.tenancy.models(tenantId);
|
||||
|
||||
const saleReceipt = await SaleReceipt.query()
|
||||
@@ -63,17 +65,21 @@ export class SaleReceiptMailNotification {
|
||||
|
||||
const formattedData = await this.textFormatter(tenantId, saleReceiptId);
|
||||
|
||||
return this.contactMailNotification.getMailOptions(
|
||||
const mailOpts = await this.contactMailNotification.getMailOptions(
|
||||
tenantId,
|
||||
saleReceipt.customerId,
|
||||
DEFAULT_RECEIPT_MAIL_SUBJECT,
|
||||
DEFAULT_RECEIPT_MAIL_CONTENT,
|
||||
formattedData
|
||||
);
|
||||
return {
|
||||
...mailOpts,
|
||||
attachReceipt: true,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the formatted text of the given sale invoice.
|
||||
* Retrieves the formatted text of the given sale receipt.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} receiptId - Sale receipt id.
|
||||
* @param {string} text - The given text.
|
||||
@@ -83,58 +89,52 @@ export class SaleReceiptMailNotification {
|
||||
tenantId: number,
|
||||
receiptId: number
|
||||
): Promise<Record<string, string>> => {
|
||||
const invoice = await this.getSaleReceiptService.getSaleReceipt(
|
||||
const receipt = await this.getSaleReceiptService.getSaleReceipt(
|
||||
tenantId,
|
||||
receiptId
|
||||
);
|
||||
const organization = await Tenant.query()
|
||||
.findById(tenantId)
|
||||
.withGraphFetched('metadata');
|
||||
|
||||
return {
|
||||
CompanyName: organization.metadata.name,
|
||||
CustomerName: invoice.customer.displayName,
|
||||
ReceiptNumber: invoice.receiptNumber,
|
||||
ReceiptDate: invoice.formattedReceiptDate,
|
||||
ReceiptAmount: invoice.formattedAmount,
|
||||
CustomerName: receipt.customer.displayName,
|
||||
ReceiptNumber: receipt.receiptNumber,
|
||||
ReceiptDate: receipt.formattedReceiptDate,
|
||||
ReceiptAmount: receipt.formattedAmount,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Triggers the mail invoice.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleInvoiceId
|
||||
* @param {SendInvoiceMailDTO} messageDTO
|
||||
* 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.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async sendMail(
|
||||
tenantId: number,
|
||||
saleReceiptId: number,
|
||||
messageOpts: SaleReceiptMailOpts
|
||||
messageOpts: SaleReceiptMailOptsDTO
|
||||
) {
|
||||
const defaultMessageOpts = await this.getMailOptions(
|
||||
tenantId,
|
||||
saleReceiptId
|
||||
);
|
||||
// Parsed message opts with default options.
|
||||
const parsedMessageOpts = {
|
||||
...defaultMessageOpts,
|
||||
...messageOpts,
|
||||
};
|
||||
|
||||
// Merges message opts with default options.
|
||||
const parsedMessageOpts = parseAndValidateMailOptions(
|
||||
defaultMessageOpts,
|
||||
messageOpts
|
||||
);
|
||||
const mail = new Mail()
|
||||
.setSubject(parsedMessageOpts.subject)
|
||||
.setTo(parsedMessageOpts.to)
|
||||
.setContent(parsedMessageOpts.body);
|
||||
|
||||
if (parsedMessageOpts.attachInvoice) {
|
||||
// Retrieves document buffer of the invoice pdf document.
|
||||
if (parsedMessageOpts.attachReceipt) {
|
||||
// Retrieves document buffer of the receipt pdf document.
|
||||
const receiptPdfBuffer = await this.receiptPdfService.saleReceiptPdf(
|
||||
tenantId,
|
||||
saleReceiptId
|
||||
);
|
||||
mail.setAttachments([
|
||||
{ filename: 'invoice.pdf', content: receiptPdfBuffer },
|
||||
{ filename: 'receipt.pdf', content: receiptPdfBuffer },
|
||||
]);
|
||||
}
|
||||
await mail.send();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const DEFAULT_RECEIPT_MAIL_SUBJECT =
|
||||
'Invoice {InvoiceNumber} from {CompanyName}';
|
||||
'Receipt {ReceiptNumber} from {CompanyName}';
|
||||
export const DEFAULT_RECEIPT_MAIL_CONTENT = `
|
||||
<p>Dear {CustomerName}</p>
|
||||
<p>Thank you for your business, You can view or print your receipt from attachements.</p>
|
||||
|
||||
Reference in New Issue
Block a user