feat: mail notifications of sales transactions

This commit is contained in:
Ahmed Bouhuolia
2023-12-30 17:49:02 +02:00
parent 0d15c16d40
commit ab7abfea35
25 changed files with 336 additions and 221 deletions

View File

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

View File

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

View 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;
}