mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 13:50:31 +00:00
refactor: split the services to multiple service classes (#202)
This commit is contained in:
@@ -0,0 +1,260 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import moment from 'moment';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import events from '@/subscribers/events';
|
||||
import {
|
||||
ISaleInvoice,
|
||||
ISaleInvoiceSmsDetailsDTO,
|
||||
ISaleInvoiceSmsDetails,
|
||||
SMS_NOTIFICATION_KEY,
|
||||
InvoiceNotificationType,
|
||||
ICustomer,
|
||||
} from '@/interfaces';
|
||||
import SmsNotificationsSettingsService from '@/services/Settings/SmsNotificationsSettings';
|
||||
import { formatSmsMessage, formatNumber } from 'utils';
|
||||
import { TenantMetadata } from '@/system/models';
|
||||
import SaleNotifyBySms from '../SaleNotifyBySms';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import { ERRORS } from './constants';
|
||||
import { CommandSaleInvoiceValidators } from './CommandSaleInvoiceValidators';
|
||||
|
||||
@Service()
|
||||
export class SaleInvoiceNotifyBySms {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private smsNotificationsSettings: SmsNotificationsSettingsService;
|
||||
|
||||
@Inject()
|
||||
private saleSmsNotification: SaleNotifyBySms;
|
||||
|
||||
@Inject()
|
||||
private validators: CommandSaleInvoiceValidators;
|
||||
|
||||
/**
|
||||
* Notify customer via sms about sale invoice.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} saleInvoiceId - Sale invoice id.
|
||||
*/
|
||||
public notifyBySms = async (
|
||||
tenantId: number,
|
||||
saleInvoiceId: number,
|
||||
invoiceNotificationType: InvoiceNotificationType
|
||||
) => {
|
||||
const { SaleInvoice } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve the sale invoice or throw not found service error.
|
||||
const saleInvoice = await SaleInvoice.query()
|
||||
.findById(saleInvoiceId)
|
||||
.withGraphFetched('customer');
|
||||
|
||||
// Validates the givne invoice existance.
|
||||
this.validators.validateInvoiceExistance(saleInvoice);
|
||||
|
||||
// Validate the customer phone number existance and number validation.
|
||||
this.saleSmsNotification.validateCustomerPhoneNumber(
|
||||
saleInvoice.customer.personalPhone
|
||||
);
|
||||
// Transformes the invoice notification key to sms notification key.
|
||||
const notificationKey = this.transformDTOKeyToNotificationKey(
|
||||
invoiceNotificationType
|
||||
);
|
||||
// Triggers `onSaleInvoiceNotifySms` event.
|
||||
await this.eventPublisher.emitAsync(events.saleInvoice.onNotifySms, {
|
||||
tenantId,
|
||||
saleInvoice,
|
||||
});
|
||||
// Formattes the sms message and sends sms notification.
|
||||
await this.sendSmsNotification(tenantId, notificationKey, saleInvoice);
|
||||
|
||||
// Triggers `onSaleInvoiceNotifySms` event.
|
||||
await this.eventPublisher.emitAsync(events.saleInvoice.onNotifiedSms, {
|
||||
tenantId,
|
||||
saleInvoice,
|
||||
});
|
||||
return saleInvoice;
|
||||
};
|
||||
|
||||
/**
|
||||
* Notify invoice details by sms notification after invoice creation.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleInvoiceId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public notifyDetailsBySmsAfterCreation = async (
|
||||
tenantId: number,
|
||||
saleInvoiceId: number
|
||||
): Promise<void> => {
|
||||
const notification = this.smsNotificationsSettings.getSmsNotificationMeta(
|
||||
tenantId,
|
||||
SMS_NOTIFICATION_KEY.SALE_INVOICE_DETAILS
|
||||
);
|
||||
// Can't continue if the sms auto-notification is not enabled.
|
||||
if (!notification.isNotificationEnabled) return;
|
||||
|
||||
await this.notifyBySms(tenantId, saleInvoiceId, 'details');
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends SMS notification.
|
||||
* @param {ISaleInvoice} invoice
|
||||
* @param {ICustomer} customer
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private sendSmsNotification = async (
|
||||
tenantId: number,
|
||||
notificationType:
|
||||
| SMS_NOTIFICATION_KEY.SALE_INVOICE_DETAILS
|
||||
| SMS_NOTIFICATION_KEY.SALE_INVOICE_REMINDER,
|
||||
invoice: ISaleInvoice & { customer: ICustomer }
|
||||
): Promise<void> => {
|
||||
const smsClient = this.tenancy.smsClient(tenantId);
|
||||
const tenantMetadata = await TenantMetadata.query().findOne({ tenantId });
|
||||
|
||||
// Formates the given sms message.
|
||||
const message = this.formattedInvoiceDetailsMessage(
|
||||
tenantId,
|
||||
notificationType,
|
||||
invoice,
|
||||
tenantMetadata
|
||||
);
|
||||
const phoneNumber = invoice.customer.personalPhone;
|
||||
|
||||
// Run the send sms notification message job.
|
||||
await smsClient.sendMessageJob(phoneNumber, message);
|
||||
};
|
||||
|
||||
/**
|
||||
* Formates the invoice details sms message.
|
||||
* @param {number} tenantId
|
||||
* @param {ISaleInvoice} invoice
|
||||
* @param {ICustomer} customer
|
||||
* @returns {string}
|
||||
*/
|
||||
private formattedInvoiceDetailsMessage = (
|
||||
tenantId: number,
|
||||
notificationKey:
|
||||
| SMS_NOTIFICATION_KEY.SALE_INVOICE_DETAILS
|
||||
| SMS_NOTIFICATION_KEY.SALE_INVOICE_REMINDER,
|
||||
invoice: ISaleInvoice,
|
||||
tenantMetadata: TenantMetadata
|
||||
): string => {
|
||||
const notification = this.smsNotificationsSettings.getSmsNotificationMeta(
|
||||
tenantId,
|
||||
notificationKey
|
||||
);
|
||||
return this.formatInvoiceDetailsMessage(
|
||||
notification.smsMessage,
|
||||
invoice,
|
||||
tenantMetadata
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Formattees the given invoice details sms message.
|
||||
* @param {string} smsMessage
|
||||
* @param {ISaleInvoice} invoice
|
||||
* @param {ICustomer} customer
|
||||
* @param {TenantMetadata} tenantMetadata
|
||||
*/
|
||||
private formatInvoiceDetailsMessage = (
|
||||
smsMessage: string,
|
||||
invoice: ISaleInvoice & { customer: ICustomer },
|
||||
tenantMetadata: TenantMetadata
|
||||
) => {
|
||||
const formattedDueAmount = formatNumber(invoice.dueAmount, {
|
||||
currencyCode: invoice.currencyCode,
|
||||
});
|
||||
const formattedAmount = formatNumber(invoice.balance, {
|
||||
currencyCode: invoice.currencyCode,
|
||||
});
|
||||
|
||||
return formatSmsMessage(smsMessage, {
|
||||
InvoiceNumber: invoice.invoiceNo,
|
||||
ReferenceNumber: invoice.referenceNo,
|
||||
CustomerName: invoice.customer.displayName,
|
||||
DueAmount: formattedDueAmount,
|
||||
DueDate: moment(invoice.dueDate).format('YYYY/MM/DD'),
|
||||
Amount: formattedAmount,
|
||||
CompanyName: tenantMetadata.name,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the SMS details of the given invoice.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} saleInvoiceId - Sale invoice id.
|
||||
*/
|
||||
public smsDetails = async (
|
||||
tenantId: number,
|
||||
saleInvoiceId: number,
|
||||
invoiceSmsDetailsDTO: ISaleInvoiceSmsDetailsDTO
|
||||
): Promise<ISaleInvoiceSmsDetails> => {
|
||||
const { SaleInvoice } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve the sale invoice or throw not found service error.
|
||||
const saleInvoice = await SaleInvoice.query()
|
||||
.findById(saleInvoiceId)
|
||||
.withGraphFetched('customer');
|
||||
|
||||
// Validates the sale invoice existance.
|
||||
this.validateSaleInvoiceExistance(saleInvoice);
|
||||
|
||||
// Current tenant metadata.
|
||||
const tenantMetadata = await TenantMetadata.query().findOne({ tenantId });
|
||||
|
||||
// Transformes the invoice notification key to sms notification key.
|
||||
const notificationKey = this.transformDTOKeyToNotificationKey(
|
||||
invoiceSmsDetailsDTO.notificationKey
|
||||
);
|
||||
// Formates the given sms message.
|
||||
const smsMessage = this.formattedInvoiceDetailsMessage(
|
||||
tenantId,
|
||||
notificationKey,
|
||||
saleInvoice,
|
||||
tenantMetadata
|
||||
);
|
||||
|
||||
return {
|
||||
customerName: saleInvoice.customer.displayName,
|
||||
customerPhoneNumber: saleInvoice.customer.personalPhone,
|
||||
smsMessage,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Transformes the invoice notification key DTO to notification key.
|
||||
* @param {string} invoiceNotifKey
|
||||
* @returns {SMS_NOTIFICATION_KEY.SALE_INVOICE_DETAILS
|
||||
* | SMS_NOTIFICATION_KEY.SALE_INVOICE_REMINDER}
|
||||
*/
|
||||
private transformDTOKeyToNotificationKey = (
|
||||
invoiceNotifKey: string
|
||||
):
|
||||
| SMS_NOTIFICATION_KEY.SALE_INVOICE_DETAILS
|
||||
| SMS_NOTIFICATION_KEY.SALE_INVOICE_REMINDER => {
|
||||
const invoiceNotifKeyPairs = {
|
||||
details: SMS_NOTIFICATION_KEY.SALE_INVOICE_DETAILS,
|
||||
reminder: SMS_NOTIFICATION_KEY.SALE_INVOICE_REMINDER,
|
||||
};
|
||||
return (
|
||||
invoiceNotifKeyPairs[invoiceNotifKey] ||
|
||||
SMS_NOTIFICATION_KEY.SALE_INVOICE_DETAILS
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates the sale invoice existance.
|
||||
* @param {ISaleInvoice|null} saleInvoice
|
||||
*/
|
||||
private validateSaleInvoiceExistance(saleInvoice: ISaleInvoice | null) {
|
||||
if (!saleInvoice) {
|
||||
throw new ServiceError(ERRORS.SALE_INVOICE_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user