diff --git a/packages/server/src/api/controllers/Sales/SalesInvoices.ts b/packages/server/src/api/controllers/Sales/SalesInvoices.ts index ba900b786..55abcb6c0 100644 --- a/packages/server/src/api/controllers/Sales/SalesInvoices.ts +++ b/packages/server/src/api/controllers/Sales/SalesInvoices.ts @@ -179,10 +179,21 @@ export default class SaleInvoicesController extends BaseController { '/:id/mail', [ ...this.specificSaleInvoiceValidation, - body('subject').isString().optional(), + + body('subject').isString().optional({ nullable: true }), + body('message').isString().optional({ nullable: true }), + body('from').isString().optional(), - body('to').isString().optional(), - body('body').isString().optional(), + + body('to').isArray().exists(), + body('to.*').isString().isEmail().optional(), + + body('cc').isArray().optional({ nullable: true }), + body('cc.*').isString().isEmail().optional(), + + body('bcc').isArray().optional({ nullable: true }), + body('bcc.*').isString().isEmail().optional(), + body('attach_invoice').optional().isBoolean().toBoolean(), ], this.validationResult, diff --git a/packages/server/src/constants/event-tracker.ts b/packages/server/src/constants/event-tracker.ts index 26e038e4e..6ab2375a4 100644 --- a/packages/server/src/constants/event-tracker.ts +++ b/packages/server/src/constants/event-tracker.ts @@ -4,6 +4,9 @@ export const SALE_INVOICE_DELETED = 'Sale invoice deleted'; export const SALE_INVOICE_MAIL_DELIVERED = 'Sale invoice mail delivered'; export const SALE_INVOICE_VIEWED = 'Sale invoice viewed'; export const SALE_INVOICE_PDF_VIEWED = 'Sale invoice PDF viewed'; +export const SALE_INVOICE_MAIL_SENT = 'Sale invoice mail sent'; +export const SALE_INVOICE_MAIL_REMINDER_SENT = + 'Sale invoice reminder mail sent'; export const SALE_ESTIMATE_CREATED = 'Sale estimate created'; export const SALE_ESTIMATE_EDITED = 'Sale estimate edited'; diff --git a/packages/server/src/interfaces/Mailable.ts b/packages/server/src/interfaces/Mailable.ts index 5682f2529..7412f2079 100644 --- a/packages/server/src/interfaces/Mailable.ts +++ b/packages/server/src/interfaces/Mailable.ts @@ -30,18 +30,14 @@ export interface AddressItem { } export interface CommonMailOptions { - toAddresses: AddressItem[]; - fromAddresses: AddressItem[]; - from: string; - to: string | string[]; + from: Array; subject: string; - body: string; + message: string; + to: Array; + cc?: Array; + bcc?: Array; data?: Record; } -export interface CommonMailOptionsDTO { - to?: string | string[]; - from?: string; - subject?: string; - body?: string; +export interface CommonMailOptionsDTO extends Partial { } diff --git a/packages/server/src/interfaces/SaleInvoice.ts b/packages/server/src/interfaces/SaleInvoice.ts index 59404b549..cb77f29fe 100644 --- a/packages/server/src/interfaces/SaleInvoice.ts +++ b/packages/server/src/interfaces/SaleInvoice.ts @@ -234,7 +234,7 @@ export enum SaleInvoiceAction { } export interface SaleInvoiceMailOptions extends CommonMailOptions { - attachInvoice: boolean; + attachInvoice?: boolean; } export interface SendInvoiceMailDTO extends CommonMailOptionsDTO { @@ -251,6 +251,7 @@ export interface ISaleInvoiceMailSend { tenantId: number; saleInvoiceId: number; messageOptions: SendInvoiceMailDTO; + formattedMessageOptions: SaleInvoiceMailOptions; } export interface ISaleInvoiceMailSent { diff --git a/packages/server/src/services/EventsTracker/events/SaleInvoicesEventsTracker.ts b/packages/server/src/services/EventsTracker/events/SaleInvoicesEventsTracker.ts index 9f3b820d1..ca05a2478 100644 --- a/packages/server/src/services/EventsTracker/events/SaleInvoicesEventsTracker.ts +++ b/packages/server/src/services/EventsTracker/events/SaleInvoicesEventsTracker.ts @@ -10,6 +10,7 @@ import { SALE_INVOICE_CREATED, SALE_INVOICE_DELETED, SALE_INVOICE_EDITED, + SALE_INVOICE_MAIL_SENT, SALE_INVOICE_PDF_VIEWED, SALE_INVOICE_VIEWED, } from '@/constants/event-tracker'; @@ -43,6 +44,10 @@ export class SaleInvoiceEventsTracker extends EventSubscriber { events.saleInvoice.onPdfViewed, this.handleTrackPdfViewedInvoiceEvent ); + bus.subscribe( + events.saleInvoice.onMailSent, + this.handleTrackMailSentInvoiceEvent + ); } private handleTrackInvoiceCreatedEvent = ({ @@ -90,4 +95,12 @@ export class SaleInvoiceEventsTracker extends EventSubscriber { properties: {}, }); }; + + private handleTrackMailSentInvoiceEvent = ({ tenantId }) => { + this.posthog.trackEvent({ + distinctId: `tenant-${tenantId}`, + event: SALE_INVOICE_MAIL_SENT, + properties: {}, + }); + }; } diff --git a/packages/server/src/services/MailNotification/ContactMailNotification.ts b/packages/server/src/services/MailNotification/ContactMailNotification.ts index e1e733a79..f5214794b 100644 --- a/packages/server/src/services/MailNotification/ContactMailNotification.ts +++ b/packages/server/src/services/MailNotification/ContactMailNotification.ts @@ -4,6 +4,7 @@ import HasTenancyService from '@/services/Tenancy/TenancyService'; import { MailTenancy } from '@/services/MailTenancy/MailTenancy'; import { formatSmsMessage } from '@/utils'; import { Tenant } from '@/system/models'; +import { castArray } from 'lodash'; @Service() export class ContactMailNotification { @@ -14,76 +15,54 @@ export class ContactMailNotification { private tenancy: HasTenancyService; /** - * Parses the default message options. - * @param {number} tenantId - - * @param {number} invoiceId - - * @param {string} subject - - * @param {string} body - - * @returns {Promise} + * Gets the default mail address of the given contact. + * @param {number} tenantId - Tenant id. + * @param {number} invoiceId - Contact id. + * @returns {Promise>} */ public async getDefaultMailOptions( tenantId: number, - contactId: number, - subject: string = '', - body: string = '' - ): Promise { + customerId: number + ): Promise> { const { Customer } = this.tenancy.models(tenantId); - const contact = await Customer.query() - .findById(contactId) + const customer = await Customer.query() + .findById(customerId) .throwIfNotFound(); - const toAddresses = contact.contactAddresses; + const toAddresses = customer.contactAddresses; const fromAddresses = await this.mailTenancy.senders(tenantId); const toAddress = toAddresses.find((a) => a.primary); const fromAddress = fromAddresses.find((a) => a.primary); - const to = toAddress?.mail || ''; - const from = fromAddress?.mail || ''; + const to = toAddress?.mail ? castArray(toAddress?.mail) : []; + const from = fromAddress?.mail ? castArray(fromAddress?.mail) : []; - return { - subject, - body, - to, - from, - fromAddresses, - toAddresses, - }; + return { to, from }; } /** * 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} */ - public async getMailOptions( + public async parseMailOptions( tenantId: number, - contactId: number, - defaultSubject?: string, - defaultBody?: string, - formatterData?: Record + mailOptions: CommonMailOptions, + formatterArgs?: Record ): Promise { - const mailOpts = await this.getDefaultMailOptions( - tenantId, - contactId, - defaultSubject, - defaultBody - ); const commonFormatArgs = await this.getCommonFormatArgs(tenantId); const formatArgs = { ...commonFormatArgs, - ...formatterData, + ...formatterArgs, }; - const subject = formatSmsMessage(mailOpts.subject, formatArgs); - const body = formatSmsMessage(mailOpts.body, formatArgs); + const subjectFormatted = formatSmsMessage(mailOptions?.subject, formatArgs); + const messageFormatted = formatSmsMessage(mailOptions?.message, formatArgs); return { - ...mailOpts, - subject, - body, + ...mailOptions, + subject: subjectFormatted, + message: messageFormatted, }; } diff --git a/packages/server/src/services/MailNotification/utils.ts b/packages/server/src/services/MailNotification/utils.ts index b9e37b297..115a01313 100644 --- a/packages/server/src/services/MailNotification/utils.ts +++ b/packages/server/src/services/MailNotification/utils.ts @@ -1,33 +1,46 @@ -import { isEmpty } from 'lodash'; +import { castArray, isEmpty } from 'lodash'; import { ServiceError } from '@/exceptions'; -import { CommonMailOptions, CommonMailOptionsDTO } from '@/interfaces'; +import { CommonMailOptions } from '@/interfaces'; import { ERRORS } from './constants'; /** * Merges the mail options with incoming options. * @param {Partial} mailOptions * @param {Partial} overridedOptions - * @throws {ServiceError} */ -export function parseAndValidateMailOptions( - mailOptions: Partial, - overridedOptions: Partial -) { +export function parseMailOptions( + mailOptions: CommonMailOptions, + overridedOptions: Partial +): CommonMailOptions { const mergedMessageOptions = { ...mailOptions, ...overridedOptions, }; - if (isEmpty(mergedMessageOptions.from)) { + const parsedMessageOptions = { + ...mergedMessageOptions, + from: mergedMessageOptions?.from + ? castArray(mergedMessageOptions?.from) + : [], + to: mergedMessageOptions?.to ? castArray(mergedMessageOptions?.to) : [], + cc: mergedMessageOptions?.cc ? castArray(mergedMessageOptions?.cc) : [], + bcc: mergedMessageOptions?.bcc ? castArray(mergedMessageOptions?.bcc) : [], + }; + return parsedMessageOptions; +} + +export function validateRequiredMailOptions( + mailOptions: Partial +) { + if (isEmpty(mailOptions.from)) { throw new ServiceError(ERRORS.MAIL_FROM_NOT_FOUND); } - if (isEmpty(mergedMessageOptions.to)) { + if (isEmpty(mailOptions.to)) { throw new ServiceError(ERRORS.MAIL_TO_NOT_FOUND); } - if (isEmpty(mergedMessageOptions.subject)) { + if (isEmpty(mailOptions.subject)) { throw new ServiceError(ERRORS.MAIL_SUBJECT_NOT_FOUND); } - if (isEmpty(mergedMessageOptions.body)) { + if (isEmpty(mailOptions.message)) { throw new ServiceError(ERRORS.MAIL_BODY_NOT_FOUND); } - return mergedMessageOptions; } diff --git a/packages/server/src/services/Sales/Invoices/GetInvoicePaymentMail.ts b/packages/server/src/services/Sales/Invoices/GetInvoicePaymentMail.ts new file mode 100644 index 000000000..4a41b3f26 --- /dev/null +++ b/packages/server/src/services/Sales/Invoices/GetInvoicePaymentMail.ts @@ -0,0 +1,66 @@ +import { Inject, Service } from 'typedi'; +import { GetPdfTemplate } from '@/services/PdfTemplate/GetPdfTemplate'; +import { GetSaleInvoice } from './GetSaleInvoice'; +import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable'; +import { + InvoicePaymentEmailProps, + renderInvoicePaymentEmail, +} from '@bigcapital/email-components'; +import { GetInvoiceMailTemplateAttributesTransformer } from './GetInvoicePaymentMailAttributesTransformer'; + +@Service() +export class GetInvoicePaymentMail { + @Inject() + private getSaleInvoiceService: GetSaleInvoice; + + @Inject() + private getBrandingTemplate: GetPdfTemplate; + + @Inject() + private transformer: TransformerInjectable; + + /** + * Retrieves the mail template attributes of the given invoice. + * @param {number} tenantId - Tenant id. + * @param {number} invoiceId - Invoice id. + */ + public async getMailTemplateAttributes(tenantId: number, invoiceId: number) { + const invoice = await this.getSaleInvoiceService.getSaleInvoice( + tenantId, + invoiceId + ); + const brandingTemplate = await this.getBrandingTemplate.getPdfTemplate( + tenantId, + invoice.pdfTemplateId + ); + const mailTemplateAttributes = await this.transformer.transform( + tenantId, + invoice, + new GetInvoiceMailTemplateAttributesTransformer(), + { + invoice, + brandingTemplate, + } + ); + return mailTemplateAttributes; + } + + /** + * Retrieves the mail template html content. + * @param {number} tenantId - Tenant id. + * @param {number} invoiceId - Invoice id. + */ + public async getMailTemplate( + tenantId: number, + invoiceId: number, + overrideAttributes?: Partial + ): Promise { + const attributes = await this.getMailTemplateAttributes( + tenantId, + invoiceId + ); + const mergedAttributes = { ...attributes, ...overrideAttributes }; + + return renderInvoicePaymentEmail(mergedAttributes); + } +} diff --git a/packages/server/src/services/Sales/Invoices/GetInvoicePaymentMailAttributesTransformer.ts b/packages/server/src/services/Sales/Invoices/GetInvoicePaymentMailAttributesTransformer.ts new file mode 100644 index 000000000..976b5053f --- /dev/null +++ b/packages/server/src/services/Sales/Invoices/GetInvoicePaymentMailAttributesTransformer.ts @@ -0,0 +1,136 @@ +import { Transformer } from '@/lib/Transformer/Transformer'; + +export class GetInvoiceMailTemplateAttributesTransformer extends Transformer { + /** + * Include these attributes to item entry object. + * @returns {Array} + */ + public includeAttributes = (): string[] => { + return [ + 'companyLogoUri', + 'companyName', + + 'invoiceAmount', + + 'primaryColor', + + 'invoiceAmount', + 'invoiceMessage', + + 'dueDate', + 'dueDateLabel', + + 'invoiceNumber', + 'invoiceNumberLabel', + + 'total', + 'totalLabel', + + 'dueAmount', + 'dueAmountLabel', + + 'viewInvoiceButtonLabel', + 'viewInvoiceButtonUrl', + + 'items', + ]; + }; + + public excludeAttributes = (): string[] => { + return ['*']; + }; + + public companyLogoUri(): string { + return this.options.brandingTemplate?.attributes?.companyLogoUri; + } + + public companyName(): string { + return this.context.organization.name; + } + + public invoiceAmount(): string { + return this.options.invoice.totalFormatted; + } + + public primaryColor(): string { + return this.options?.brandingTemplate?.attributes?.primaryColor; + } + + public invoiceMessage(): string { + return ''; + } + + public dueDate(): string { + return this.options?.invoice?.dueDateFormatted; + } + + public dueDateLabel(): string { + return 'Due {dueDate}'; + } + + public invoiceNumber(): string { + return this.options?.invoice?.invoiceNo; + } + + public invoiceNumberLabel(): string { + return 'Invoice # {invoiceNumber}'; + } + + public total(): string { + return this.options.invoice?.totalFormatted; + } + + public totalLabel(): string { + return 'Total'; + } + + public dueAmount(): string { + return this.options?.invoice.dueAmountFormatted; + } + + public dueAmountLabel(): string { + return 'Due Amount'; + } + + public viewInvoiceButtonLabel(): string { + return 'View Invoice'; + } + + public viewInvoiceButtonUrl(): string { + return ''; + } + + public items(): Array { + return this.item( + this.options.invoice?.entries, + new GetInvoiceMailTemplateItemAttrsTransformer() + ); + } +} + +class GetInvoiceMailTemplateItemAttrsTransformer extends Transformer { + /** + * Include these attributes to item entry object. + * @returns {Array} + */ + public includeAttributes = (): string[] => { + return ['quantity', 'label', 'rate']; + }; + + public excludeAttributes = (): string[] => { + return ['*']; + }; + + public quantity(entry): string { + return entry?.quantity; + } + + public label(entry): string { + console.log(entry); + return entry?.item?.name; + } + + public rate(entry): string { + return entry?.rateFormatted; + } +} diff --git a/packages/server/src/services/Sales/Invoices/SendInvoiceInvoiceMailCommon.ts b/packages/server/src/services/Sales/Invoices/SendInvoiceInvoiceMailCommon.ts index 52ef46a59..b7b863c27 100644 --- a/packages/server/src/services/Sales/Invoices/SendInvoiceInvoiceMailCommon.ts +++ b/packages/server/src/services/Sales/Invoices/SendInvoiceInvoiceMailCommon.ts @@ -7,6 +7,7 @@ import { DEFAULT_INVOICE_MAIL_CONTENT, DEFAULT_INVOICE_MAIL_SUBJECT, } from './constants'; +import { GetInvoicePaymentMail } from './GetInvoicePaymentMail'; @Service() export class SendSaleInvoiceMailCommon { @@ -19,6 +20,9 @@ export class SendSaleInvoiceMailCommon { @Inject() private contactMailNotification: ContactMailNotification; + @Inject() + private getInvoicePaymentMail: GetInvoicePaymentMail; + /** * Retrieves the mail options. * @param {number} tenantId - Tenant id. @@ -27,11 +31,11 @@ export class SendSaleInvoiceMailCommon { * @param {string} defaultBody - Subject body. * @returns {Promise} */ - public async getMailOption( + public async getInvoiceMailOptions( tenantId: number, invoiceId: number, defaultSubject: string = DEFAULT_INVOICE_MAIL_SUBJECT, - defaultBody: string = DEFAULT_INVOICE_MAIL_CONTENT + defaultMessage: string = DEFAULT_INVOICE_MAIL_CONTENT ): Promise { const { SaleInvoice } = this.tenancy.models(tenantId); @@ -39,21 +43,51 @@ export class SendSaleInvoiceMailCommon { .findById(invoiceId) .throwIfNotFound(); - const formatterData = await this.formatText(tenantId, invoiceId); - - const mailOptions = await this.contactMailNotification.getMailOptions( - tenantId, - saleInvoice.customerId, - defaultSubject, - defaultBody, - formatterData - ); + const contactMailDefaultOptions = + await this.contactMailNotification.getDefaultMailOptions( + tenantId, + saleInvoice.customerId + ); return { - ...mailOptions, + ...contactMailDefaultOptions, attachInvoice: true, + subject: defaultSubject, + message: defaultMessage, }; } + /** + * Formats the given invoice mail options. + * @param {number} tenantId + * @param {number} invoiceId + * @param {SaleInvoiceMailOptions} mailOptions + * @returns {Promise} + */ + public async formatInvoiceMailOptions( + tenantId: number, + invoiceId: number, + mailOptions: SaleInvoiceMailOptions + ): Promise { + const formatterArgs = await this.getInvoiceFormatterArgs( + tenantId, + invoiceId + ); + const parsedOptions = await this.contactMailNotification.parseMailOptions( + tenantId, + mailOptions, + formatterArgs + ); + const message = await this.getInvoicePaymentMail.getMailTemplate( + tenantId, + invoiceId, + { + // # Invoice message + invoiceMessage: parsedOptions.message, + } + ); + return { ...parsedOptions, message }; + } + /** * Retrieves the formatted text of the given sale invoice. * @param {number} tenantId - Tenant id. @@ -61,7 +95,7 @@ export class SendSaleInvoiceMailCommon { * @param {string} text - The given text. * @returns {Promise} */ - public formatText = async ( + public getInvoiceFormatterArgs = async ( tenantId: number, invoiceId: number ): Promise> => { @@ -69,7 +103,6 @@ export class SendSaleInvoiceMailCommon { tenantId, invoiceId ); - return { CustomerName: invoice.customer.displayName, InvoiceNumber: invoice.invoiceNo, diff --git a/packages/server/src/services/Sales/Invoices/SendSaleInvoiceMail.ts b/packages/server/src/services/Sales/Invoices/SendSaleInvoiceMail.ts index eff8b2603..86b664229 100644 --- a/packages/server/src/services/Sales/Invoices/SendSaleInvoiceMail.ts +++ b/packages/server/src/services/Sales/Invoices/SendSaleInvoiceMail.ts @@ -1,15 +1,23 @@ 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 { DEFAULT_INVOICE_MAIL_CONTENT, DEFAULT_INVOICE_MAIL_SUBJECT, } from './constants'; -import { parseAndValidateMailOptions } from '@/services/MailNotification/utils'; +import { + parseMailOptions, + validateRequiredMailOptions, +} from '@/services/MailNotification/utils'; import events from '@/subscribers/events'; import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; +import { ParsedNumberSearch } from 'libphonenumber-js'; @Service() export class SendSaleInvoiceMail { @@ -57,12 +65,17 @@ export class SendSaleInvoiceMail { * @param {number} saleInvoiceId * @returns {Promise} */ - public async getMailOption(tenantId: number, saleInvoiceId: number) { - return this.invoiceMail.getMailOption( + public async getMailOption( + tenantId: number, + saleInvoiceId: number, + defaultSubject: string = DEFAULT_INVOICE_MAIL_SUBJECT, + defaultMessage: string = DEFAULT_INVOICE_MAIL_CONTENT + ): Promise { + return this.invoiceMail.getInvoiceMailOptions( tenantId, saleInvoiceId, - DEFAULT_INVOICE_MAIL_SUBJECT, - DEFAULT_INVOICE_MAIL_CONTENT + defaultSubject, + defaultMessage ); } @@ -78,44 +91,58 @@ export class SendSaleInvoiceMail { saleInvoiceId: number, messageOptions: SendInvoiceMailDTO ) { - const defaultMessageOpts = await this.getMailOption( + const defaultMessageOptions = await this.getMailOption( tenantId, saleInvoiceId ); - // Merge message opts with default options and validate the incoming options. - const messageOpts = parseAndValidateMailOptions( - defaultMessageOpts, + // Merges message options with default options and parses the options values. + const parsedMessageOptions = parseMailOptions( + defaultMessageOptions, messageOptions ); - const mail = new Mail() - .setSubject(messageOpts.subject) - .setTo(messageOpts.to) - .setContent(messageOpts.body); + // Validates the required mail options. + validateRequiredMailOptions(parsedMessageOptions); - if (messageOpts.attachInvoice) { - // Retrieves document buffer of the invoice pdf document. - const invoicePdfBuffer = await this.invoicePdf.saleInvoicePdf( + const formattedMessageOptions = + await this.invoiceMail.formatInvoiceMailOptions( tenantId, - saleInvoiceId + saleInvoiceId, + parsedMessageOptions ); + const mail = new Mail() + .setSubject(formattedMessageOptions.subject) + .setTo(formattedMessageOptions.to) + .setContent(formattedMessageOptions.message); + + // Attach invoice document. + if (formattedMessageOptions.attachInvoice) { + // Retrieves document buffer of the invoice pdf document. + const [invoicePdfBuffer, invoiceFilename] = + await this.invoicePdf.saleInvoicePdf(tenantId, saleInvoiceId); + mail.setAttachments([ - { filename: 'invoice.pdf', content: invoicePdfBuffer }, + { filename: `${invoiceFilename}.pdf`, content: invoicePdfBuffer }, ]); } - // Triggers the event `onSaleInvoiceSend`. - await this.eventPublisher.emitAsync(events.saleInvoice.onMailSend, { + + const eventPayload = { tenantId, saleInvoiceId, messageOptions, - } as ISaleInvoiceMailSend); + formattedMessageOptions, + } as ISaleInvoiceMailSend; + // Triggers the event `onSaleInvoiceSend`. + await this.eventPublisher.emitAsync( + events.saleInvoice.onMailSend, + eventPayload + ); await mail.send(); // Triggers the event `onSaleInvoiceSend`. - await this.eventPublisher.emitAsync(events.saleInvoice.onMailSent, { - tenantId, - saleInvoiceId, - messageOptions, - } as ISaleInvoiceMailSend); + await this.eventPublisher.emitAsync( + events.saleInvoice.onMailSent, + eventPayload + ); } } diff --git a/packages/server/src/utils/index.ts b/packages/server/src/utils/index.ts index 9edf85b44..aaf236eb5 100644 --- a/packages/server/src/utils/index.ts +++ b/packages/server/src/utils/index.ts @@ -406,7 +406,7 @@ export const runningAmount = (amount: number) => { }; }; -export const formatSmsMessage = (message, args) => { +export const formatSmsMessage = (message: string, args) => { let formattedMessage = message; Object.keys(args).forEach((key) => { diff --git a/packages/webapp/src/containers/Preferences/PaymentMethods/drawers/StripeIntegrationEditBoot.tsx b/packages/webapp/src/containers/Preferences/PaymentMethods/drawers/StripeIntegrationEditBoot.tsx index 524c7c37e..2c1d2ff94 100644 --- a/packages/webapp/src/containers/Preferences/PaymentMethods/drawers/StripeIntegrationEditBoot.tsx +++ b/packages/webapp/src/containers/Preferences/PaymentMethods/drawers/StripeIntegrationEditBoot.tsx @@ -29,7 +29,13 @@ export const useStripeIntegrationEditBoot = () => { return context; }; -export const StripeIntegrationEditBoot: React.FC = ({ children }) => { +interface StripeIntegrationEditBootProps { + children: React.ReactNode; +} + +export const StripeIntegrationEditBoot: React.FC< + StripeIntegrationEditBootProps +> = ({ children }) => { const { payload: { stripePaymentMethodId }, } = useDrawerContext(); diff --git a/packages/webapp/src/hooks/query/invoices.tsx b/packages/webapp/src/hooks/query/invoices.tsx index 7887aecc2..8cf123d6b 100644 --- a/packages/webapp/src/hooks/query/invoices.tsx +++ b/packages/webapp/src/hooks/query/invoices.tsx @@ -357,7 +357,6 @@ export function useSendSaleInvoiceMail( (value) => apiRequest.post(`sales/invoices/${value.id}/mail`, value.values), { onSuccess: (res) => { - // Common invalidate queries. commonInvalidateQueries(queryClient); }, ...options, diff --git a/shared/email-components/package.json b/shared/email-components/package.json index e156bb5df..d206dd876 100644 --- a/shared/email-components/package.json +++ b/shared/email-components/package.json @@ -2,7 +2,6 @@ "name": "@bigcapital/email-components", "private": true, "version": "0.0.0", - "type": "module", "scripts": { "build": "tsc && vite build", "lint": "eslint .",