Merge pull request #332 from bigcapitalhq/big-105-convert-invoice-status-after-sending-mail-notification

feat: convert invoice status after sending mail notification
This commit is contained in:
Ahmed Bouhuolia
2024-02-21 20:29:55 +02:00
committed by GitHub
50 changed files with 767 additions and 94 deletions

View File

@@ -173,3 +173,9 @@ export type IPaymentReceiveGLCommonEntry = Pick<
export interface PaymentReceiveMailOpts extends CommonMailOptions {} export interface PaymentReceiveMailOpts extends CommonMailOptions {}
export interface PaymentReceiveMailOptsDTO extends CommonMailOptionsDTO {} export interface PaymentReceiveMailOptsDTO extends CommonMailOptionsDTO {}
export interface PaymentReceiveMailPresendEvent {
tenantId: number;
paymentReceiveId: number;
messageOptions: PaymentReceiveMailOptsDTO;
}

View File

@@ -133,3 +133,9 @@ export interface SaleEstimateMailOptions extends CommonMailOptions {
export interface SaleEstimateMailOptionsDTO extends CommonMailOptionsDTO { export interface SaleEstimateMailOptionsDTO extends CommonMailOptionsDTO {
attachEstimate?: boolean; attachEstimate?: boolean;
} }
export interface ISaleEstimateMailPresendEvent {
tenantId: number;
saleEstimateId: number;
messageOptions: SaleEstimateMailOptionsDTO;
}

View File

@@ -201,3 +201,15 @@ export interface ISaleInvoiceNotifyPayload {
saleInvoiceId: number; saleInvoiceId: number;
messageDTO: SendInvoiceMailDTO; messageDTO: SendInvoiceMailDTO;
} }
export interface ISaleInvoiceMailSend {
tenantId: number;
saleInvoiceId: number;
messageOptions: SendInvoiceMailDTO;
}
export interface ISaleInvoiceMailSent {
tenantId: number;
saleInvoiceId: number;
messageOptions: SendInvoiceMailDTO;
}

View File

@@ -143,3 +143,9 @@ export interface SaleReceiptMailOpts extends CommonMailOptions {
export interface SaleReceiptMailOptsDTO extends CommonMailOptionsDTO { export interface SaleReceiptMailOptsDTO extends CommonMailOptionsDTO {
attachReceipt?: boolean; attachReceipt?: boolean;
} }
export interface ISaleReceiptMailPresend {
tenantId: number;
saleReceiptId: number;
messageOptions: SaleReceiptMailOptsDTO;
}

View File

@@ -84,6 +84,9 @@ import { WriteInvoiceTaxTransactionsSubscriber } from '@/services/TaxRates/subsc
import { BillTaxRateValidateSubscriber } from '@/services/TaxRates/subscribers/BillTaxRateValidateSubscriber'; import { BillTaxRateValidateSubscriber } from '@/services/TaxRates/subscribers/BillTaxRateValidateSubscriber';
import { WriteBillTaxTransactionsSubscriber } from '@/services/TaxRates/subscribers/WriteBillTaxTransactionsSubscriber'; import { WriteBillTaxTransactionsSubscriber } from '@/services/TaxRates/subscribers/WriteBillTaxTransactionsSubscriber';
import { SyncItemTaxRateOnEditTaxSubscriber } from '@/services/TaxRates/SyncItemTaxRateOnEditTaxSubscriber'; 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 () => { export default () => {
return new EventPublisher(); return new EventPublisher();
@@ -104,8 +107,12 @@ export const susbcribers = () => {
InventorySubscriber, InventorySubscriber,
CustomerWriteGLOpeningBalanceSubscriber, CustomerWriteGLOpeningBalanceSubscriber,
VendorsWriteGLOpeningSubscriber, VendorsWriteGLOpeningSubscriber,
// # Estimate
SaleEstimateAutoSerialSubscriber, SaleEstimateAutoSerialSubscriber,
SaleEstimateSmsNotificationSubscriber, SaleEstimateSmsNotificationSubscriber,
SaleEstimateMarkApprovedOnMailSent,
ExpensesWriteGLSubscriber, ExpensesWriteGLSubscriber,
SaleReceiptAutoSerialSubscriber, SaleReceiptAutoSerialSubscriber,
SaleInvoiceAutoIncrementSubscriber, SaleInvoiceAutoIncrementSubscriber,
@@ -152,11 +159,13 @@ export const susbcribers = () => {
// #Invoices // #Invoices
InvoicePaymentGLRewriteSubscriber, InvoicePaymentGLRewriteSubscriber,
InvoiceCostGLEntriesSubscriber, InvoiceCostGLEntriesSubscriber,
InvoiceChangeStatusOnMailSentSubscriber,
BillPaymentsGLEntriesRewriteSubscriber, BillPaymentsGLEntriesRewriteSubscriber,
// # Receipts // # Receipts
SaleReceiptCostGLEntriesSubscriber, SaleReceiptCostGLEntriesSubscriber,
SaleReceiptMarkClosedOnMailSentSubcriber,
// Transaction locking. // Transaction locking.
SalesTransactionLockingGuardSubscriber, SalesTransactionLockingGuardSubscriber,
@@ -199,6 +208,6 @@ export const susbcribers = () => {
BillTaxRateValidateSubscriber, BillTaxRateValidateSubscriber,
WriteBillTaxTransactionsSubscriber, WriteBillTaxTransactionsSubscriber,
SyncItemTaxRateOnEditTaxSubscriber SyncItemTaxRateOnEditTaxSubscriber,
]; ];
}; };

View File

@@ -1,6 +1,5 @@
import { IAccountTransaction } from '@/interfaces'; import { IAccountTransaction } from '@/interfaces';
import { Transformer } from '@/lib/Transformer/Transformer'; import { Transformer } from '@/lib/Transformer/Transformer';
import { transaction } from 'objection';
export default class AccountTransactionTransformer extends Transformer { export default class AccountTransactionTransformer extends Transformer {
/** /**

View File

@@ -5,7 +5,6 @@ import { IAccountEventActivatedPayload } from '@/interfaces';
import events from '@/subscribers/events'; import events from '@/subscribers/events';
import UnitOfWork from '@/services/UnitOfWork'; import UnitOfWork from '@/services/UnitOfWork';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import { CommandAccountValidators } from './CommandAccountValidators';
@Service() @Service()
export class ActivateAccount { export class ActivateAccount {
@@ -18,9 +17,6 @@ export class ActivateAccount {
@Inject() @Inject()
private uow: UnitOfWork; private uow: UnitOfWork;
@Inject()
private validator: CommandAccountValidators;
/** /**
* Activates/Inactivates the given account. * Activates/Inactivates the given account.
* @param {number} tenantId * @param {number} tenantId

View File

@@ -8,11 +8,14 @@ import {
import { SaleEstimatesPdf } from './SaleEstimatesPdf'; import { SaleEstimatesPdf } from './SaleEstimatesPdf';
import { GetSaleEstimate } from './GetSaleEstimate'; import { GetSaleEstimate } from './GetSaleEstimate';
import { import {
ISaleEstimateMailPresendEvent,
SaleEstimateMailOptions, SaleEstimateMailOptions,
SaleEstimateMailOptionsDTO, SaleEstimateMailOptionsDTO,
} from '@/interfaces'; } from '@/interfaces';
import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification'; import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification';
import { parseAndValidateMailOptions } from '@/services/MailNotification/utils'; import { parseAndValidateMailOptions } from '@/services/MailNotification/utils';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import events from '@/subscribers/events';
@Service() @Service()
export class SendSaleEstimateMail { export class SendSaleEstimateMail {
@@ -31,6 +34,9 @@ export class SendSaleEstimateMail {
@Inject('agenda') @Inject('agenda')
private agenda: any; private agenda: any;
@Inject()
private eventPublisher: EventPublisher;
/** /**
* Triggers the reminder mail of the given sale estimate. * Triggers the reminder mail of the given sale estimate.
* @param {number} tenantId - * @param {number} tenantId -
@@ -49,6 +55,13 @@ export class SendSaleEstimateMail {
messageOptions, messageOptions,
}; };
await this.agenda.now('sale-estimate-mail-send', payload); 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 { return {
...mailOptions, ...mailOptions,
data: formatterData, data: formatterData,
attachEstimate: true attachEstimate: true,
}; };
}; };

View File

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

View File

@@ -4,7 +4,6 @@ import { ServiceError } from '@/exceptions';
import { import {
ISaleInvoiceDeliveringPayload, ISaleInvoiceDeliveringPayload,
ISaleInvoiceEventDeliveredPayload, ISaleInvoiceEventDeliveredPayload,
ISystemUser,
} from '@/interfaces'; } from '@/interfaces';
import { ERRORS } from './constants'; import { ERRORS } from './constants';
import { Inject, Service } from 'typedi'; import { Inject, Service } from 'typedi';
@@ -36,8 +35,7 @@ export class DeliverSaleInvoice {
*/ */
public async deliverSaleInvoice( public async deliverSaleInvoice(
tenantId: number, tenantId: number,
saleInvoiceId: number, saleInvoiceId: number
authorizedUser: ISystemUser
): Promise<void> { ): Promise<void> {
const { SaleInvoice } = this.tenancy.models(tenantId); const { SaleInvoice } = this.tenancy.models(tenantId);

View File

@@ -1,6 +1,6 @@
import { Inject, Service } from 'typedi'; import { Inject, Service } from 'typedi';
import Mail from '@/lib/Mail'; import Mail from '@/lib/Mail';
import { SendInvoiceMailDTO } from '@/interfaces'; import { ISaleInvoiceMailSend, SendInvoiceMailDTO } from '@/interfaces';
import { SaleInvoicePdf } from './SaleInvoicePdf'; import { SaleInvoicePdf } from './SaleInvoicePdf';
import { SendSaleInvoiceMailCommon } from './SendInvoiceInvoiceMailCommon'; import { SendSaleInvoiceMailCommon } from './SendInvoiceInvoiceMailCommon';
import { import {
@@ -8,6 +8,8 @@ import {
DEFAULT_INVOICE_MAIL_SUBJECT, DEFAULT_INVOICE_MAIL_SUBJECT,
} from './constants'; } from './constants';
import { parseAndValidateMailOptions } from '@/services/MailNotification/utils'; import { parseAndValidateMailOptions } from '@/services/MailNotification/utils';
import events from '@/subscribers/events';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
@Service() @Service()
export class SendSaleInvoiceMail { export class SendSaleInvoiceMail {
@@ -20,6 +22,9 @@ export class SendSaleInvoiceMail {
@Inject('agenda') @Inject('agenda')
private agenda: any; private agenda: any;
@Inject()
private eventPublisher: EventPublisher;
/** /**
* Sends the invoice mail of the given sale invoice. * Sends the invoice mail of the given sale invoice.
* @param {number} tenantId * @param {number} tenantId
@@ -29,14 +34,21 @@ export class SendSaleInvoiceMail {
public async triggerMail( public async triggerMail(
tenantId: number, tenantId: number,
saleInvoiceId: number, saleInvoiceId: number,
messageDTO: SendInvoiceMailDTO messageOptions: SendInvoiceMailDTO
) { ) {
const payload = { const payload = {
tenantId, tenantId,
saleInvoiceId, saleInvoiceId,
messageDTO, messageOptions,
}; };
await this.agenda.now('sale-invoice-mail-send', payload); 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( public async sendMail(
tenantId: number, tenantId: number,
saleInvoiceId: number, saleInvoiceId: number,
messageDTO: SendInvoiceMailDTO messageOptions: SendInvoiceMailDTO
) { ) {
const defaultMessageOpts = await this.getMailOption( const defaultMessageOpts = await this.getMailOption(
tenantId, tenantId,
@@ -73,7 +85,7 @@ export class SendSaleInvoiceMail {
// Merge message opts with default options and validate the incoming options. // Merge message opts with default options and validate the incoming options.
const messageOpts = parseAndValidateMailOptions( const messageOpts = parseAndValidateMailOptions(
defaultMessageOpts, defaultMessageOpts,
messageDTO messageOptions
); );
const mail = new Mail() const mail = new Mail()
.setSubject(messageOpts.subject) .setSubject(messageOpts.subject)
@@ -90,6 +102,20 @@ export class SendSaleInvoiceMail {
{ filename: 'invoice.pdf', content: invoicePdfBuffer }, { filename: 'invoice.pdf', content: invoicePdfBuffer },
]); ]);
} }
// Triggers the event `onSaleInvoiceSend`.
await this.eventPublisher.emitAsync(events.saleInvoice.onMailSend, {
tenantId,
saleInvoiceId,
messageOptions,
} as ISaleInvoiceMailSend);
await mail.send(); await mail.send();
// Triggers the event `onSaleInvoiceSend`.
await this.eventPublisher.emitAsync(events.saleInvoice.onMailSent, {
tenantId,
saleInvoiceId,
messageOptions,
} as ISaleInvoiceMailSend);
} }
} }

View File

@@ -19,11 +19,11 @@ export class SendSaleInvoiceMailJob {
* Triggers sending invoice mail. * Triggers sending invoice mail.
*/ */
private handler = async (job, done: Function) => { 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); const sendInvoiceMail = Container.get(SendSaleInvoiceMail);
try { try {
await sendInvoiceMail.sendMail(tenantId, saleInvoiceId, messageDTO); await sendInvoiceMail.sendMail(tenantId, saleInvoiceId, messageOptions);
done(); done();
} catch (error) { } catch (error) {
console.log(error); console.log(error);

View File

@@ -1,5 +1,9 @@
import { Inject, Service } from 'typedi'; import { Inject, Service } from 'typedi';
import { SendInvoiceMailDTO } from '@/interfaces'; import {
ISaleInvoiceMailSend,
ISaleInvoiceMailSent,
SendInvoiceMailDTO,
} from '@/interfaces';
import Mail from '@/lib/Mail'; import Mail from '@/lib/Mail';
import { SaleInvoicePdf } from './SaleInvoicePdf'; import { SaleInvoicePdf } from './SaleInvoicePdf';
import { SendSaleInvoiceMailCommon } from './SendInvoiceInvoiceMailCommon'; import { SendSaleInvoiceMailCommon } from './SendInvoiceInvoiceMailCommon';
@@ -8,6 +12,8 @@ import {
DEFAULT_INVOICE_REMINDER_MAIL_SUBJECT, DEFAULT_INVOICE_REMINDER_MAIL_SUBJECT,
} from './constants'; } from './constants';
import { parseAndValidateMailOptions } from '@/services/MailNotification/utils'; import { parseAndValidateMailOptions } from '@/services/MailNotification/utils';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import events from '@/subscribers/events';
@Service() @Service()
export class SendInvoiceMailReminder { export class SendInvoiceMailReminder {
@@ -20,6 +26,9 @@ export class SendInvoiceMailReminder {
@Inject() @Inject()
private invoiceCommonMail: SendSaleInvoiceMailCommon; private invoiceCommonMail: SendSaleInvoiceMailCommon;
@Inject()
private eventPublisher: EventPublisher;
/** /**
* Triggers the reminder mail of the given sale invoice. * Triggers the reminder mail of the given sale invoice.
* @param {number} tenantId * @param {number} tenantId
@@ -86,6 +95,18 @@ export class SendInvoiceMailReminder {
{ filename: 'invoice.pdf', content: invoicePdfBuffer }, { filename: 'invoice.pdf', content: invoicePdfBuffer },
]); ]);
} }
// Triggers the event `onSaleInvoiceSend`.
await this.eventPublisher.emitAsync(events.saleInvoice.onMailReminderSend, {
saleInvoiceId,
messageOptions,
} as ISaleInvoiceMailSend);
await mail.send(); await mail.send();
// Triggers the event `onSaleInvoiceSent`.
await this.eventPublisher.emitAsync(events.saleInvoice.onMailReminderSent, {
saleInvoiceId,
messageOptions,
} as ISaleInvoiceMailSent);
} }
} }

View File

@@ -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<void>}
*/
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;
}
}
};
}

View File

@@ -2,6 +2,7 @@ import { Inject, Service } from 'typedi';
import { import {
PaymentReceiveMailOpts, PaymentReceiveMailOpts,
PaymentReceiveMailOptsDTO, PaymentReceiveMailOptsDTO,
PaymentReceiveMailPresendEvent,
SendInvoiceMailDTO, SendInvoiceMailDTO,
} from '@/interfaces'; } from '@/interfaces';
import Mail from '@/lib/Mail'; import Mail from '@/lib/Mail';
@@ -13,6 +14,8 @@ import {
import { GetPaymentReceive } from './GetPaymentReceive'; import { GetPaymentReceive } from './GetPaymentReceive';
import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification'; import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification';
import { parseAndValidateMailOptions } from '@/services/MailNotification/utils'; import { parseAndValidateMailOptions } from '@/services/MailNotification/utils';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import events from '@/subscribers/events';
@Service() @Service()
export class SendPaymentReceiveMailNotification { export class SendPaymentReceiveMailNotification {
@@ -28,6 +31,9 @@ export class SendPaymentReceiveMailNotification {
@Inject('agenda') @Inject('agenda')
private agenda: any; private agenda: any;
@Inject()
private eventPublisher: EventPublisher;
/** /**
* Sends the mail of the given payment receive. * Sends the mail of the given payment receive.
* @param {number} tenantId * @param {number} tenantId
@@ -46,6 +52,13 @@ export class SendPaymentReceiveMailNotification {
messageDTO, messageDTO,
}; };
await this.agenda.now('payment-receive-mail-send', payload); 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);
} }
/** /**

View File

@@ -7,9 +7,15 @@ import {
DEFAULT_RECEIPT_MAIL_CONTENT, DEFAULT_RECEIPT_MAIL_CONTENT,
DEFAULT_RECEIPT_MAIL_SUBJECT, DEFAULT_RECEIPT_MAIL_SUBJECT,
} from './constants'; } from './constants';
import { SaleReceiptMailOpts, SaleReceiptMailOptsDTO } from '@/interfaces'; import {
ISaleReceiptMailPresend,
SaleReceiptMailOpts,
SaleReceiptMailOptsDTO,
} from '@/interfaces';
import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification'; import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification';
import { parseAndValidateMailOptions } from '@/services/MailNotification/utils'; import { parseAndValidateMailOptions } from '@/services/MailNotification/utils';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import events from '@/subscribers/events';
@Service() @Service()
export class SaleReceiptMailNotification { export class SaleReceiptMailNotification {
@@ -25,6 +31,9 @@ export class SaleReceiptMailNotification {
@Inject() @Inject()
private contactMailNotification: ContactMailNotification; private contactMailNotification: ContactMailNotification;
@Inject()
private eventPublisher: EventPublisher;
@Inject('agenda') @Inject('agenda')
private agenda: any; private agenda: any;
@@ -37,14 +46,21 @@ export class SaleReceiptMailNotification {
public async triggerMail( public async triggerMail(
tenantId: number, tenantId: number,
saleReceiptId: number, saleReceiptId: number,
messageOpts: SaleReceiptMailOptsDTO messageOptions: SaleReceiptMailOptsDTO
) { ) {
const payload = { const payload = {
tenantId, tenantId,
saleReceiptId, saleReceiptId,
messageOpts, messageOpts: messageOptions,
}; };
await this.agenda.now('sale-receipt-mail-send', payload); 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);
} }
/** /**

View File

@@ -6,7 +6,7 @@ import { SaleReceiptCostGLEntries } from '../SaleReceiptCostGLEntries';
@Service() @Service()
export class SaleReceiptCostGLEntriesSubscriber { export class SaleReceiptCostGLEntriesSubscriber {
@Inject() @Inject()
saleReceiptCostEntries: SaleReceiptCostGLEntries; private saleReceiptCostEntries: SaleReceiptCostGLEntries;
/** /**
* Attaches events. * Attaches events.

View File

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

View File

@@ -131,7 +131,14 @@ export default {
onNotifiedSms: 'onSaleInvoiceNotifiedSms', onNotifiedSms: 'onSaleInvoiceNotifiedSms',
onNotifyMail: 'onSaleInvoiceNotifyMail', onNotifyMail: 'onSaleInvoiceNotifyMail',
onNotifyReminderMail: 'onSaleInvoiceNotifyReminderMail' onNotifyReminderMail: 'onSaleInvoiceNotifyReminderMail',
onPreMailSend: 'onSaleInvoicePreMailSend',
onMailSend: 'onSaleInvoiceMailSend',
onMailSent: 'onSaleInvoiceMailSent',
onMailReminderSend: 'onSaleInvoiceMailReminderSend',
onMailReminderSent: 'onSaleInvoiceMailReminderSent',
}, },
/** /**
@@ -164,7 +171,11 @@ export default {
onRejecting: 'onSaleEstimateRejecting', onRejecting: 'onSaleEstimateRejecting',
onRejected: 'onSaleEstimateRejected', onRejected: 'onSaleEstimateRejected',
onNotifyMail: 'onSaleEstimateNotifyMail' onNotifyMail: 'onSaleEstimateNotifyMail',
onPreMailSend: 'onSaleEstimatePreMailSend',
onMailSend: 'onSaleEstimateMailSend',
onMailSent: 'onSaleEstimateMailSend',
}, },
/** /**
@@ -188,6 +199,10 @@ export default {
onNotifySms: 'onSaleReceiptNotifySms', onNotifySms: 'onSaleReceiptNotifySms',
onNotifiedSms: 'onSaleReceiptNotifiedSms', onNotifiedSms: 'onSaleReceiptNotifiedSms',
onPreMailSend: 'onSaleReceiptPreMailSend',
onMailSend: 'onSaleReceiptMailSend',
onMailSent: 'onSaleReceiptMailSent',
}, },
/** /**
@@ -208,6 +223,10 @@ export default {
onNotifySms: 'onPaymentReceiveNotifySms', onNotifySms: 'onPaymentReceiveNotifySms',
onNotifiedSms: 'onPaymentReceiveNotifiedSms', onNotifiedSms: 'onPaymentReceiveNotifiedSms',
onPreMailSend: 'onPaymentReceivePreMailSend',
onMailSend: 'onPaymentReceiveMailSend',
onMailSent: 'onPaymentReceiveMailSent',
}, },
/** /**
@@ -580,6 +599,6 @@ export default {
onActivated: 'onTaxRateActivated', onActivated: 'onTaxRateActivated',
onInactivating: 'onTaxRateInactivating', onInactivating: 'onTaxRateInactivating',
onInactivated: 'onTaxRateInactivated' onInactivated: 'onTaxRateInactivated',
}, },
}; };

View File

@@ -53,6 +53,9 @@ export enum DialogsName {
EstimateMail = 'estimate-mail', EstimateMail = 'estimate-mail',
ReceiptMail = 'receipt-mail', ReceiptMail = 'receipt-mail',
PaymentMail = 'payment-mail', PaymentMail = 'payment-mail',
InvoiceFormMailDeliver = 'InvoiceFormMailDeliver',
EstimateFormMailDeliver = 'EstimateFormMailDeliver',
ReceiptFormMailDeliver = 'ReceiptFormMailDeliver',
BalanceSheetPdfPreview = 'BalanceSheetPdfPreview', BalanceSheetPdfPreview = 'BalanceSheetPdfPreview',
TrialBalanceSheetPdfPreview = 'TrialBalanceSheetPdfPreview', TrialBalanceSheetPdfPreview = 'TrialBalanceSheetPdfPreview',
CashflowSheetPdfPreview = 'CashflowSheetPdfPreview', CashflowSheetPdfPreview = 'CashflowSheetPdfPreview',

View File

@@ -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 (
<Dialog
name={dialogName}
title={'Estimate Mail'}
isOpen={isOpen}
canEscapeJeyClose={false}
isCloseButtonShown={false}
autoFocus={true}
style={{ width: 600 }}
>
<DialogSuspense>
<EstimateFormMailDeliverDialogContent
dialogName={dialogName}
estimateId={estimateId}
/>
</DialogSuspense>
</Dialog>
);
}
export default compose(withDialogRedux())(EstimateFormMailDeliverDialog);

View File

@@ -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 (
<EstimateMailDialogContent
estimateId={estimateId}
onFormSubmit={handleSubmit}
onCancelClick={handleCancel}
/>
);
}
export default R.compose(withDialogActions)(
EstimateFormDeliverDialogContentRoot,
);

View File

@@ -2,6 +2,8 @@
import React from 'react'; import React from 'react';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import EstimateNumberDialog from '@/containers/Dialogs/EstimateNumberDialog'; import EstimateNumberDialog from '@/containers/Dialogs/EstimateNumberDialog';
import EstimateFormMailDeliverDialog from './Dialogs/EstimateFormMailDeliverDialog';
import { DialogsName } from '@/constants/dialogs';
/** /**
* Estimate form dialogs. * Estimate form dialogs.
@@ -25,6 +27,9 @@ export default function EstimateFormDialogs() {
dialogName={'estimate-number-form'} dialogName={'estimate-number-form'}
onConfirm={handleEstimateNumberFormConfirm} onConfirm={handleEstimateNumberFormConfirm}
/> />
<EstimateFormMailDeliverDialog
dialogName={DialogsName.EstimateFormMailDeliver}
/>
</> </>
); );
} }

View File

@@ -4,12 +4,12 @@ import { Dialog, DialogSuspense } from '@/components';
import withDialogRedux from '@/components/DialogReduxConnect'; import withDialogRedux from '@/components/DialogReduxConnect';
import { compose } from '@/utils'; import { compose } from '@/utils';
const EstimateMailDialogContent = React.lazy( const EstimateMailDialogBody = React.lazy(
() => import('./EstimateMailDialogContent'), () => import('./EstimateMailDialogBody'),
); );
/** /**
* Invoice mail dialog. * Estimate mail dialog.
*/ */
function EstimateMailDialog({ function EstimateMailDialog({
dialogName, dialogName,
@@ -26,10 +26,7 @@ function EstimateMailDialog({
style={{ width: 600 }} style={{ width: 600 }}
> >
<DialogSuspense> <DialogSuspense>
<EstimateMailDialogContent <EstimateMailDialogBody estimateId={estimateId} />
dialogName={dialogName}
estimateId={estimateId}
/>
</DialogSuspense> </DialogSuspense>
</Dialog> </Dialog>
); );

View File

@@ -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 (
<EstimateMailDialogContent
estimateId={estimateId}
onFormSubmit={handleSubmit}
onCancelClick={handleCancelClick}
/>
);
}
export default R.compose(withDialogActions)(EstimateMailDialogBodyRoot);

View File

@@ -6,12 +6,14 @@ import { DialogContent } from '@/components';
interface EstimateMailDialogBootValues { interface EstimateMailDialogBootValues {
estimateId: number; estimateId: number;
mailOptions: any; mailOptions: any;
redirectToEstimatesList: boolean;
} }
const EstimateMailDialagBoot = createContext<EstimateMailDialogBootValues>(); const EstimateMailDialagBoot = createContext<EstimateMailDialogBootValues>();
interface EstimateMailDialogBootProps { interface EstimateMailDialogBootProps {
estimateId: number; estimateId: number;
redirectToEstimatesList?: boolean;
children: React.ReactNode; children: React.ReactNode;
} }
@@ -20,6 +22,7 @@ interface EstimateMailDialogBootProps {
*/ */
function EstimateMailDialogBoot({ function EstimateMailDialogBoot({
estimateId, estimateId,
redirectToEstimatesList,
...props ...props
}: EstimateMailDialogBootProps) { }: EstimateMailDialogBootProps) {
const { data: mailOptions, isLoading: isMailOptionsLoading } = const { data: mailOptions, isLoading: isMailOptionsLoading } =
@@ -29,6 +32,7 @@ function EstimateMailDialogBoot({
saleEstimateId: estimateId, saleEstimateId: estimateId,
mailOptions, mailOptions,
isMailOptionsLoading, isMailOptionsLoading,
redirectToEstimatesList,
}; };
return ( return (

View File

@@ -2,16 +2,21 @@ import { EstimateMailDialogBoot } from './EstimateMailDialogBoot';
import { EstimateMailDialogForm } from './EstimateMailDialogForm'; import { EstimateMailDialogForm } from './EstimateMailDialogForm';
interface EstimateMailDialogContentProps { interface EstimateMailDialogContentProps {
dialogName: string;
estimateId: number; estimateId: number;
onFormSubmit?: () => void;
onCancelClick?: () => void;
} }
export default function EstimateMailDialogContent({ export default function EstimateMailDialogContent({
dialogName,
estimateId, estimateId,
onFormSubmit,
onCancelClick,
}: EstimateMailDialogContentProps) { }: EstimateMailDialogContentProps) {
return ( return (
<EstimateMailDialogBoot estimateId={estimateId}> <EstimateMailDialogBoot estimateId={estimateId}>
<EstimateMailDialogForm /> <EstimateMailDialogForm
onFormSubmit={onFormSubmit}
onCancelClick={onCancelClick}
/>
</EstimateMailDialogBoot> </EstimateMailDialogBoot>
) );
} }

View File

@@ -1,6 +1,7 @@
// @ts-nocheck // @ts-nocheck
import { Formik } from 'formik'; import { Formik } from 'formik';
import * as R from 'ramda'; import * as R from 'ramda';
import { Intent } from '@blueprintjs/core';
import { useEstimateMailDialogBoot } from './EstimateMailDialogBoot'; import { useEstimateMailDialogBoot } from './EstimateMailDialogBoot';
import { DialogsName } from '@/constants/dialogs'; import { DialogsName } from '@/constants/dialogs';
import withDialogActions from '@/containers/Dialog/withDialogActions'; import withDialogActions from '@/containers/Dialog/withDialogActions';
@@ -12,7 +13,6 @@ import {
transformMailFormToInitialValues, transformMailFormToInitialValues,
transformMailFormToRequest, transformMailFormToRequest,
} from '@/containers/SendMailNotification/utils'; } from '@/containers/SendMailNotification/utils';
import { Intent } from '@blueprintjs/core';
import { AppToaster } from '@/components'; import { AppToaster } from '@/components';
const initialFormValues = { const initialFormValues = {
@@ -25,11 +25,15 @@ interface EstimateMailFormValues extends MailNotificationFormValues {
} }
function EstimateMailDialogFormRoot({ function EstimateMailDialogFormRoot({
onFormSubmit,
onCancelClick,
// #withDialogClose // #withDialogClose
closeDialog, closeDialog,
}) { }) {
const { mutateAsync: sendEstimateMail } = useSendSaleEstimateMail(); const { mutateAsync: sendEstimateMail } = useSendSaleEstimateMail();
const { mailOptions, saleEstimateId } = useEstimateMailDialogBoot(); const { mailOptions, saleEstimateId, redirectToEstimatesList } =
useEstimateMailDialogBoot();
const initialValues = transformMailFormToInitialValues( const initialValues = transformMailFormToInitialValues(
mailOptions, mailOptions,
@@ -48,14 +52,16 @@ function EstimateMailDialogFormRoot({
}); });
closeDialog(DialogsName.EstimateMail); closeDialog(DialogsName.EstimateMail);
setSubmitting(false); setSubmitting(false);
onFormSubmit && onFormSubmit();
}) })
.catch((error) => { .catch(() => {
setSubmitting(false); setSubmitting(false);
closeDialog(DialogsName.EstimateMail); closeDialog(DialogsName.EstimateMail);
AppToaster.show({ AppToaster.show({
message: 'Something went wrong.', message: 'Something went wrong.',
intent: Intent.DANGER, intent: Intent.DANGER,
}); });
onCancelClick && onCancelClick();
}); });
}; };

View File

@@ -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 (
<Dialog
name={dialogName}
title={'Invoice Mail'}
isOpen={isOpen}
canEscapeJeyClose={false}
isCloseButtonShown={false}
autoFocus={true}
style={{ width: 600 }}
>
<DialogSuspense>
<InvoiceFormMailDeliverDialogContent
dialogName={dialogName}
invoiceId={invoiceId}
/>
</DialogSuspense>
</Dialog>
);
}
export default compose(withDialogRedux())(InvoiceFormMailDeliverDialog);

View File

@@ -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 (
<InvoiceMailDialogContent
invoiceId={invoiceId}
onFormSubmit={handleSubmit}
onCancelClick={handleCancel}
/>
);
}
export default R.compose(withDialogActions)(
InvoiceFormDeliverDialogContentRoot,
);

View File

@@ -1,8 +1,8 @@
// @ts-nocheck // @ts-nocheck
import React from 'react';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import InvoiceNumberDialog from '@/containers/Dialogs/InvoiceNumberDialog'; import InvoiceNumberDialog from '@/containers/Dialogs/InvoiceNumberDialog';
import { DialogsName } from '@/constants/dialogs'; import { DialogsName } from '@/constants/dialogs';
import InvoiceFormMailDeliverDialog from './Dialogs/InvoiceFormMailDeliverDialog/InvoiceFormMailDeliverDialog';
/** /**
* Invoice form dialogs. * Invoice form dialogs.
@@ -23,9 +23,14 @@ export default function InvoiceFormDialogs() {
}; };
return ( return (
<>
<InvoiceNumberDialog <InvoiceNumberDialog
dialogName={DialogsName.InvoiceNumberSettings} dialogName={DialogsName.InvoiceNumberSettings}
onConfirm={handleInvoiceNumberFormConfirm} onConfirm={handleInvoiceNumberFormConfirm}
/> />
<InvoiceFormMailDeliverDialog
dialogName={DialogsName.InvoiceFormMailDeliver}
/>
</>
); );
} }

View File

@@ -4,8 +4,8 @@ import { Dialog, DialogSuspense } from '@/components';
import withDialogRedux from '@/components/DialogReduxConnect'; import withDialogRedux from '@/components/DialogReduxConnect';
import { compose } from '@/utils'; import { compose } from '@/utils';
const InvoiceMailDialogContent = React.lazy( const InvoiceMailDialogBody = React.lazy(
() => import('./InvoiceMailDialogContent'), () => import('./InvoiceMailDialogBody'),
); );
/** /**
@@ -21,15 +21,13 @@ function InvoiceMailDialog({
name={dialogName} name={dialogName}
title={'Invoice Mail'} title={'Invoice Mail'}
isOpen={isOpen} isOpen={isOpen}
canEscapeJeyClose={true} canEscapeJeyClose={false}
isCloseButtonShown={false}
autoFocus={true} autoFocus={true}
style={{ width: 600 }} style={{ width: 600 }}
> >
<DialogSuspense> <DialogSuspense>
<InvoiceMailDialogContent <InvoiceMailDialogBody invoiceId={invoiceId} />
dialogName={dialogName}
invoiceId={invoiceId}
/>
</DialogSuspense> </DialogSuspense>
</Dialog> </Dialog>
); );

View File

@@ -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 (
<InvoiceMailDialogContent
invoiceId={invoiceId}
onCancelClick={handleCancelClick}
onFormSubmit={handleSubmitClick}
/>
);
}
export default R.compose(withDialogActions)(InvoiceMailDialogBodyRoot);

View File

@@ -6,12 +6,14 @@ import { DialogContent } from '@/components';
interface InvoiceMailDialogBootValues { interface InvoiceMailDialogBootValues {
invoiceId: number; invoiceId: number;
mailOptions: any; mailOptions: any;
redirectToInvoicesList: boolean;
} }
const InvoiceMailDialagBoot = createContext<InvoiceMailDialogBootValues>(); const InvoiceMailDialagBoot = createContext<InvoiceMailDialogBootValues>();
interface InvoiceMailDialogBootProps { interface InvoiceMailDialogBootProps {
invoiceId: number; invoiceId: number;
redirectToInvoicesList?: boolean;
children: React.ReactNode; children: React.ReactNode;
} }
@@ -20,6 +22,7 @@ interface InvoiceMailDialogBootProps {
*/ */
function InvoiceMailDialogBoot({ function InvoiceMailDialogBoot({
invoiceId, invoiceId,
redirectToInvoicesList,
...props ...props
}: InvoiceMailDialogBootProps) { }: InvoiceMailDialogBootProps) {
const { data: mailOptions, isLoading: isMailOptionsLoading } = const { data: mailOptions, isLoading: isMailOptionsLoading } =
@@ -29,6 +32,7 @@ function InvoiceMailDialogBoot({
saleInvoiceId: invoiceId, saleInvoiceId: invoiceId,
mailOptions, mailOptions,
isMailOptionsLoading, isMailOptionsLoading,
redirectToInvoicesList,
}; };
return ( return (

View File

@@ -1,17 +1,22 @@
import { InvoiceMailDialogBoot } from './InvoiceMailDialogBoot'; import { InvoiceMailDialogBoot } from './InvoiceMailDialogBoot';
import { InvoiceMailDialogForm } from './InvoiceMailDialogForm'; import { InvoiceMailDialogForm } from './InvoiceMailDialogForm';
interface InvoiceMailDialogContentProps { export interface InvoiceMailDialogContentProps {
dialogName: string;
invoiceId: number; invoiceId: number;
onFormSubmit?: () => void;
onCancelClick?: () => void;
} }
export default function InvoiceMailDialogContent({ export default function InvoiceMailDialogContent({
dialogName,
invoiceId, invoiceId,
onFormSubmit,
onCancelClick,
}: InvoiceMailDialogContentProps) { }: InvoiceMailDialogContentProps) {
return ( return (
<InvoiceMailDialogBoot invoiceId={invoiceId}> <InvoiceMailDialogBoot invoiceId={invoiceId}>
<InvoiceMailDialogForm /> <InvoiceMailDialogForm
onFormSubmit={onFormSubmit}
onCancelClick={onCancelClick}
/>
</InvoiceMailDialogBoot> </InvoiceMailDialogBoot>
); );
} }

View File

@@ -1,12 +1,9 @@
// @ts-nocheck // @ts-nocheck
import { Formik } from 'formik'; import { Formik } from 'formik';
import * as R from 'ramda';
import { Intent } from '@blueprintjs/core'; import { Intent } from '@blueprintjs/core';
import { useInvoiceMailDialogBoot } from './InvoiceMailDialogBoot'; import { useInvoiceMailDialogBoot } from './InvoiceMailDialogBoot';
import { DialogsName } from '@/constants/dialogs';
import { AppToaster } from '@/components'; import { AppToaster } from '@/components';
import { useSendSaleInvoiceMail } from '@/hooks/query'; import { useSendSaleInvoiceMail } from '@/hooks/query';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import { InvoiceMailDialogFormContent } from './InvoiceMailDialogFormContent'; import { InvoiceMailDialogFormContent } from './InvoiceMailDialogFormContent';
import { InvoiceMailFormSchema } from './InvoiceMailDialogForm.schema'; import { InvoiceMailFormSchema } from './InvoiceMailDialogForm.schema';
import { import {
@@ -25,10 +22,7 @@ interface InvoiceMailFormValues extends MailNotificationFormValues {
attachInvoice: boolean; attachInvoice: boolean;
} }
function InvoiceMailDialogFormRoot({ export function InvoiceMailDialogForm({ onFormSubmit, onCancelClick }) {
// #withDialogActions
closeDialog,
}) {
const { mailOptions, saleInvoiceId } = useInvoiceMailDialogBoot(); const { mailOptions, saleInvoiceId } = useInvoiceMailDialogBoot();
const { mutateAsync: sendInvoiceMail } = useSendSaleInvoiceMail(); const { mutateAsync: sendInvoiceMail } = useSendSaleInvoiceMail();
@@ -47,8 +41,8 @@ function InvoiceMailDialogFormRoot({
message: 'The mail notification has been sent successfully.', message: 'The mail notification has been sent successfully.',
intent: Intent.SUCCESS, intent: Intent.SUCCESS,
}); });
closeDialog(DialogsName.InvoiceMail);
setSubmitting(false); setSubmitting(false);
onFormSubmit && onFormSubmit(values);
}) })
.catch(() => { .catch(() => {
AppToaster.show({ AppToaster.show({
@@ -60,7 +54,7 @@ function InvoiceMailDialogFormRoot({
}; };
// Handle the close button click. // Handle the close button click.
const handleClose = () => { const handleClose = () => {
closeDialog(DialogsName.InvoiceMail); onCancelClick && onCancelClick();
}; };
return ( return (
@@ -73,7 +67,3 @@ function InvoiceMailDialogFormRoot({
</Formik> </Formik>
); );
} }
export const InvoiceMailDialogForm = R.compose(withDialogActions)(
InvoiceMailDialogFormRoot,
);

View File

@@ -1 +1,2 @@
export * from './InvoiceMailDialog'; export * from './InvoiceMailDialog';
export * from './InvoiceMailDialogContent';

View File

@@ -13,7 +13,12 @@ const PaymentMailDialogContent = React.lazy(
*/ */
function PaymentMailDialog({ function PaymentMailDialog({
dialogName, dialogName,
payload: { paymentReceiveId = null }, payload: {
paymentReceiveId = null,
// Redirects to the payments list on mail submitting.
redirectToPaymentsList = false,
},
isOpen, isOpen,
}) { }) {
return ( return (
@@ -29,6 +34,7 @@ function PaymentMailDialog({
<PaymentMailDialogContent <PaymentMailDialogContent
dialogName={dialogName} dialogName={dialogName}
paymentReceiveId={paymentReceiveId} paymentReceiveId={paymentReceiveId}
redirectToPaymentsList={redirectToPaymentsList}
/> />
</DialogSuspense> </DialogSuspense>
</Dialog> </Dialog>

View File

@@ -13,6 +13,7 @@ const PaymentMailDialogBootContext =
interface PaymentMailDialogBootProps { interface PaymentMailDialogBootProps {
paymentReceiveId: number; paymentReceiveId: number;
redirectToPaymentsList: boolean;
children: React.ReactNode; children: React.ReactNode;
} }
@@ -29,7 +30,8 @@ function PaymentMailDialogBoot({
const provider = { const provider = {
mailOptions, mailOptions,
isMailOptionsLoading, isMailOptionsLoading,
paymentReceiveId paymentReceiveId,
redirectToPaymentsList
}; };
return ( return (

View File

@@ -4,13 +4,18 @@ import { PaymentMailDialogForm } from './PaymentMailDialogForm';
interface PaymentMailDialogContentProps { interface PaymentMailDialogContentProps {
dialogName: string; dialogName: string;
paymentReceiveId: number; paymentReceiveId: number;
redirectToPaymentsList: boolean;
} }
export default function PaymentMailDialogContent({ export default function PaymentMailDialogContent({
dialogName, dialogName,
paymentReceiveId, paymentReceiveId,
redirectToPaymentsList,
}: PaymentMailDialogContentProps) { }: PaymentMailDialogContentProps) {
return ( return (
<PaymentMailDialogBoot paymentReceiveId={paymentReceiveId}> <PaymentMailDialogBoot
paymentReceiveId={paymentReceiveId}
redirectToPaymentsList={redirectToPaymentsList}
>
<PaymentMailDialogForm /> <PaymentMailDialogForm />
</PaymentMailDialogBoot> </PaymentMailDialogBoot>
); );

View File

@@ -3,7 +3,6 @@ import { Formik, FormikBag } from 'formik';
import * as R from 'ramda'; import * as R from 'ramda';
import { Intent } from '@blueprintjs/core'; import { Intent } from '@blueprintjs/core';
import { usePaymentMailDialogBoot } from './PaymentMailDialogBoot'; import { usePaymentMailDialogBoot } from './PaymentMailDialogBoot';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import { DialogsName } from '@/constants/dialogs'; import { DialogsName } from '@/constants/dialogs';
import { useSendPaymentReceiveMail } from '@/hooks/query'; import { useSendPaymentReceiveMail } from '@/hooks/query';
import { PaymentMailDialogFormContent } from './PaymentMailDialogFormContent'; import { PaymentMailDialogFormContent } from './PaymentMailDialogFormContent';
@@ -14,6 +13,8 @@ import {
transformMailFormToInitialValues, transformMailFormToInitialValues,
} from '@/containers/SendMailNotification/utils'; } from '@/containers/SendMailNotification/utils';
import { AppToaster } from '@/components'; import { AppToaster } from '@/components';
import { useHistory } from 'react-router-dom';
import withDialogActions from '@/containers/Dialog/withDialogActions';
const initialFormValues = { const initialFormValues = {
...initialMailNotificationValues, ...initialMailNotificationValues,
@@ -28,9 +29,12 @@ export function PaymentMailDialogFormRoot({
// #withDialogActions // #withDialogActions
closeDialog, closeDialog,
}) { }) {
const { mailOptions, paymentReceiveId } = usePaymentMailDialogBoot(); const { mailOptions, paymentReceiveId, redirectToPaymentsList } =
usePaymentMailDialogBoot();
const { mutateAsync: sendPaymentMail } = useSendPaymentReceiveMail(); const { mutateAsync: sendPaymentMail } = useSendPaymentReceiveMail();
const history = useHistory();
const initialValues = transformMailFormToInitialValues( const initialValues = transformMailFormToInitialValues(
mailOptions, mailOptions,
initialFormValues, initialFormValues,
@@ -51,6 +55,11 @@ export function PaymentMailDialogFormRoot({
}); });
setSubmitting(false); setSubmitting(false);
closeDialog(DialogsName.PaymentMail); closeDialog(DialogsName.PaymentMail);
// Redirects to payments list if the option is enabled.
if (redirectToPaymentsList) {
history.push('/payment-receives');
}
}) })
.catch(() => { .catch(() => {
AppToaster.show({ AppToaster.show({

View File

@@ -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 (
<Dialog
name={dialogName}
title={'Receipt Mail'}
isOpen={isOpen}
canEscapeJeyClose={false}
isCloseButtonShown={false}
autoFocus={true}
style={{ width: 600 }}
>
<DialogSuspense>
<ReceiptFormMailDeliverDialogContent
dialogName={dialogName}
receiptId={receiptId}
/>
</DialogSuspense>
</Dialog>
);
}
export default compose(withDialogRedux())(ReceiptFormMailDeliverDialog);

View File

@@ -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 (
<ReceiptMailDialogContent
receiptId={receiptId}
onFormSubmit={handleSubmit}
onCancelClick={handleCancel}
/>
);
}
export default R.compose(withDialogActions)(
ReceiptFormDeliverDialogContentRoot,
);

View File

@@ -2,6 +2,8 @@
import React from 'react'; import React from 'react';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import ReceiptNumberDialog from '@/containers/Dialogs/ReceiptNumberDialog'; import ReceiptNumberDialog from '@/containers/Dialogs/ReceiptNumberDialog';
import ReceiptFormMailDeliverDialog from './Dialogs/ReceiptFormMailDeliverDialog';
import { DialogsName } from '@/constants/dialogs';
/** /**
* Receipt form dialogs. * Receipt form dialogs.
@@ -27,6 +29,9 @@ export default function ReceiptFormDialogs() {
dialogName={'receipt-number-form'} dialogName={'receipt-number-form'}
onConfirm={handleReceiptNumberFormConfirm} onConfirm={handleReceiptNumberFormConfirm}
/> />
<ReceiptFormMailDeliverDialog
dialogName={DialogsName.ReceiptFormMailDeliver}
/>
</> </>
); );
} }

View File

@@ -4,12 +4,12 @@ import { Dialog, DialogSuspense } from '@/components';
import withDialogRedux from '@/components/DialogReduxConnect'; import withDialogRedux from '@/components/DialogReduxConnect';
import { compose } from '@/utils'; import { compose } from '@/utils';
const ReceiptMailDialogContent = React.lazy( const ReceiptMailDialogBody = React.lazy(
() => import('./ReceiptMailDialogContent'), () => import('./ReceiptMailDialogBody'),
); );
/** /**
* Invoice mail dialog. * Receipt mail dialog.
*/ */
function ReceiptMailDialog({ function ReceiptMailDialog({
dialogName, dialogName,
@@ -26,10 +26,7 @@ function ReceiptMailDialog({
style={{ width: 600 }} style={{ width: 600 }}
> >
<DialogSuspense> <DialogSuspense>
<ReceiptMailDialogContent <ReceiptMailDialogBody receiptId={receiptId} />
dialogName={dialogName}
receiptId={receiptId}
/>
</DialogSuspense> </DialogSuspense>
</Dialog> </Dialog>
); );

View File

@@ -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 (
<ReceiptMailDialogContent
receiptId={receiptId}
onFormSubmit={handleSubmitClick}
onCancelClick={handleCancelClick}
/>
);
}
export default R.compose(withDialogActions)(ReceiptMailDialogBodyRoot);

View File

@@ -6,6 +6,7 @@ import { DialogContent } from '@/components';
interface ReceiptMailDialogBootValues { interface ReceiptMailDialogBootValues {
receiptId: number; receiptId: number;
mailOptions: any; mailOptions: any;
redirectToReceiptsList: boolean;
} }
const ReceiptMailDialogBootContext = const ReceiptMailDialogBootContext =
@@ -14,6 +15,7 @@ const ReceiptMailDialogBootContext =
interface ReceiptMailDialogBootProps { interface ReceiptMailDialogBootProps {
receiptId: number; receiptId: number;
children: React.ReactNode; children: React.ReactNode;
redirectToReceiptsList?: boolean;
} }
/** /**
@@ -21,6 +23,7 @@ interface ReceiptMailDialogBootProps {
*/ */
function ReceiptMailDialogBoot({ function ReceiptMailDialogBoot({
receiptId, receiptId,
redirectToReceiptsList = false,
...props ...props
}: ReceiptMailDialogBootProps) { }: ReceiptMailDialogBootProps) {
const { data: mailOptions, isLoading: isMailOptionsLoading } = const { data: mailOptions, isLoading: isMailOptionsLoading } =
@@ -30,6 +33,7 @@ function ReceiptMailDialogBoot({
saleReceiptId: receiptId, saleReceiptId: receiptId,
mailOptions, mailOptions,
isMailOptionsLoading, isMailOptionsLoading,
redirectToReceiptsList,
}; };
return ( return (

View File

@@ -2,17 +2,22 @@ import React from 'react';
import { ReceiptMailDialogBoot } from './ReceiptMailDialogBoot'; import { ReceiptMailDialogBoot } from './ReceiptMailDialogBoot';
import { ReceiptMailDialogForm } from './ReceiptMailDialogForm'; import { ReceiptMailDialogForm } from './ReceiptMailDialogForm';
interface ReceiptMailDialogContentProps { export interface ReceiptMailDialogContentProps {
dialogName: string
receiptId: number; receiptId: number;
onFormSubmit?: () => void;
onCancelClick?: () => void;
} }
export default function ReceiptMailDialogContent({ export default function ReceiptMailDialogContent({
dialogName,
receiptId, receiptId,
onFormSubmit,
onCancelClick
}: ReceiptMailDialogContentProps) { }: ReceiptMailDialogContentProps) {
return ( return (
<ReceiptMailDialogBoot receiptId={receiptId}> <ReceiptMailDialogBoot receiptId={receiptId}>
<ReceiptMailDialogForm /> <ReceiptMailDialogForm
onFormSubmit={onFormSubmit}
onCancelClick={onCancelClick}
/>
</ReceiptMailDialogBoot> </ReceiptMailDialogBoot>
); );
} }

View File

@@ -23,7 +23,16 @@ interface ReceiptMailFormValues extends MailNotificationFormValues {
attachReceipt: boolean; attachReceipt: boolean;
} }
function ReceiptMailDialogFormRoot({ closeDialog }) { interface ReceiptMailDialogFormProps {
onFormSubmit?: () => void;
onCancelClick?: () => void;
}
export function ReceiptMailDialogForm({
// #props
onFormSubmit,
onCancelClick,
}: ReceiptMailDialogFormProps) {
const { mailOptions, saleReceiptId } = useReceiptMailDialogBoot(); const { mailOptions, saleReceiptId } = useReceiptMailDialogBoot();
const { mutateAsync: sendReceiptMail } = useSendSaleReceiptMail(); const { mutateAsync: sendReceiptMail } = useSendSaleReceiptMail();
@@ -46,8 +55,8 @@ function ReceiptMailDialogFormRoot({ closeDialog }) {
message: 'The mail notification has been sent successfully.', message: 'The mail notification has been sent successfully.',
intent: Intent.SUCCESS, intent: Intent.SUCCESS,
}); });
closeDialog(DialogsName.ReceiptMail);
setSubmitting(false); setSubmitting(false);
onFormSubmit && onFormSubmit(values);
}) })
.catch(() => { .catch(() => {
AppToaster.show({ AppToaster.show({
@@ -59,7 +68,7 @@ function ReceiptMailDialogFormRoot({ closeDialog }) {
}; };
// Handle the close button click. // Handle the close button click.
const handleClose = () => { const handleClose = () => {
closeDialog(DialogsName.ReceiptMail); onCancelClick && onCancelClick();
}; };
return ( return (
@@ -68,7 +77,3 @@ function ReceiptMailDialogFormRoot({ closeDialog }) {
</Formik> </Formik>
); );
} }
export const ReceiptMailDialogForm = R.compose(withDialogActions)(
ReceiptMailDialogFormRoot,
);