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:
@@ -4,9 +4,8 @@ import { body, check, param, query, ValidationChain } from 'express-validator';
|
||||
import {
|
||||
AbilitySubject,
|
||||
IPaymentReceiveDTO,
|
||||
IPaymentReceiveMailOpts,
|
||||
// IPaymentReceiveMailOpts,
|
||||
PaymentReceiveAction,
|
||||
PaymentReceiveMailOptsDTO,
|
||||
} from '@/interfaces';
|
||||
import BaseController from '@/api/controllers/BaseController';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
@@ -541,9 +540,12 @@ export default class PaymentReceivesController extends BaseController {
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
const { id: paymentReceiveId } = req.params;
|
||||
const paymentMailDTO: IPaymentReceiveMailOpts = this.matchedBodyData(req, {
|
||||
includeOptionals: false,
|
||||
});
|
||||
const paymentMailDTO: PaymentReceiveMailOptsDTO = this.matchedBodyData(
|
||||
req,
|
||||
{
|
||||
includeOptionals: false,
|
||||
}
|
||||
);
|
||||
try {
|
||||
await this.paymentReceiveApplication.notifyPaymentByMail(
|
||||
tenantId,
|
||||
@@ -574,7 +576,7 @@ export default class PaymentReceivesController extends BaseController {
|
||||
const { id: paymentReceiveId } = req.params;
|
||||
|
||||
try {
|
||||
const data = await this.paymentReceiveApplication.getPaymentDefaultMail(
|
||||
const data = await this.paymentReceiveApplication.getPaymentMailOptions(
|
||||
tenantId,
|
||||
paymentReceiveId
|
||||
);
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
AbilitySubject,
|
||||
ISaleEstimateDTO,
|
||||
SaleEstimateAction,
|
||||
SaleEstimateMailOptions,
|
||||
SaleEstimateMailOptionsDTO,
|
||||
} from '@/interfaces';
|
||||
import BaseController from '@/api/controllers/BaseController';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
@@ -513,10 +513,12 @@ export default class SalesEstimatesController extends BaseController {
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
const { id: invoiceId } = req.params;
|
||||
const saleEstimateDTO: SaleEstimateMailOptions = this.matchedBodyData(req, {
|
||||
includeOptionals: false,
|
||||
});
|
||||
|
||||
const saleEstimateDTO: SaleEstimateMailOptionsDTO = this.matchedBodyData(
|
||||
req,
|
||||
{
|
||||
includeOptionals: false,
|
||||
}
|
||||
);
|
||||
try {
|
||||
await this.saleEstimatesApplication.sendSaleEstimateMail(
|
||||
tenantId,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { body, check, param, query } from 'express-validator';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
import BaseController from '../BaseController';
|
||||
import { ISaleReceiptDTO, SaleReceiptMailOpts } from '@/interfaces/SaleReceipt';
|
||||
import { ISaleReceiptDTO, SaleReceiptMailOpts, SaleReceiptMailOptsDTO } from '@/interfaces/SaleReceipt';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
@@ -54,7 +54,7 @@ export default class SalesReceiptsController extends BaseController {
|
||||
body('from').isString().optional(),
|
||||
body('to').isString().optional(),
|
||||
body('body').isString().optional(),
|
||||
body('attach_invoice').optional().isBoolean().toBoolean(),
|
||||
body('attach_receipt').optional().isBoolean().toBoolean(),
|
||||
],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.sendSaleReceiptMail.bind(this)),
|
||||
@@ -439,7 +439,7 @@ export default class SalesReceiptsController extends BaseController {
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
const { id: receiptId } = req.params;
|
||||
const receiptMailDTO: SaleReceiptMailOpts = this.matchedBodyData(req, {
|
||||
const receiptMailDTO: SaleReceiptMailOptsDTO = this.matchedBodyData(req, {
|
||||
includeOptionals: false,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
export type IMailAttachment = MailAttachmentPath | MailAttachmentContent;
|
||||
|
||||
export interface MailAttachmentPath {
|
||||
filename: string;
|
||||
path: string;
|
||||
cid: string;
|
||||
}
|
||||
export interface MailAttachmentContent {
|
||||
filename: string;
|
||||
content: Buffer;
|
||||
}
|
||||
|
||||
export interface IMailable {
|
||||
constructor(
|
||||
view: string,
|
||||
data?: { [key: string]: string | number },
|
||||
);
|
||||
constructor(view: string, data?: { [key: string]: string | number });
|
||||
send(): Promise<any>;
|
||||
build(): void;
|
||||
setData(data: { [key: string]: string | number }): IMailable;
|
||||
@@ -13,4 +21,27 @@ export interface IMailable {
|
||||
setView(view: string): IMailable;
|
||||
render(data?: { [key: string]: string | number }): string;
|
||||
getViewContent(): string;
|
||||
}
|
||||
}
|
||||
|
||||
export interface AddressItem {
|
||||
label: string;
|
||||
mail: string;
|
||||
primary?: boolean;
|
||||
}
|
||||
|
||||
export interface CommonMailOptions {
|
||||
toAddresses: AddressItem[];
|
||||
fromAddresses: AddressItem[];
|
||||
from: string;
|
||||
to: string | string[];
|
||||
subject: string;
|
||||
body: string;
|
||||
data?: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface CommonMailOptionsDTO {
|
||||
to?: string | string[];
|
||||
from?: string;
|
||||
subject?: string;
|
||||
body?: string;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { Knex } from 'knex';
|
||||
import { ISystemUser } from '@/interfaces';
|
||||
import {
|
||||
CommonMailOptions,
|
||||
CommonMailOptionsDTO,
|
||||
ISystemUser,
|
||||
} from '@/interfaces';
|
||||
import { ILedgerEntry } from './Ledger';
|
||||
import { ISaleInvoice } from './SaleInvoice';
|
||||
|
||||
@@ -19,7 +23,7 @@ export interface IPaymentReceive {
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
localAmount?: number;
|
||||
branchId?: number
|
||||
branchId?: number;
|
||||
}
|
||||
export interface IPaymentReceiveCreateDTO {
|
||||
customerId: number;
|
||||
@@ -166,6 +170,6 @@ export type IPaymentReceiveGLCommonEntry = Pick<
|
||||
| 'branchId'
|
||||
>;
|
||||
|
||||
export interface IPaymentReceiveMailOpts {
|
||||
|
||||
}
|
||||
export interface PaymentReceiveMailOpts extends CommonMailOptions {}
|
||||
|
||||
export interface PaymentReceiveMailOptsDTO extends CommonMailOptionsDTO {}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Knex } from 'knex';
|
||||
import { IItemEntry, IItemEntryDTO } from './ItemEntry';
|
||||
import { IDynamicListFilterDTO } from '@/interfaces/DynamicFilter';
|
||||
import { CommonMailOptions, CommonMailOptionsDTO } from './Mailable';
|
||||
|
||||
export interface ISaleEstimate {
|
||||
id?: number;
|
||||
@@ -125,10 +126,10 @@ export interface ISaleEstimateApprovedEvent {
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
export interface SaleEstimateMailOptions {
|
||||
to: string;
|
||||
from: string;
|
||||
subject: string;
|
||||
body: string;
|
||||
attachInvoice?: boolean;
|
||||
export interface SaleEstimateMailOptions extends CommonMailOptions {
|
||||
attachEstimate?: boolean;
|
||||
}
|
||||
|
||||
export interface SaleEstimateMailOptionsDTO extends CommonMailOptionsDTO {
|
||||
attachEstimate?: boolean;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Knex } from 'knex';
|
||||
import { ISystemUser, IAccount, ITaxTransaction, AddressItem } from '@/interfaces';
|
||||
import { ISystemUser, IAccount, ITaxTransaction } from '@/interfaces';
|
||||
import { CommonMailOptions, CommonMailOptionsDTO } from './Mailable';
|
||||
import { IDynamicListFilter } from '@/interfaces/DynamicFilter';
|
||||
import { IItemEntry, IItemEntryDTO } from './ItemEntry';
|
||||
|
||||
@@ -187,21 +188,11 @@ export enum SaleInvoiceAction {
|
||||
NotifyBySms = 'NotifyBySms',
|
||||
}
|
||||
|
||||
export interface SaleInvoiceMailOptions {
|
||||
toAddresses: AddressItem[];
|
||||
fromAddresses: AddressItem[];
|
||||
from: string;
|
||||
to: string | string[];
|
||||
subject: string;
|
||||
body: string;
|
||||
export interface SaleInvoiceMailOptions extends CommonMailOptions {
|
||||
attachInvoice: boolean;
|
||||
}
|
||||
|
||||
export interface SendInvoiceMailDTO {
|
||||
to: string | string[];
|
||||
from: string;
|
||||
subject: string;
|
||||
body: string;
|
||||
export interface SendInvoiceMailDTO extends CommonMailOptionsDTO {
|
||||
attachInvoice?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Knex } from 'knex';
|
||||
import { IItemEntry } from './ItemEntry';
|
||||
import { CommonMailOptions, CommonMailOptionsDTO } from './Mailable';
|
||||
|
||||
export interface ISaleReceipt {
|
||||
id?: number;
|
||||
@@ -135,6 +136,10 @@ export interface ISaleReceiptDeletingPayload {
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
export interface SaleReceiptMailOpts {
|
||||
|
||||
}
|
||||
export interface SaleReceiptMailOpts extends CommonMailOptions {
|
||||
attachReceipt: boolean;
|
||||
}
|
||||
|
||||
export interface SaleReceiptMailOptsDTO extends CommonMailOptionsDTO {
|
||||
attachReceipt?: boolean;
|
||||
}
|
||||
|
||||
@@ -2,18 +2,13 @@ import fs from 'fs';
|
||||
import Mustache from 'mustache';
|
||||
import { Container } from 'typedi';
|
||||
import path from 'path';
|
||||
import { IMailable } from '@/interfaces';
|
||||
|
||||
interface IMailAttachment {
|
||||
filename: string;
|
||||
path: string;
|
||||
cid: string;
|
||||
}
|
||||
import { IMailAttachment } from '@/interfaces';
|
||||
|
||||
export default class Mail {
|
||||
view: string;
|
||||
subject: string;
|
||||
to: string;
|
||||
subject: string = '';
|
||||
content: string = '';
|
||||
to: string | string[];
|
||||
from: string = `${process.env.MAIL_FROM_NAME} ${process.env.MAIL_FROM_ADDRESS}`;
|
||||
data: { [key: string]: string | number };
|
||||
attachments: IMailAttachment[];
|
||||
@@ -21,16 +16,24 @@ export default class Mail {
|
||||
/**
|
||||
* Mail options.
|
||||
*/
|
||||
private get mailOptions() {
|
||||
public get mailOptions() {
|
||||
return {
|
||||
to: this.to,
|
||||
from: this.from,
|
||||
subject: this.subject,
|
||||
html: this.render(this.data),
|
||||
html: this.html,
|
||||
attachments: this.attachments,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the html content of the mail.
|
||||
* @returns {string}
|
||||
*/
|
||||
public get html() {
|
||||
return this.view ? Mail.render(this.view, this.data) : this.content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the given mail to the target address.
|
||||
*/
|
||||
@@ -52,7 +55,7 @@ export default class Mail {
|
||||
* Set send mail to address.
|
||||
* @param {string} to -
|
||||
*/
|
||||
setTo(to: string) {
|
||||
setTo(to: string | string[]) {
|
||||
this.to = to;
|
||||
return this;
|
||||
}
|
||||
@@ -62,11 +65,16 @@ export default class Mail {
|
||||
* @param {string} from
|
||||
* @return {}
|
||||
*/
|
||||
private setFrom(from: string) {
|
||||
setFrom(from: string) {
|
||||
this.from = from;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set attachments to the mail.
|
||||
* @param {IMailAttachment[]} attachments
|
||||
* @returns {Mail}
|
||||
*/
|
||||
setAttachments(attachments: IMailAttachment[]) {
|
||||
this.attachments = attachments;
|
||||
return this;
|
||||
@@ -95,21 +103,26 @@ export default class Mail {
|
||||
return this;
|
||||
}
|
||||
|
||||
setContent(content: string) {
|
||||
this.content = content;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the view template with the given data.
|
||||
* @param {object} data
|
||||
* @return {string}
|
||||
*/
|
||||
render(data): string {
|
||||
const viewContent = this.getViewContent();
|
||||
static render(view: string, data: Record<string, any>): string {
|
||||
const viewContent = Mail.getViewContent(view);
|
||||
return Mustache.render(viewContent, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve view content from the view directory.
|
||||
*/
|
||||
private getViewContent(): string {
|
||||
const filePath = path.join(global.__views_dir, `/${this.view}`);
|
||||
static getViewContent(view: string): string {
|
||||
const filePath = path.join(global.__views_dir, `/${view}`);
|
||||
return fs.readFileSync(filePath, 'utf8');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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