diff --git a/packages/server/src/interfaces/PaymentReceive.ts b/packages/server/src/interfaces/PaymentReceive.ts index 2926d923c..329d0d944 100644 --- a/packages/server/src/interfaces/PaymentReceive.ts +++ b/packages/server/src/interfaces/PaymentReceive.ts @@ -173,3 +173,9 @@ export type IPaymentReceiveGLCommonEntry = Pick< export interface PaymentReceiveMailOpts extends CommonMailOptions {} export interface PaymentReceiveMailOptsDTO extends CommonMailOptionsDTO {} + +export interface PaymentReceiveMailPresendEvent { + tenantId: number; + paymentReceiveId: number; + messageOptions: PaymentReceiveMailOptsDTO; +} diff --git a/packages/server/src/interfaces/SaleEstimate.ts b/packages/server/src/interfaces/SaleEstimate.ts index 171c8a0d1..9ac17295c 100644 --- a/packages/server/src/interfaces/SaleEstimate.ts +++ b/packages/server/src/interfaces/SaleEstimate.ts @@ -132,4 +132,10 @@ export interface SaleEstimateMailOptions extends CommonMailOptions { export interface SaleEstimateMailOptionsDTO extends CommonMailOptionsDTO { attachEstimate?: boolean; -} \ No newline at end of file +} + +export interface ISaleEstimateMailPresendEvent { + tenantId: number; + saleEstimateId: number; + messageOptions: SaleEstimateMailOptionsDTO; +} diff --git a/packages/server/src/interfaces/SaleInvoice.ts b/packages/server/src/interfaces/SaleInvoice.ts index 394319e86..61594306a 100644 --- a/packages/server/src/interfaces/SaleInvoice.ts +++ b/packages/server/src/interfaces/SaleInvoice.ts @@ -201,3 +201,15 @@ export interface ISaleInvoiceNotifyPayload { saleInvoiceId: number; messageDTO: SendInvoiceMailDTO; } + +export interface ISaleInvoiceMailSend { + tenantId: number; + saleInvoiceId: number; + messageOptions: SendInvoiceMailDTO; +} + +export interface ISaleInvoiceMailSent { + tenantId: number; + saleInvoiceId: number; + messageOptions: SendInvoiceMailDTO; +} diff --git a/packages/server/src/interfaces/SaleReceipt.ts b/packages/server/src/interfaces/SaleReceipt.ts index 1e8ffa98e..8904767c6 100644 --- a/packages/server/src/interfaces/SaleReceipt.ts +++ b/packages/server/src/interfaces/SaleReceipt.ts @@ -143,3 +143,9 @@ export interface SaleReceiptMailOpts extends CommonMailOptions { export interface SaleReceiptMailOptsDTO extends CommonMailOptionsDTO { attachReceipt?: boolean; } + +export interface ISaleReceiptMailPresend { + tenantId: number; + saleReceiptId: number; + messageOptions: SaleReceiptMailOptsDTO; +} diff --git a/packages/server/src/loaders/eventEmitter.ts b/packages/server/src/loaders/eventEmitter.ts index fa1942b72..5d3269646 100644 --- a/packages/server/src/loaders/eventEmitter.ts +++ b/packages/server/src/loaders/eventEmitter.ts @@ -84,6 +84,9 @@ import { WriteInvoiceTaxTransactionsSubscriber } from '@/services/TaxRates/subsc import { BillTaxRateValidateSubscriber } from '@/services/TaxRates/subscribers/BillTaxRateValidateSubscriber'; import { WriteBillTaxTransactionsSubscriber } from '@/services/TaxRates/subscribers/WriteBillTaxTransactionsSubscriber'; import { SyncItemTaxRateOnEditTaxSubscriber } from '@/services/TaxRates/SyncItemTaxRateOnEditTaxSubscriber'; +import { InvoiceChangeStatusOnMailSentSubscriber } from '@/services/Sales/Invoices/subscribers/InvoiceChangeStatusOnMailSentSubscriber'; +import { SaleReceiptMarkClosedOnMailSentSubcriber } from '@/services/Sales/Receipts/subscribers/SaleReceiptMarkClosedOnMailSentSubcriber'; +import { SaleEstimateMarkApprovedOnMailSent } from '@/services/Sales/Estimates/subscribers/SaleEstimateMarkApprovedOnMailSent'; export default () => { return new EventPublisher(); @@ -104,8 +107,12 @@ export const susbcribers = () => { InventorySubscriber, CustomerWriteGLOpeningBalanceSubscriber, VendorsWriteGLOpeningSubscriber, + + // # Estimate SaleEstimateAutoSerialSubscriber, SaleEstimateSmsNotificationSubscriber, + SaleEstimateMarkApprovedOnMailSent, + ExpensesWriteGLSubscriber, SaleReceiptAutoSerialSubscriber, SaleInvoiceAutoIncrementSubscriber, @@ -152,11 +159,13 @@ export const susbcribers = () => { // #Invoices InvoicePaymentGLRewriteSubscriber, InvoiceCostGLEntriesSubscriber, + InvoiceChangeStatusOnMailSentSubscriber, BillPaymentsGLEntriesRewriteSubscriber, // # Receipts SaleReceiptCostGLEntriesSubscriber, + SaleReceiptMarkClosedOnMailSentSubcriber, // Transaction locking. SalesTransactionLockingGuardSubscriber, @@ -199,6 +208,6 @@ export const susbcribers = () => { BillTaxRateValidateSubscriber, WriteBillTaxTransactionsSubscriber, - SyncItemTaxRateOnEditTaxSubscriber + SyncItemTaxRateOnEditTaxSubscriber, ]; }; diff --git a/packages/server/src/services/Accounts/AccountTransactionTransformer.ts b/packages/server/src/services/Accounts/AccountTransactionTransformer.ts index 857fe5ccc..d0e3e487f 100644 --- a/packages/server/src/services/Accounts/AccountTransactionTransformer.ts +++ b/packages/server/src/services/Accounts/AccountTransactionTransformer.ts @@ -1,6 +1,5 @@ import { IAccountTransaction } from '@/interfaces'; import { Transformer } from '@/lib/Transformer/Transformer'; -import { transaction } from 'objection'; export default class AccountTransactionTransformer extends Transformer { /** diff --git a/packages/server/src/services/Accounts/AccountTransform.ts b/packages/server/src/services/Accounts/AccountTransform.ts index 9297994be..98e19553e 100644 --- a/packages/server/src/services/Accounts/AccountTransform.ts +++ b/packages/server/src/services/Accounts/AccountTransform.ts @@ -34,7 +34,7 @@ export class AccountTransformer extends Transformer { /** * Retrieve formatted account amount. - * @param {IAccount} invoice + * @param {IAccount} invoice * @returns {string} */ protected formattedAmount = (account: IAccount): string => { diff --git a/packages/server/src/services/Accounts/ActivateAccount.ts b/packages/server/src/services/Accounts/ActivateAccount.ts index 1fcd104f6..26afd836d 100644 --- a/packages/server/src/services/Accounts/ActivateAccount.ts +++ b/packages/server/src/services/Accounts/ActivateAccount.ts @@ -5,7 +5,6 @@ import { IAccountEventActivatedPayload } from '@/interfaces'; import events from '@/subscribers/events'; import UnitOfWork from '@/services/UnitOfWork'; import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; -import { CommandAccountValidators } from './CommandAccountValidators'; @Service() export class ActivateAccount { @@ -18,9 +17,6 @@ export class ActivateAccount { @Inject() private uow: UnitOfWork; - @Inject() - private validator: CommandAccountValidators; - /** * Activates/Inactivates the given account. * @param {number} tenantId diff --git a/packages/server/src/services/Sales/Estimates/SendSaleEstimateMail.ts b/packages/server/src/services/Sales/Estimates/SendSaleEstimateMail.ts index 258496306..0ac580b90 100644 --- a/packages/server/src/services/Sales/Estimates/SendSaleEstimateMail.ts +++ b/packages/server/src/services/Sales/Estimates/SendSaleEstimateMail.ts @@ -8,11 +8,14 @@ import { import { SaleEstimatesPdf } from './SaleEstimatesPdf'; import { GetSaleEstimate } from './GetSaleEstimate'; import { + ISaleEstimateMailPresendEvent, SaleEstimateMailOptions, SaleEstimateMailOptionsDTO, } from '@/interfaces'; import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification'; import { parseAndValidateMailOptions } from '@/services/MailNotification/utils'; +import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; +import events from '@/subscribers/events'; @Service() export class SendSaleEstimateMail { @@ -31,6 +34,9 @@ export class SendSaleEstimateMail { @Inject('agenda') private agenda: any; + @Inject() + private eventPublisher: EventPublisher; + /** * Triggers the reminder mail of the given sale estimate. * @param {number} tenantId - @@ -49,6 +55,13 @@ export class SendSaleEstimateMail { messageOptions, }; await this.agenda.now('sale-estimate-mail-send', payload); + + // Triggers `onSaleEstimatePreMailSend` event. + await this.eventPublisher.emitAsync(events.saleEstimate.onPreMailSend, { + tenantId, + saleEstimateId, + messageOptions, + } as ISaleEstimateMailPresendEvent); } /** @@ -99,7 +112,7 @@ export class SendSaleEstimateMail { return { ...mailOptions, data: formatterData, - attachEstimate: true + attachEstimate: true, }; }; diff --git a/packages/server/src/services/Sales/Estimates/subscribers/SaleEstimateMarkApprovedOnMailSent.ts b/packages/server/src/services/Sales/Estimates/subscribers/SaleEstimateMarkApprovedOnMailSent.ts new file mode 100644 index 000000000..99caa3952 --- /dev/null +++ b/packages/server/src/services/Sales/Estimates/subscribers/SaleEstimateMarkApprovedOnMailSent.ts @@ -0,0 +1,43 @@ +import { Inject, Service } from 'typedi'; +import events from '@/subscribers/events'; +import { ISaleEstimateMailPresendEvent } from '@/interfaces'; +import { DeliverSaleEstimate } from '../DeliverSaleEstimate'; +import { ServiceError } from '@/exceptions'; +import { ERRORS } from '../constants'; + +@Service() +export class SaleEstimateMarkApprovedOnMailSent { + @Inject() + private deliverEstimateService: DeliverSaleEstimate; + + /** + * Attaches events. + */ + public attach(bus) { + bus.subscribe(events.saleEstimate.onPreMailSend, this.markEstimateApproved); + } + + /** + * Marks the given estimate approved on submitting mail. + * @param {ISaleEstimateMailPresendEvent} + */ + private markEstimateApproved = async ({ + tenantId, + saleEstimateId, + }: ISaleEstimateMailPresendEvent) => { + try { + await this.deliverEstimateService.deliverSaleEstimate( + tenantId, + saleEstimateId + ); + } catch (error) { + if ( + error instanceof ServiceError && + error.errorType === ERRORS.SALE_ESTIMATE_ALREADY_DELIVERED + ) { + } else { + throw error; + } + } + }; +} diff --git a/packages/server/src/services/Sales/Invoices/DeliverSaleInvoice.ts b/packages/server/src/services/Sales/Invoices/DeliverSaleInvoice.ts index 30ba635a6..f5500a03a 100644 --- a/packages/server/src/services/Sales/Invoices/DeliverSaleInvoice.ts +++ b/packages/server/src/services/Sales/Invoices/DeliverSaleInvoice.ts @@ -4,7 +4,6 @@ import { ServiceError } from '@/exceptions'; import { ISaleInvoiceDeliveringPayload, ISaleInvoiceEventDeliveredPayload, - ISystemUser, } from '@/interfaces'; import { ERRORS } from './constants'; import { Inject, Service } from 'typedi'; @@ -36,8 +35,7 @@ export class DeliverSaleInvoice { */ public async deliverSaleInvoice( tenantId: number, - saleInvoiceId: number, - authorizedUser: ISystemUser + saleInvoiceId: number ): Promise { const { SaleInvoice } = this.tenancy.models(tenantId); diff --git a/packages/server/src/services/Sales/Invoices/SendSaleInvoiceMail.ts b/packages/server/src/services/Sales/Invoices/SendSaleInvoiceMail.ts index 05db4f73e..eff8b2603 100644 --- a/packages/server/src/services/Sales/Invoices/SendSaleInvoiceMail.ts +++ b/packages/server/src/services/Sales/Invoices/SendSaleInvoiceMail.ts @@ -1,6 +1,6 @@ import { Inject, Service } from 'typedi'; import Mail from '@/lib/Mail'; -import { SendInvoiceMailDTO } from '@/interfaces'; +import { ISaleInvoiceMailSend, SendInvoiceMailDTO } from '@/interfaces'; import { SaleInvoicePdf } from './SaleInvoicePdf'; import { SendSaleInvoiceMailCommon } from './SendInvoiceInvoiceMailCommon'; import { @@ -8,6 +8,8 @@ import { DEFAULT_INVOICE_MAIL_SUBJECT, } from './constants'; import { parseAndValidateMailOptions } from '@/services/MailNotification/utils'; +import events from '@/subscribers/events'; +import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; @Service() export class SendSaleInvoiceMail { @@ -20,6 +22,9 @@ export class SendSaleInvoiceMail { @Inject('agenda') private agenda: any; + @Inject() + private eventPublisher: EventPublisher; + /** * Sends the invoice mail of the given sale invoice. * @param {number} tenantId @@ -29,14 +34,21 @@ export class SendSaleInvoiceMail { public async triggerMail( tenantId: number, saleInvoiceId: number, - messageDTO: SendInvoiceMailDTO + messageOptions: SendInvoiceMailDTO ) { const payload = { tenantId, saleInvoiceId, - messageDTO, + messageOptions, }; await this.agenda.now('sale-invoice-mail-send', payload); + + // Triggers the event `onSaleInvoicePreMailSend`. + await this.eventPublisher.emitAsync(events.saleInvoice.onPreMailSend, { + tenantId, + saleInvoiceId, + messageOptions, + } as ISaleInvoiceMailSend); } /** @@ -64,7 +76,7 @@ export class SendSaleInvoiceMail { public async sendMail( tenantId: number, saleInvoiceId: number, - messageDTO: SendInvoiceMailDTO + messageOptions: SendInvoiceMailDTO ) { const defaultMessageOpts = await this.getMailOption( tenantId, @@ -73,7 +85,7 @@ export class SendSaleInvoiceMail { // Merge message opts with default options and validate the incoming options. const messageOpts = parseAndValidateMailOptions( defaultMessageOpts, - messageDTO + messageOptions ); const mail = new Mail() .setSubject(messageOpts.subject) @@ -90,6 +102,20 @@ export class SendSaleInvoiceMail { { filename: 'invoice.pdf', content: invoicePdfBuffer }, ]); } + // Triggers the event `onSaleInvoiceSend`. + await this.eventPublisher.emitAsync(events.saleInvoice.onMailSend, { + tenantId, + saleInvoiceId, + messageOptions, + } as ISaleInvoiceMailSend); + await mail.send(); + + // Triggers the event `onSaleInvoiceSend`. + await this.eventPublisher.emitAsync(events.saleInvoice.onMailSent, { + tenantId, + saleInvoiceId, + messageOptions, + } as ISaleInvoiceMailSend); } } diff --git a/packages/server/src/services/Sales/Invoices/SendSaleInvoiceMailJob.ts b/packages/server/src/services/Sales/Invoices/SendSaleInvoiceMailJob.ts index 3c1e49a6c..9de941f5f 100644 --- a/packages/server/src/services/Sales/Invoices/SendSaleInvoiceMailJob.ts +++ b/packages/server/src/services/Sales/Invoices/SendSaleInvoiceMailJob.ts @@ -19,11 +19,11 @@ export class SendSaleInvoiceMailJob { * Triggers sending invoice mail. */ private handler = async (job, done: Function) => { - const { tenantId, saleInvoiceId, messageDTO } = job.attrs.data; + const { tenantId, saleInvoiceId, messageOptions } = job.attrs.data; const sendInvoiceMail = Container.get(SendSaleInvoiceMail); try { - await sendInvoiceMail.sendMail(tenantId, saleInvoiceId, messageDTO); + await sendInvoiceMail.sendMail(tenantId, saleInvoiceId, messageOptions); done(); } catch (error) { console.log(error); diff --git a/packages/server/src/services/Sales/Invoices/SendSaleInvoiceMailReminder.ts b/packages/server/src/services/Sales/Invoices/SendSaleInvoiceMailReminder.ts index f16db0172..7829a3bf2 100644 --- a/packages/server/src/services/Sales/Invoices/SendSaleInvoiceMailReminder.ts +++ b/packages/server/src/services/Sales/Invoices/SendSaleInvoiceMailReminder.ts @@ -1,5 +1,9 @@ import { Inject, Service } from 'typedi'; -import { SendInvoiceMailDTO } from '@/interfaces'; +import { + ISaleInvoiceMailSend, + ISaleInvoiceMailSent, + SendInvoiceMailDTO, +} from '@/interfaces'; import Mail from '@/lib/Mail'; import { SaleInvoicePdf } from './SaleInvoicePdf'; import { SendSaleInvoiceMailCommon } from './SendInvoiceInvoiceMailCommon'; @@ -8,6 +12,8 @@ import { DEFAULT_INVOICE_REMINDER_MAIL_SUBJECT, } from './constants'; import { parseAndValidateMailOptions } from '@/services/MailNotification/utils'; +import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; +import events from '@/subscribers/events'; @Service() export class SendInvoiceMailReminder { @@ -20,6 +26,9 @@ export class SendInvoiceMailReminder { @Inject() private invoiceCommonMail: SendSaleInvoiceMailCommon; + @Inject() + private eventPublisher: EventPublisher; + /** * Triggers the reminder mail of the given sale invoice. * @param {number} tenantId @@ -86,6 +95,18 @@ export class SendInvoiceMailReminder { { filename: 'invoice.pdf', content: invoicePdfBuffer }, ]); } + // Triggers the event `onSaleInvoiceSend`. + await this.eventPublisher.emitAsync(events.saleInvoice.onMailReminderSend, { + saleInvoiceId, + messageOptions, + } as ISaleInvoiceMailSend); + await mail.send(); + + // Triggers the event `onSaleInvoiceSent`. + await this.eventPublisher.emitAsync(events.saleInvoice.onMailReminderSent, { + saleInvoiceId, + messageOptions, + } as ISaleInvoiceMailSent); } } diff --git a/packages/server/src/services/Sales/Invoices/subscribers/InvoiceChangeStatusOnMailSentSubscriber.ts b/packages/server/src/services/Sales/Invoices/subscribers/InvoiceChangeStatusOnMailSentSubscriber.ts new file mode 100644 index 000000000..fc4392777 --- /dev/null +++ b/packages/server/src/services/Sales/Invoices/subscribers/InvoiceChangeStatusOnMailSentSubscriber.ts @@ -0,0 +1,49 @@ +import { Inject, Service } from 'typedi'; +import events from '@/subscribers/events'; +import { ISaleInvoiceMailSent } from '@/interfaces'; +import { DeliverSaleInvoice } from '../DeliverSaleInvoice'; +import { ServiceError } from '@/exceptions'; +import { ERRORS } from '../constants'; + +@Service() +export class InvoiceChangeStatusOnMailSentSubscriber { + @Inject() + private markInvoiceDelivedService: DeliverSaleInvoice; + + /** + * Attaches events. + */ + public attach(bus) { + bus.subscribe(events.saleInvoice.onPreMailSend, this.markInvoiceDelivered); + bus.subscribe( + events.saleInvoice.onMailReminderSent, + this.markInvoiceDelivered + ); + } + + /** + * Marks the invoice delivered once the invoice mail sent. + * @param {ISaleInvoiceMailSent} + * @returns {Promise} + */ + private markInvoiceDelivered = async ({ + tenantId, + saleInvoiceId, + messageOptions, + }: ISaleInvoiceMailSent) => { + try { + await this.markInvoiceDelivedService.deliverSaleInvoice( + tenantId, + saleInvoiceId + ); + } catch (error) { + if ( + error instanceof ServiceError && + error.errorType === ERRORS.SALE_INVOICE_ALREADY_DELIVERED + ) { + } else { + throw error; + } + } + }; +} diff --git a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveMailNotification.ts b/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveMailNotification.ts index acb1ea7a1..bd8d4fa64 100644 --- a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveMailNotification.ts +++ b/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveMailNotification.ts @@ -2,6 +2,7 @@ import { Inject, Service } from 'typedi'; import { PaymentReceiveMailOpts, PaymentReceiveMailOptsDTO, + PaymentReceiveMailPresendEvent, SendInvoiceMailDTO, } from '@/interfaces'; import Mail from '@/lib/Mail'; @@ -13,6 +14,8 @@ import { import { GetPaymentReceive } from './GetPaymentReceive'; import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification'; import { parseAndValidateMailOptions } from '@/services/MailNotification/utils'; +import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; +import events from '@/subscribers/events'; @Service() export class SendPaymentReceiveMailNotification { @@ -28,6 +31,9 @@ export class SendPaymentReceiveMailNotification { @Inject('agenda') private agenda: any; + @Inject() + private eventPublisher: EventPublisher; + /** * Sends the mail of the given payment receive. * @param {number} tenantId @@ -46,6 +52,13 @@ export class SendPaymentReceiveMailNotification { messageDTO, }; await this.agenda.now('payment-receive-mail-send', payload); + + // Triggers `onPaymentReceivePreMailSend` event. + await this.eventPublisher.emitAsync(events.paymentReceive.onPreMailSend, { + tenantId, + paymentReceiveId, + messageOptions: messageDTO, + } as PaymentReceiveMailPresendEvent); } /** diff --git a/packages/server/src/services/Sales/Receipts/SaleReceiptMailNotification.ts b/packages/server/src/services/Sales/Receipts/SaleReceiptMailNotification.ts index 572bed2f8..24add40cc 100644 --- a/packages/server/src/services/Sales/Receipts/SaleReceiptMailNotification.ts +++ b/packages/server/src/services/Sales/Receipts/SaleReceiptMailNotification.ts @@ -7,9 +7,15 @@ import { DEFAULT_RECEIPT_MAIL_CONTENT, DEFAULT_RECEIPT_MAIL_SUBJECT, } from './constants'; -import { SaleReceiptMailOpts, SaleReceiptMailOptsDTO } from '@/interfaces'; +import { + ISaleReceiptMailPresend, + SaleReceiptMailOpts, + SaleReceiptMailOptsDTO, +} from '@/interfaces'; import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification'; import { parseAndValidateMailOptions } from '@/services/MailNotification/utils'; +import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; +import events from '@/subscribers/events'; @Service() export class SaleReceiptMailNotification { @@ -25,6 +31,9 @@ export class SaleReceiptMailNotification { @Inject() private contactMailNotification: ContactMailNotification; + @Inject() + private eventPublisher: EventPublisher; + @Inject('agenda') private agenda: any; @@ -37,14 +46,21 @@ export class SaleReceiptMailNotification { public async triggerMail( tenantId: number, saleReceiptId: number, - messageOpts: SaleReceiptMailOptsDTO + messageOptions: SaleReceiptMailOptsDTO ) { const payload = { tenantId, saleReceiptId, - messageOpts, + messageOpts: messageOptions, }; await this.agenda.now('sale-receipt-mail-send', payload); + + // Triggers the event `onSaleReceiptPreMailSend`. + await this.eventPublisher.emitAsync(events.saleReceipt.onPreMailSend, { + tenantId, + saleReceiptId, + messageOptions, + } as ISaleReceiptMailPresend); } /** diff --git a/packages/server/src/services/Sales/Receipts/subscribers/SaleReceiptCostGLEntriesSubscriber.ts b/packages/server/src/services/Sales/Receipts/subscribers/SaleReceiptCostGLEntriesSubscriber.ts index 5e6311005..39590198b 100644 --- a/packages/server/src/services/Sales/Receipts/subscribers/SaleReceiptCostGLEntriesSubscriber.ts +++ b/packages/server/src/services/Sales/Receipts/subscribers/SaleReceiptCostGLEntriesSubscriber.ts @@ -6,7 +6,7 @@ import { SaleReceiptCostGLEntries } from '../SaleReceiptCostGLEntries'; @Service() export class SaleReceiptCostGLEntriesSubscriber { @Inject() - saleReceiptCostEntries: SaleReceiptCostGLEntries; + private saleReceiptCostEntries: SaleReceiptCostGLEntries; /** * Attaches events. diff --git a/packages/server/src/services/Sales/Receipts/subscribers/SaleReceiptMarkClosedOnMailSentSubcriber.ts b/packages/server/src/services/Sales/Receipts/subscribers/SaleReceiptMarkClosedOnMailSentSubcriber.ts new file mode 100644 index 000000000..3a8d26394 --- /dev/null +++ b/packages/server/src/services/Sales/Receipts/subscribers/SaleReceiptMarkClosedOnMailSentSubcriber.ts @@ -0,0 +1,41 @@ +import { ISaleReceiptMailPresend } from '@/interfaces'; +import events from '@/subscribers/events'; +import { CloseSaleReceipt } from '../CloseSaleReceipt'; +import { Inject, Service } from 'typedi'; +import { ServiceError } from '@/exceptions'; +import { ERRORS } from '../constants'; + +@Service() +export class SaleReceiptMarkClosedOnMailSentSubcriber { + @Inject() + private closeReceiptService: CloseSaleReceipt; + + /** + * Attaches events. + */ + public attach(bus) { + bus.subscribe(events.saleReceipt.onPreMailSend, this.markReceiptClosed); + } + + /** + * Marks the sale receipt closed on submitting mail. + * @param {ISaleReceiptMailPresend} + */ + private markReceiptClosed = async ({ + tenantId, + saleReceiptId, + messageOptions, + }: ISaleReceiptMailPresend) => { + try { + await this.closeReceiptService.closeSaleReceipt(tenantId, saleReceiptId); + } catch (error) { + if ( + error instanceof ServiceError && + error.errorType === ERRORS.SALE_RECEIPT_IS_ALREADY_CLOSED + ) { + } else { + throw error; + } + } + }; +} diff --git a/packages/server/src/subscribers/events.ts b/packages/server/src/subscribers/events.ts index e54f48152..882027f98 100644 --- a/packages/server/src/subscribers/events.ts +++ b/packages/server/src/subscribers/events.ts @@ -131,7 +131,14 @@ export default { onNotifiedSms: 'onSaleInvoiceNotifiedSms', onNotifyMail: 'onSaleInvoiceNotifyMail', - onNotifyReminderMail: 'onSaleInvoiceNotifyReminderMail' + onNotifyReminderMail: 'onSaleInvoiceNotifyReminderMail', + + onPreMailSend: 'onSaleInvoicePreMailSend', + onMailSend: 'onSaleInvoiceMailSend', + onMailSent: 'onSaleInvoiceMailSent', + + onMailReminderSend: 'onSaleInvoiceMailReminderSend', + onMailReminderSent: 'onSaleInvoiceMailReminderSent', }, /** @@ -164,7 +171,11 @@ export default { onRejecting: 'onSaleEstimateRejecting', onRejected: 'onSaleEstimateRejected', - onNotifyMail: 'onSaleEstimateNotifyMail' + onNotifyMail: 'onSaleEstimateNotifyMail', + + onPreMailSend: 'onSaleEstimatePreMailSend', + onMailSend: 'onSaleEstimateMailSend', + onMailSent: 'onSaleEstimateMailSend', }, /** @@ -188,6 +199,10 @@ export default { onNotifySms: 'onSaleReceiptNotifySms', onNotifiedSms: 'onSaleReceiptNotifiedSms', + + onPreMailSend: 'onSaleReceiptPreMailSend', + onMailSend: 'onSaleReceiptMailSend', + onMailSent: 'onSaleReceiptMailSent', }, /** @@ -208,6 +223,10 @@ export default { onNotifySms: 'onPaymentReceiveNotifySms', onNotifiedSms: 'onPaymentReceiveNotifiedSms', + + onPreMailSend: 'onPaymentReceivePreMailSend', + onMailSend: 'onPaymentReceiveMailSend', + onMailSent: 'onPaymentReceiveMailSent', }, /** @@ -580,6 +599,6 @@ export default { onActivated: 'onTaxRateActivated', onInactivating: 'onTaxRateInactivating', - onInactivated: 'onTaxRateInactivated' + onInactivated: 'onTaxRateInactivated', }, }; diff --git a/packages/webapp/src/constants/dialogs.ts b/packages/webapp/src/constants/dialogs.ts index ba06da095..7fe1436fc 100644 --- a/packages/webapp/src/constants/dialogs.ts +++ b/packages/webapp/src/constants/dialogs.ts @@ -53,6 +53,9 @@ export enum DialogsName { EstimateMail = 'estimate-mail', ReceiptMail = 'receipt-mail', PaymentMail = 'payment-mail', + InvoiceFormMailDeliver = 'InvoiceFormMailDeliver', + EstimateFormMailDeliver = 'EstimateFormMailDeliver', + ReceiptFormMailDeliver = 'ReceiptFormMailDeliver', BalanceSheetPdfPreview = 'BalanceSheetPdfPreview', TrialBalanceSheetPdfPreview = 'TrialBalanceSheetPdfPreview', CashflowSheetPdfPreview = 'CashflowSheetPdfPreview', diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateForm/Dialogs/EstimateFormMailDeliverDialog.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateForm/Dialogs/EstimateFormMailDeliverDialog.tsx new file mode 100644 index 000000000..6a0b832c3 --- /dev/null +++ b/packages/webapp/src/containers/Sales/Estimates/EstimateForm/Dialogs/EstimateFormMailDeliverDialog.tsx @@ -0,0 +1,39 @@ +// @ts-nocheck +import React from 'react'; +import { Dialog, DialogSuspense } from '@/components'; +import withDialogRedux from '@/components/DialogReduxConnect'; +import { compose } from '@/utils'; + +const EstimateFormMailDeliverDialogContent = React.lazy( + () => import('./EstimateFormMailDeliverDialogContent'), +); + +/** + * Estimate mail dialog. + */ +function EstimateFormMailDeliverDialog({ + dialogName, + payload: { estimateId = null }, + isOpen, +}) { + return ( + + + + + + ); +} + +export default compose(withDialogRedux())(EstimateFormMailDeliverDialog); diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateForm/Dialogs/EstimateFormMailDeliverDialogContent.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateForm/Dialogs/EstimateFormMailDeliverDialogContent.tsx new file mode 100644 index 000000000..e77e3ee99 --- /dev/null +++ b/packages/webapp/src/containers/Sales/Estimates/EstimateForm/Dialogs/EstimateFormMailDeliverDialogContent.tsx @@ -0,0 +1,40 @@ +// @ts-nocheck +import * as R from 'ramda'; +import withDialogActions from '@/containers/Dialog/withDialogActions'; +import { useHistory } from 'react-router-dom'; +import EstimateMailDialogContent from '../../EstimateMailDialog/EstimateMailDialogContent'; +import { DialogsName } from '@/constants/dialogs'; + +interface EstimateFormDeliverDialogContent { + estimateId: number; +} + +function EstimateFormDeliverDialogContentRoot({ + estimateId, + + // #withDialogActions + closeDialog, +}: EstimateFormDeliverDialogContent) { + const history = useHistory(); + + const handleSubmit = () => { + closeDialog(DialogsName.EstimateFormMailDeliver); + history.push('/estimates'); + }; + const handleCancel = () => { + closeDialog(DialogsName.EstimateFormMailDeliver); + history.push('/estimates'); + }; + + return ( + + ); +} + +export default R.compose(withDialogActions)( + EstimateFormDeliverDialogContentRoot, +); diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateForm/EstimateFormDialogs.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateForm/EstimateFormDialogs.tsx index aa1c165a1..a50326486 100644 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateForm/EstimateFormDialogs.tsx +++ b/packages/webapp/src/containers/Sales/Estimates/EstimateForm/EstimateFormDialogs.tsx @@ -2,6 +2,8 @@ import React from 'react'; import { useFormikContext } from 'formik'; import EstimateNumberDialog from '@/containers/Dialogs/EstimateNumberDialog'; +import EstimateFormMailDeliverDialog from './Dialogs/EstimateFormMailDeliverDialog'; +import { DialogsName } from '@/constants/dialogs'; /** * Estimate form dialogs. @@ -25,6 +27,9 @@ export default function EstimateFormDialogs() { dialogName={'estimate-number-form'} onConfirm={handleEstimateNumberFormConfirm} /> + ); } diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialog.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialog.tsx index 9965db833..0d13e07fb 100644 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialog.tsx +++ b/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialog.tsx @@ -4,12 +4,12 @@ import { Dialog, DialogSuspense } from '@/components'; import withDialogRedux from '@/components/DialogReduxConnect'; import { compose } from '@/utils'; -const EstimateMailDialogContent = React.lazy( - () => import('./EstimateMailDialogContent'), +const EstimateMailDialogBody = React.lazy( + () => import('./EstimateMailDialogBody'), ); /** - * Invoice mail dialog. + * Estimate mail dialog. */ function EstimateMailDialog({ dialogName, @@ -26,10 +26,7 @@ function EstimateMailDialog({ style={{ width: 600 }} > - + ); diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogBody.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogBody.tsx new file mode 100644 index 000000000..2fa1c0472 --- /dev/null +++ b/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogBody.tsx @@ -0,0 +1,33 @@ +// @ts-nocheck +import * as R from 'ramda'; +import withDialogActions from '@/containers/Dialog/withDialogActions'; +import EstimateMailDialogContent from './EstimateMailDialogContent'; +import { DialogsName } from '@/constants/dialogs'; + +interface EstimateMailDialogBodyProps { + estimateId: number; +} + +function EstimateMailDialogBodyRoot({ + estimateId, + + // #withDialogActions + closeDialog, +}: EstimateMailDialogBodyProps) { + const handleSubmit = () => { + closeDialog(DialogsName.EstimateMail); + }; + const handleCancelClick = () => { + closeDialog(DialogsName.EstimateMail); + }; + + return ( + + ); +} + +export default R.compose(withDialogActions)(EstimateMailDialogBodyRoot); diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogBoot.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogBoot.tsx index d68afb4c5..65b05a9c1 100644 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogBoot.tsx +++ b/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogBoot.tsx @@ -6,12 +6,14 @@ import { DialogContent } from '@/components'; interface EstimateMailDialogBootValues { estimateId: number; mailOptions: any; + redirectToEstimatesList: boolean; } const EstimateMailDialagBoot = createContext(); interface EstimateMailDialogBootProps { estimateId: number; + redirectToEstimatesList?: boolean; children: React.ReactNode; } @@ -20,6 +22,7 @@ interface EstimateMailDialogBootProps { */ function EstimateMailDialogBoot({ estimateId, + redirectToEstimatesList, ...props }: EstimateMailDialogBootProps) { const { data: mailOptions, isLoading: isMailOptionsLoading } = @@ -29,6 +32,7 @@ function EstimateMailDialogBoot({ saleEstimateId: estimateId, mailOptions, isMailOptionsLoading, + redirectToEstimatesList, }; return ( diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogContent.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogContent.tsx index ad67bb048..c673f71c6 100644 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogContent.tsx +++ b/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogContent.tsx @@ -2,16 +2,21 @@ import { EstimateMailDialogBoot } from './EstimateMailDialogBoot'; import { EstimateMailDialogForm } from './EstimateMailDialogForm'; interface EstimateMailDialogContentProps { - dialogName: string; estimateId: number; + onFormSubmit?: () => void; + onCancelClick?: () => void; } export default function EstimateMailDialogContent({ - dialogName, estimateId, + onFormSubmit, + onCancelClick, }: EstimateMailDialogContentProps) { return ( - + - ) + ); } diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogForm.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogForm.tsx index f8811cdbb..8f51add43 100644 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogForm.tsx +++ b/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogForm.tsx @@ -1,6 +1,7 @@ // @ts-nocheck import { Formik } from 'formik'; import * as R from 'ramda'; +import { Intent } from '@blueprintjs/core'; import { useEstimateMailDialogBoot } from './EstimateMailDialogBoot'; import { DialogsName } from '@/constants/dialogs'; import withDialogActions from '@/containers/Dialog/withDialogActions'; @@ -12,7 +13,6 @@ import { transformMailFormToInitialValues, transformMailFormToRequest, } from '@/containers/SendMailNotification/utils'; -import { Intent } from '@blueprintjs/core'; import { AppToaster } from '@/components'; const initialFormValues = { @@ -25,11 +25,15 @@ interface EstimateMailFormValues extends MailNotificationFormValues { } function EstimateMailDialogFormRoot({ + onFormSubmit, + onCancelClick, + // #withDialogClose closeDialog, }) { const { mutateAsync: sendEstimateMail } = useSendSaleEstimateMail(); - const { mailOptions, saleEstimateId } = useEstimateMailDialogBoot(); + const { mailOptions, saleEstimateId, redirectToEstimatesList } = + useEstimateMailDialogBoot(); const initialValues = transformMailFormToInitialValues( mailOptions, @@ -48,14 +52,16 @@ function EstimateMailDialogFormRoot({ }); closeDialog(DialogsName.EstimateMail); setSubmitting(false); + onFormSubmit && onFormSubmit(); }) - .catch((error) => { + .catch(() => { setSubmitting(false); closeDialog(DialogsName.EstimateMail); AppToaster.show({ message: 'Something went wrong.', intent: Intent.DANGER, }); + onCancelClick && onCancelClick(); }); }; diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceForm/Dialogs/InvoiceFormMailDeliverDialog/InvoiceFormMailDeliverDialog.tsx b/packages/webapp/src/containers/Sales/Invoices/InvoiceForm/Dialogs/InvoiceFormMailDeliverDialog/InvoiceFormMailDeliverDialog.tsx new file mode 100644 index 000000000..f6ceb38f8 --- /dev/null +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceForm/Dialogs/InvoiceFormMailDeliverDialog/InvoiceFormMailDeliverDialog.tsx @@ -0,0 +1,39 @@ +// @ts-nocheck +import React from 'react'; +import { Dialog, DialogSuspense } from '@/components'; +import withDialogRedux from '@/components/DialogReduxConnect'; +import { compose } from '@/utils'; + +const InvoiceFormMailDeliverDialogContent = React.lazy( + () => import('./InvoiceFormMailDeliverDialogContent'), +); + +/** + * Invoice mail dialog. + */ +function InvoiceFormMailDeliverDialog({ + dialogName, + payload: { invoiceId = null }, + isOpen, +}) { + return ( + + + + + + ); +} + +export default compose(withDialogRedux())(InvoiceFormMailDeliverDialog); diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceForm/Dialogs/InvoiceFormMailDeliverDialog/InvoiceFormMailDeliverDialogContent.tsx b/packages/webapp/src/containers/Sales/Invoices/InvoiceForm/Dialogs/InvoiceFormMailDeliverDialog/InvoiceFormMailDeliverDialogContent.tsx new file mode 100644 index 000000000..8ce5e7c12 --- /dev/null +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceForm/Dialogs/InvoiceFormMailDeliverDialog/InvoiceFormMailDeliverDialogContent.tsx @@ -0,0 +1,40 @@ +// @ts-nocheck +import * as R from 'ramda'; +import { useHistory } from 'react-router-dom'; +import InvoiceMailDialogContent from '../../../InvoiceMailDialog/InvoiceMailDialogContent'; +import withDialogActions from '@/containers/Dialog/withDialogActions'; +import { DialogsName } from '@/constants/dialogs'; + +interface InvoiceFormDeliverDialogContent { + invoiceId: number; +} + +function InvoiceFormDeliverDialogContentRoot({ + invoiceId, + + // #withDialogActions + closeDialog, +}: InvoiceFormDeliverDialogContent) { + const history = useHistory(); + + const handleSubmit = () => { + history.push('/invoices'); + closeDialog(DialogsName.InvoiceFormMailDeliver); + }; + const handleCancel = () => { + history.push('/invoices'); + closeDialog(DialogsName.InvoiceFormMailDeliver); + }; + + return ( + + ); +} + +export default R.compose(withDialogActions)( + InvoiceFormDeliverDialogContentRoot, +); diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceForm/InvoiceFormDialogs.tsx b/packages/webapp/src/containers/Sales/Invoices/InvoiceForm/InvoiceFormDialogs.tsx index eb0e3e0ba..fca6a8bcb 100644 --- a/packages/webapp/src/containers/Sales/Invoices/InvoiceForm/InvoiceFormDialogs.tsx +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceForm/InvoiceFormDialogs.tsx @@ -1,8 +1,8 @@ // @ts-nocheck -import React from 'react'; import { useFormikContext } from 'formik'; import InvoiceNumberDialog from '@/containers/Dialogs/InvoiceNumberDialog'; import { DialogsName } from '@/constants/dialogs'; +import InvoiceFormMailDeliverDialog from './Dialogs/InvoiceFormMailDeliverDialog/InvoiceFormMailDeliverDialog'; /** * Invoice form dialogs. @@ -23,9 +23,14 @@ export default function InvoiceFormDialogs() { }; return ( - + <> + + + ); } diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/InvoiceMailDialog.tsx b/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/InvoiceMailDialog.tsx index 63430ce10..02c629e7c 100644 --- a/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/InvoiceMailDialog.tsx +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/InvoiceMailDialog.tsx @@ -4,8 +4,8 @@ import { Dialog, DialogSuspense } from '@/components'; import withDialogRedux from '@/components/DialogReduxConnect'; import { compose } from '@/utils'; -const InvoiceMailDialogContent = React.lazy( - () => import('./InvoiceMailDialogContent'), +const InvoiceMailDialogBody = React.lazy( + () => import('./InvoiceMailDialogBody'), ); /** @@ -21,15 +21,13 @@ function InvoiceMailDialog({ name={dialogName} title={'Invoice Mail'} isOpen={isOpen} - canEscapeJeyClose={true} + canEscapeJeyClose={false} + isCloseButtonShown={false} autoFocus={true} style={{ width: 600 }} > - + ); diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/InvoiceMailDialogBody.tsx b/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/InvoiceMailDialogBody.tsx new file mode 100644 index 000000000..3728c60ce --- /dev/null +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/InvoiceMailDialogBody.tsx @@ -0,0 +1,36 @@ +// @ts-nocheck +import * as R from 'ramda'; +import withDialogActions from '@/containers/Dialog/withDialogActions'; +import InvoiceMailDialogContent, { + InvoiceMailDialogContentProps, +} from './InvoiceMailDialogContent'; +import { DialogsName } from '@/constants/dialogs'; + +export interface InvoiceMailDialogBodyProps + extends InvoiceMailDialogContentProps {} + +function InvoiceMailDialogBodyRoot({ + invoiceId, + onCancelClick, + onFormSubmit, + + // #withDialogActions + closeDialog, +}: InvoiceMailDialogBodyProps) { + const handleCancelClick = () => { + closeDialog(DialogsName.InvoiceMail); + }; + const handleSubmitClick = () => { + closeDialog(DialogsName.InvoiceMail); + }; + + return ( + + ); +} + +export default R.compose(withDialogActions)(InvoiceMailDialogBodyRoot); diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/InvoiceMailDialogBoot.tsx b/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/InvoiceMailDialogBoot.tsx index ae16a0cf2..8c7d5f7e2 100644 --- a/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/InvoiceMailDialogBoot.tsx +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/InvoiceMailDialogBoot.tsx @@ -6,12 +6,14 @@ import { DialogContent } from '@/components'; interface InvoiceMailDialogBootValues { invoiceId: number; mailOptions: any; + redirectToInvoicesList: boolean; } const InvoiceMailDialagBoot = createContext(); interface InvoiceMailDialogBootProps { invoiceId: number; + redirectToInvoicesList?: boolean; children: React.ReactNode; } @@ -20,6 +22,7 @@ interface InvoiceMailDialogBootProps { */ function InvoiceMailDialogBoot({ invoiceId, + redirectToInvoicesList, ...props }: InvoiceMailDialogBootProps) { const { data: mailOptions, isLoading: isMailOptionsLoading } = @@ -29,6 +32,7 @@ function InvoiceMailDialogBoot({ saleInvoiceId: invoiceId, mailOptions, isMailOptionsLoading, + redirectToInvoicesList, }; return ( diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/InvoiceMailDialogContent.tsx b/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/InvoiceMailDialogContent.tsx index 37f3f091f..dbecb34fc 100644 --- a/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/InvoiceMailDialogContent.tsx +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/InvoiceMailDialogContent.tsx @@ -1,17 +1,22 @@ import { InvoiceMailDialogBoot } from './InvoiceMailDialogBoot'; import { InvoiceMailDialogForm } from './InvoiceMailDialogForm'; -interface InvoiceMailDialogContentProps { - dialogName: string; +export interface InvoiceMailDialogContentProps { invoiceId: number; + onFormSubmit?: () => void; + onCancelClick?: () => void; } export default function InvoiceMailDialogContent({ - dialogName, invoiceId, + onFormSubmit, + onCancelClick, }: InvoiceMailDialogContentProps) { return ( - + ); } diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/InvoiceMailDialogForm.tsx b/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/InvoiceMailDialogForm.tsx index 794ed890d..a91c03466 100644 --- a/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/InvoiceMailDialogForm.tsx +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/InvoiceMailDialogForm.tsx @@ -1,12 +1,9 @@ // @ts-nocheck import { Formik } from 'formik'; -import * as R from 'ramda'; import { Intent } from '@blueprintjs/core'; import { useInvoiceMailDialogBoot } from './InvoiceMailDialogBoot'; -import { DialogsName } from '@/constants/dialogs'; import { AppToaster } from '@/components'; import { useSendSaleInvoiceMail } from '@/hooks/query'; -import withDialogActions from '@/containers/Dialog/withDialogActions'; import { InvoiceMailDialogFormContent } from './InvoiceMailDialogFormContent'; import { InvoiceMailFormSchema } from './InvoiceMailDialogForm.schema'; import { @@ -25,10 +22,7 @@ interface InvoiceMailFormValues extends MailNotificationFormValues { attachInvoice: boolean; } -function InvoiceMailDialogFormRoot({ - // #withDialogActions - closeDialog, -}) { +export function InvoiceMailDialogForm({ onFormSubmit, onCancelClick }) { const { mailOptions, saleInvoiceId } = useInvoiceMailDialogBoot(); const { mutateAsync: sendInvoiceMail } = useSendSaleInvoiceMail(); @@ -47,8 +41,8 @@ function InvoiceMailDialogFormRoot({ message: 'The mail notification has been sent successfully.', intent: Intent.SUCCESS, }); - closeDialog(DialogsName.InvoiceMail); setSubmitting(false); + onFormSubmit && onFormSubmit(values); }) .catch(() => { AppToaster.show({ @@ -60,7 +54,7 @@ function InvoiceMailDialogFormRoot({ }; // Handle the close button click. const handleClose = () => { - closeDialog(DialogsName.InvoiceMail); + onCancelClick && onCancelClick(); }; return ( @@ -73,7 +67,3 @@ function InvoiceMailDialogFormRoot({ ); } - -export const InvoiceMailDialogForm = R.compose(withDialogActions)( - InvoiceMailDialogFormRoot, -); diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/index.ts b/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/index.ts index b64dcaaf3..b40bce27b 100644 --- a/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/index.ts +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceMailDialog/index.ts @@ -1 +1,2 @@ -export * from './InvoiceMailDialog'; \ No newline at end of file +export * from './InvoiceMailDialog'; +export * from './InvoiceMailDialogContent'; \ No newline at end of file diff --git a/packages/webapp/src/containers/Sales/PaymentReceives/PaymentMailDialog/PaymentMailDialog.tsx b/packages/webapp/src/containers/Sales/PaymentReceives/PaymentMailDialog/PaymentMailDialog.tsx index 6da51d03e..32c175ed9 100644 --- a/packages/webapp/src/containers/Sales/PaymentReceives/PaymentMailDialog/PaymentMailDialog.tsx +++ b/packages/webapp/src/containers/Sales/PaymentReceives/PaymentMailDialog/PaymentMailDialog.tsx @@ -13,7 +13,12 @@ const PaymentMailDialogContent = React.lazy( */ function PaymentMailDialog({ dialogName, - payload: { paymentReceiveId = null }, + payload: { + paymentReceiveId = null, + + // Redirects to the payments list on mail submitting. + redirectToPaymentsList = false, + }, isOpen, }) { return ( @@ -29,6 +34,7 @@ function PaymentMailDialog({ diff --git a/packages/webapp/src/containers/Sales/PaymentReceives/PaymentMailDialog/PaymentMailDialogBoot.tsx b/packages/webapp/src/containers/Sales/PaymentReceives/PaymentMailDialog/PaymentMailDialogBoot.tsx index aa08bd2e1..5fcbd4afa 100644 --- a/packages/webapp/src/containers/Sales/PaymentReceives/PaymentMailDialog/PaymentMailDialogBoot.tsx +++ b/packages/webapp/src/containers/Sales/PaymentReceives/PaymentMailDialog/PaymentMailDialogBoot.tsx @@ -13,6 +13,7 @@ const PaymentMailDialogBootContext = interface PaymentMailDialogBootProps { paymentReceiveId: number; + redirectToPaymentsList: boolean; children: React.ReactNode; } @@ -29,7 +30,8 @@ function PaymentMailDialogBoot({ const provider = { mailOptions, isMailOptionsLoading, - paymentReceiveId + paymentReceiveId, + redirectToPaymentsList }; return ( diff --git a/packages/webapp/src/containers/Sales/PaymentReceives/PaymentMailDialog/PaymentMailDialogContent.tsx b/packages/webapp/src/containers/Sales/PaymentReceives/PaymentMailDialog/PaymentMailDialogContent.tsx index 12fa57e05..33597cfa9 100644 --- a/packages/webapp/src/containers/Sales/PaymentReceives/PaymentMailDialog/PaymentMailDialogContent.tsx +++ b/packages/webapp/src/containers/Sales/PaymentReceives/PaymentMailDialog/PaymentMailDialogContent.tsx @@ -4,13 +4,18 @@ import { PaymentMailDialogForm } from './PaymentMailDialogForm'; interface PaymentMailDialogContentProps { dialogName: string; paymentReceiveId: number; + redirectToPaymentsList: boolean; } export default function PaymentMailDialogContent({ dialogName, paymentReceiveId, + redirectToPaymentsList, }: PaymentMailDialogContentProps) { return ( - + ); diff --git a/packages/webapp/src/containers/Sales/PaymentReceives/PaymentMailDialog/PaymentMailDialogForm.tsx b/packages/webapp/src/containers/Sales/PaymentReceives/PaymentMailDialog/PaymentMailDialogForm.tsx index bf0aa578b..f397a8740 100644 --- a/packages/webapp/src/containers/Sales/PaymentReceives/PaymentMailDialog/PaymentMailDialogForm.tsx +++ b/packages/webapp/src/containers/Sales/PaymentReceives/PaymentMailDialog/PaymentMailDialogForm.tsx @@ -3,7 +3,6 @@ import { Formik, FormikBag } from 'formik'; import * as R from 'ramda'; import { Intent } from '@blueprintjs/core'; import { usePaymentMailDialogBoot } from './PaymentMailDialogBoot'; -import withDialogActions from '@/containers/Dialog/withDialogActions'; import { DialogsName } from '@/constants/dialogs'; import { useSendPaymentReceiveMail } from '@/hooks/query'; import { PaymentMailDialogFormContent } from './PaymentMailDialogFormContent'; @@ -14,6 +13,8 @@ import { transformMailFormToInitialValues, } from '@/containers/SendMailNotification/utils'; import { AppToaster } from '@/components'; +import { useHistory } from 'react-router-dom'; +import withDialogActions from '@/containers/Dialog/withDialogActions'; const initialFormValues = { ...initialMailNotificationValues, @@ -28,9 +29,12 @@ export function PaymentMailDialogFormRoot({ // #withDialogActions closeDialog, }) { - const { mailOptions, paymentReceiveId } = usePaymentMailDialogBoot(); + const { mailOptions, paymentReceiveId, redirectToPaymentsList } = + usePaymentMailDialogBoot(); const { mutateAsync: sendPaymentMail } = useSendPaymentReceiveMail(); + const history = useHistory(); + const initialValues = transformMailFormToInitialValues( mailOptions, initialFormValues, @@ -51,6 +55,11 @@ export function PaymentMailDialogFormRoot({ }); setSubmitting(false); closeDialog(DialogsName.PaymentMail); + + // Redirects to payments list if the option is enabled. + if (redirectToPaymentsList) { + history.push('/payment-receives'); + } }) .catch(() => { AppToaster.show({ diff --git a/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/Dialogs/ReceiptFormMailDeliverDialog.tsx b/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/Dialogs/ReceiptFormMailDeliverDialog.tsx new file mode 100644 index 000000000..60f2758ff --- /dev/null +++ b/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/Dialogs/ReceiptFormMailDeliverDialog.tsx @@ -0,0 +1,39 @@ +// @ts-nocheck +import React from 'react'; +import { Dialog, DialogSuspense } from '@/components'; +import withDialogRedux from '@/components/DialogReduxConnect'; +import { compose } from '@/utils'; + +const ReceiptFormMailDeliverDialogContent = React.lazy( + () => import('./ReceiptFormMailDeliverDialogContent'), +); + +/** + * Receipt mail dialog. + */ +function ReceiptFormMailDeliverDialog({ + dialogName, + payload: { receiptId = null }, + isOpen, +}) { + return ( + + + + + + ); +} + +export default compose(withDialogRedux())(ReceiptFormMailDeliverDialog); diff --git a/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/Dialogs/ReceiptFormMailDeliverDialogContent.tsx b/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/Dialogs/ReceiptFormMailDeliverDialogContent.tsx new file mode 100644 index 000000000..4b5d31e40 --- /dev/null +++ b/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/Dialogs/ReceiptFormMailDeliverDialogContent.tsx @@ -0,0 +1,40 @@ +// @ts-nocheck +import * as R from 'ramda'; +import { useHistory } from 'react-router-dom'; +import withDialogActions from '@/containers/Dialog/withDialogActions'; +import ReceiptMailDialogContent from '../../ReceiptMailDialog/ReceiptMailDialogContent'; +import { DialogsName } from '@/constants/dialogs'; + +interface ReceiptFormDeliverDialogContent { + receiptId: number; +} + +function ReceiptFormDeliverDialogContentRoot({ + receiptId, + + // #withDialogActions + closeDialog, +}: ReceiptFormDeliverDialogContent) { + const history = useHistory(); + + const handleSubmit = () => { + history.push('/receipts'); + closeDialog(DialogsName.ReceiptFormMailDeliver); + }; + const handleCancel = () => { + history.push('/receipts'); + closeDialog(DialogsName.ReceiptFormMailDeliver); + }; + + return ( + + ); +} + +export default R.compose(withDialogActions)( + ReceiptFormDeliverDialogContentRoot, +); diff --git a/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormDialogs.tsx b/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormDialogs.tsx index 4fe2cb947..30477aee6 100644 --- a/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormDialogs.tsx +++ b/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormDialogs.tsx @@ -2,6 +2,8 @@ import React from 'react'; import { useFormikContext } from 'formik'; import ReceiptNumberDialog from '@/containers/Dialogs/ReceiptNumberDialog'; +import ReceiptFormMailDeliverDialog from './Dialogs/ReceiptFormMailDeliverDialog'; +import { DialogsName } from '@/constants/dialogs'; /** * Receipt form dialogs. @@ -27,6 +29,9 @@ export default function ReceiptFormDialogs() { dialogName={'receipt-number-form'} onConfirm={handleReceiptNumberFormConfirm} /> + ); } diff --git a/packages/webapp/src/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialog.tsx b/packages/webapp/src/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialog.tsx index a64ad5531..69a0e64a9 100644 --- a/packages/webapp/src/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialog.tsx +++ b/packages/webapp/src/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialog.tsx @@ -4,12 +4,12 @@ import { Dialog, DialogSuspense } from '@/components'; import withDialogRedux from '@/components/DialogReduxConnect'; import { compose } from '@/utils'; -const ReceiptMailDialogContent = React.lazy( - () => import('./ReceiptMailDialogContent'), +const ReceiptMailDialogBody = React.lazy( + () => import('./ReceiptMailDialogBody'), ); /** - * Invoice mail dialog. + * Receipt mail dialog. */ function ReceiptMailDialog({ dialogName, @@ -26,10 +26,7 @@ function ReceiptMailDialog({ style={{ width: 600 }} > - + ); diff --git a/packages/webapp/src/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialogBody.tsx b/packages/webapp/src/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialogBody.tsx new file mode 100644 index 000000000..fbd379b84 --- /dev/null +++ b/packages/webapp/src/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialogBody.tsx @@ -0,0 +1,33 @@ +// @ts-nocheck +import * as R from 'ramda'; +import withDialogActions from '@/containers/Dialog/withDialogActions'; +import ReceiptMailDialogContent, { + ReceiptMailDialogContentProps, +} from './ReceiptMailDialogContent'; +import { DialogsName } from '@/constants/dialogs'; + +interface ReceiptMailDialogBodyProps extends ReceiptMailDialogContentProps {} + +function ReceiptMailDialogBodyRoot({ + receiptId, + + // #withDialogActions + closeDialog, +}: ReceiptMailDialogBodyProps) { + const handleCancelClick = () => { + closeDialog(DialogsName.ReceiptMail); + }; + const handleSubmitClick = () => { + closeDialog(DialogsName.ReceiptMail); + }; + + return ( + + ); +} + +export default R.compose(withDialogActions)(ReceiptMailDialogBodyRoot); diff --git a/packages/webapp/src/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialogBoot.tsx b/packages/webapp/src/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialogBoot.tsx index 09eeb55f1..54f7200db 100644 --- a/packages/webapp/src/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialogBoot.tsx +++ b/packages/webapp/src/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialogBoot.tsx @@ -6,6 +6,7 @@ import { DialogContent } from '@/components'; interface ReceiptMailDialogBootValues { receiptId: number; mailOptions: any; + redirectToReceiptsList: boolean; } const ReceiptMailDialogBootContext = @@ -14,6 +15,7 @@ const ReceiptMailDialogBootContext = interface ReceiptMailDialogBootProps { receiptId: number; children: React.ReactNode; + redirectToReceiptsList?: boolean; } /** @@ -21,6 +23,7 @@ interface ReceiptMailDialogBootProps { */ function ReceiptMailDialogBoot({ receiptId, + redirectToReceiptsList = false, ...props }: ReceiptMailDialogBootProps) { const { data: mailOptions, isLoading: isMailOptionsLoading } = @@ -30,6 +33,7 @@ function ReceiptMailDialogBoot({ saleReceiptId: receiptId, mailOptions, isMailOptionsLoading, + redirectToReceiptsList, }; return ( diff --git a/packages/webapp/src/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialogContent.tsx b/packages/webapp/src/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialogContent.tsx index 955620f86..a02966a1c 100644 --- a/packages/webapp/src/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialogContent.tsx +++ b/packages/webapp/src/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialogContent.tsx @@ -2,17 +2,22 @@ import React from 'react'; import { ReceiptMailDialogBoot } from './ReceiptMailDialogBoot'; import { ReceiptMailDialogForm } from './ReceiptMailDialogForm'; -interface ReceiptMailDialogContentProps { - dialogName: string +export interface ReceiptMailDialogContentProps { receiptId: number; + onFormSubmit?: () => void; + onCancelClick?: () => void; } export default function ReceiptMailDialogContent({ - dialogName, receiptId, + onFormSubmit, + onCancelClick }: ReceiptMailDialogContentProps) { return ( - + ); } diff --git a/packages/webapp/src/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialogForm.tsx b/packages/webapp/src/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialogForm.tsx index fb9b845af..db2808f4c 100644 --- a/packages/webapp/src/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialogForm.tsx +++ b/packages/webapp/src/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialogForm.tsx @@ -23,7 +23,16 @@ interface ReceiptMailFormValues extends MailNotificationFormValues { attachReceipt: boolean; } -function ReceiptMailDialogFormRoot({ closeDialog }) { +interface ReceiptMailDialogFormProps { + onFormSubmit?: () => void; + onCancelClick?: () => void; +} + +export function ReceiptMailDialogForm({ + // #props + onFormSubmit, + onCancelClick, +}: ReceiptMailDialogFormProps) { const { mailOptions, saleReceiptId } = useReceiptMailDialogBoot(); const { mutateAsync: sendReceiptMail } = useSendSaleReceiptMail(); @@ -46,8 +55,8 @@ function ReceiptMailDialogFormRoot({ closeDialog }) { message: 'The mail notification has been sent successfully.', intent: Intent.SUCCESS, }); - closeDialog(DialogsName.ReceiptMail); setSubmitting(false); + onFormSubmit && onFormSubmit(values); }) .catch(() => { AppToaster.show({ @@ -59,7 +68,7 @@ function ReceiptMailDialogFormRoot({ closeDialog }) { }; // Handle the close button click. const handleClose = () => { - closeDialog(DialogsName.ReceiptMail); + onCancelClick && onCancelClick(); }; return ( @@ -68,7 +77,3 @@ function ReceiptMailDialogFormRoot({ closeDialog }) { ); } - -export const ReceiptMailDialogForm = R.compose(withDialogActions)( - ReceiptMailDialogFormRoot, -);