fix: sending mail jobs

This commit is contained in:
Ahmed Bouhuolia
2025-12-05 00:07:26 +02:00
parent 32d74b0413
commit c3dc26a1e4
11 changed files with 100 additions and 31 deletions

View File

@@ -1,12 +1,11 @@
import { JOB_REF, Process, Processor } from '@nestjs/bull';
import { Job } from 'bull';
import { Inject, Scope } from '@nestjs/common';
import { ClsService, UseCls } from 'nestjs-cls';
import {
SEND_PAYMENT_RECEIVED_MAIL_JOB,
SEND_PAYMENT_RECEIVED_MAIL_QUEUE,
} from '../constants';
import { Inject, Scope } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { ClsService } from 'nestjs-cls';
import { SendPaymentReceiveMailNotification } from '../commands/PaymentReceivedMailNotification';
import { SendPaymentReceivedMailPayload } from '../types/PaymentReceived.types';
@@ -21,9 +20,10 @@ export class SendPaymentReceivedMailProcessor {
@Inject(JOB_REF)
private readonly jobRef: Job<SendPaymentReceivedMailPayload>,
) {}
) { }
@Process(SEND_PAYMENT_RECEIVED_MAIL_JOB)
@UseCls()
async handleSendMail() {
const { messageOptions, paymentReceivedId, organizationId, userId } =
this.jobRef.data;
@@ -37,7 +37,8 @@ export class SendPaymentReceivedMailProcessor {
messageOptions,
);
} catch (error) {
console.log(error);
console.error('Failed to process payment received mail job:', error);
throw error;
}
}
}

View File

@@ -42,6 +42,7 @@ import { GetSaleEstimateMailTemplateService } from './queries/GetSaleEstimateMai
import { SaleEstimateAutoIncrementSubscriber } from './subscribers/SaleEstimateAutoIncrementSubscriber';
import { BulkDeleteSaleEstimatesService } from './BulkDeleteSaleEstimates.service';
import { ValidateBulkDeleteSaleEstimatesService } from './ValidateBulkDeleteSaleEstimates.service';
import { SendSaleEstimateMailProcess } from './processes/SendSaleEstimateMail.process';
@Module({
imports: [
@@ -89,6 +90,7 @@ import { ValidateBulkDeleteSaleEstimatesService } from './ValidateBulkDeleteSale
SaleEstimateAutoIncrementSubscriber,
BulkDeleteSaleEstimatesService,
ValidateBulkDeleteSaleEstimatesService,
SendSaleEstimateMailProcess,
],
exports: [
SaleEstimatesExportable,

View File

@@ -24,6 +24,7 @@ import { Mail } from '@/modules/Mail/Mail';
import { MailTransporter } from '@/modules/Mail/MailTransporter.service';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { GetSaleEstimateMailTemplateService } from '../queries/GetSaleEstimateMailTemplate.service';
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
@Injectable()
export class SendSaleEstimateMail {
@@ -42,13 +43,14 @@ export class SendSaleEstimateMail {
private readonly getEstimateMailTemplate: GetSaleEstimateMailTemplateService,
private readonly eventPublisher: EventEmitter2,
private readonly mailTransporter: MailTransporter,
private readonly tenancyContext: TenancyContext,
@Inject(SaleEstimate.name)
private readonly saleEstimateModel: TenantModelProxy<typeof SaleEstimate>,
@InjectQueue(SendSaleEstimateMailQueue)
private readonly sendEstimateMailQueue: Queue,
) {}
) { }
/**
* Triggers the reminder mail of the given sale estimate.
@@ -60,10 +62,19 @@ export class SendSaleEstimateMail {
saleEstimateId: number,
messageOptions: SaleEstimateMailOptionsDTO,
): Promise<void> {
const tenant = await this.tenancyContext.getTenant();
const user = await this.tenancyContext.getSystemUser();
const organizationId = tenant.organizationId;
const userId = user.id;
const payload = {
saleEstimateId,
messageOptions,
userId,
organizationId,
};
await this.sendEstimateMailQueue.add(SendSaleEstimateMailJob, payload);
// Triggers `onSaleEstimatePreMailSend` event.

View File

@@ -1,19 +1,39 @@
import { Process, Processor } from '@nestjs/bull';
import { Job } from 'bull';
import { Inject, Scope } from '@nestjs/common';
import { JOB_REF } from '@nestjs/bull';
import {
SendSaleEstimateMailJob,
SendSaleEstimateMailQueue,
} from '../types/SaleEstimates.types';
import { SendSaleEstimateMail } from '../commands/SendSaleEstimateMail';
import { ClsService, UseCls } from 'nestjs-cls';
@Processor(SendSaleEstimateMailQueue)
@Processor({
name: SendSaleEstimateMailQueue,
scope: Scope.REQUEST,
})
export class SendSaleEstimateMailProcess {
constructor(private readonly sendEstimateMailService: SendSaleEstimateMail) {}
constructor(
private readonly sendEstimateMailService: SendSaleEstimateMail,
private readonly clsService: ClsService,
@Inject(JOB_REF)
private readonly jobRef: Job,
) { }
@Process(SendSaleEstimateMailJob)
async handleSendMail(job: Job) {
const { saleEstimateId, messageOptions } = job.data;
@UseCls()
async handleSendMail() {
const { saleEstimateId, messageOptions, organizationId, userId } = this.jobRef.data;
await this.sendEstimateMailService.sendMail(saleEstimateId, messageOptions);
this.clsService.set('organizationId', organizationId);
this.clsService.set('userId', userId);
try {
await this.sendEstimateMailService.sendMail(saleEstimateId, messageOptions);
} catch (error) {
console.error('Failed to process estimate mail job:', error);
throw error;
}
}
}

View File

@@ -99,7 +99,8 @@ export class SaleInvoicesController {
return this.saleInvoiceApplication.createSaleInvoice(saleInvoiceDTO);
}
@Put(':id/mail')
@Post(':id/mail')
@HttpCode(200)
@ApiOperation({ summary: 'Send the sale invoice mail.' })
@ApiResponse({
status: 200,

View File

@@ -33,7 +33,7 @@ export class SendSaleInvoiceMail {
private readonly tenancyContect: TenancyContext,
@InjectQueue(SendSaleInvoiceQueue) private readonly sendInvoiceQueue: Queue,
) {}
) { }
/**
* Sends the invoice mail of the given sale invoice.
@@ -132,7 +132,13 @@ export class SendSaleInvoiceMail {
events.saleInvoice.onMailSend,
eventPayload,
);
await this.mailTransporter.send(mail);
try {
await this.mailTransporter.send(mail);
} catch (error) {
console.error('Failed to send invoice mail:', error);
throw error;
}
// Triggers the event `onSaleInvoiceSend`.
await this.eventEmitter.emitAsync(

View File

@@ -18,9 +18,10 @@ export class SendSaleInvoiceMailProcessor {
@Inject(JOB_REF)
private readonly jobRef: Job<SendSaleInvoiceMailJobPayload>,
private readonly clsService: ClsService,
) {}
) { }
@Process(SendSaleInvoiceMailJob)
@UseCls()
async handleSendInvoice() {
const { messageOptions, saleInvoiceId, organizationId, userId } =
this.jobRef.data;
@@ -31,7 +32,8 @@ export class SendSaleInvoiceMailProcessor {
try {
await this.sendSaleInvoiceMail.sendMail(saleInvoiceId, messageOptions);
} catch (error) {
console.log(error);
console.error('Failed to process invoice mail job:', error);
throw error;
}
}
}

View File

@@ -25,7 +25,10 @@ import {
CreateSaleReceiptDto,
EditSaleReceiptDto,
} from './dtos/SaleReceipt.dto';
import { ISalesReceiptsFilter } from './types/SaleReceipts.types';
import {
ISalesReceiptsFilter,
SaleReceiptMailOptsDTO,
} from './types/SaleReceipts.types';
import { AcceptType } from '@/constants/accept-type';
import { Response } from 'express';
import { SaleReceiptResponseDto } from './dtos/SaleReceiptResponse.dto';
@@ -87,7 +90,7 @@ export class SaleReceiptsController {
return this.saleReceiptApplication.createSaleReceipt(saleReceiptDTO);
}
@Put(':id/mail')
@Post(':id/mail')
@HttpCode(200)
@ApiOperation({ summary: 'Send the sale receipt mail.' })
@ApiParam({
@@ -96,8 +99,11 @@ export class SaleReceiptsController {
type: Number,
description: 'The sale receipt id',
})
sendSaleReceiptMail(@Param('id', ParseIntPipe) id: number) {
return this.saleReceiptApplication.getSaleReceiptMail(id);
sendSaleReceiptMail(
@Param('id', ParseIntPipe) id: number,
@Body() messageOpts: SaleReceiptMailOptsDTO,
) {
return this.saleReceiptApplication.sendSaleReceiptMail(id, messageOpts);
}
@Get('state')

View File

@@ -1,24 +1,42 @@
import { Process, Processor } from '@nestjs/bull';
import { Job } from 'bull';
import { SendSaleReceiptMailQueue } from '../constants';
import { Inject, Scope } from '@nestjs/common';
import { JOB_REF } from '@nestjs/bull';
import { SendSaleReceiptMailQueue, SendSaleReceiptMailJob } from '../constants';
import { SaleReceiptMailNotification } from '../commands/SaleReceiptMailNotification';
import { SaleReceiptSendMailPayload } from '../types/SaleReceipts.types';
import { ClsService } from 'nestjs-cls';
import { ClsService, UseCls } from 'nestjs-cls';
@Processor(SendSaleReceiptMailQueue)
@Processor({
name: SendSaleReceiptMailQueue,
scope: Scope.REQUEST,
})
export class SendSaleReceiptMailProcess {
constructor(
private readonly saleReceiptMailNotification: SaleReceiptMailNotification,
private readonly clsService: ClsService,
) {}
@Process(SendSaleReceiptMailQueue)
async handleSendMailJob(job: Job<SaleReceiptSendMailPayload>) {
const { messageOpts, saleReceiptId, organizationId, userId } = job.data;
@Inject(JOB_REF)
private readonly jobRef: Job<SaleReceiptSendMailPayload>,
) { }
@Process(SendSaleReceiptMailJob)
@UseCls()
async handleSendMailJob() {
const { messageOpts, saleReceiptId, organizationId, userId } =
this.jobRef.data;
this.clsService.set('organizationId', organizationId);
this.clsService.set('userId', userId);
await this.saleReceiptMailNotification.sendMail(saleReceiptId, messageOpts);
try {
await this.saleReceiptMailNotification.sendMail(
saleReceiptId,
messageOpts,
);
} catch (error) {
console.error('Failed to process receipt mail job:', error);
throw error;
}
}
}

View File

@@ -21,9 +21,10 @@ export class SendInviteUserMailProcessor {
@Inject(JOB_REF)
private readonly jobRef: Job<SendInviteUserMailJobPayload>,
private readonly clsService: ClsService,
) {}
) { }
@Process(SendInviteUserMailJob)
@UseCls()
async handleSendInviteMail() {
const { fromUser, invite, organizationId, userId } = this.jobRef.data;
@@ -33,7 +34,8 @@ export class SendInviteUserMailProcessor {
try {
await this.sendInviteUsersMailService.sendInviteMail(fromUser, invite);
} catch (error) {
console.log(error);
console.error('Failed to process invite user mail job:', error);
throw error;
}
}
}