feat: add delete route for the invoice
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Body, Controller, Get, Param, Patch, Post, Query } from "@nestjs/common";
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from "@nestjs/common";
|
||||
import { ApiOperation } from "@nestjs/swagger";
|
||||
|
||||
import { ApplyDiscountDto } from "./DTO/apply-discount.dto";
|
||||
@@ -34,6 +34,13 @@ export class InvoicesController {
|
||||
return this.invoiceService.updateInvoiceAdmin(paramDto.id, updateDto);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "delete an invoice ==> admin route" })
|
||||
@PermissionsDec(PermissionEnum.INVOICES)
|
||||
@Delete(":id")
|
||||
deleteInvoice(@Param() paramDto: ParamDto) {
|
||||
return this.invoiceService.deleteInvoiceAdmin(paramDto.id);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "get all invoices ==> admin route" })
|
||||
@PermissionsDec(PermissionEnum.INVOICES)
|
||||
@Get()
|
||||
|
||||
@@ -202,6 +202,105 @@ export class InvoicesService {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
//********************************** */
|
||||
|
||||
async deleteInvoiceAdmin(invoiceId: string) {
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
|
||||
try {
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
const invoice = await queryRunner.manager.findOne(this.invoiceRepository.target, {
|
||||
where: { id: invoiceId },
|
||||
});
|
||||
|
||||
if (!invoice) throw new BadRequestException(InvoiceMessage.NOT_FOUND_BY_ID);
|
||||
|
||||
this.logger.log(`Deleting invoice: ${invoiceId} (numeric ID: ${invoice.numericId}, status: ${invoice.status})`);
|
||||
|
||||
this.logger.log(`Starting cleanup of queue jobs for invoice: ${invoiceId}`);
|
||||
await this.cleanupInvoiceQueueJobs(invoiceId);
|
||||
this.logger.log(`Completed cleanup of queue jobs for invoice: ${invoiceId}`);
|
||||
|
||||
await queryRunner.manager.remove(this.invoiceRepository.target, invoice);
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
return {
|
||||
message: InvoiceMessage.DELETED,
|
||||
};
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up all queue jobs related to a specific invoice
|
||||
*/
|
||||
private async cleanupInvoiceQueueJobs(invoiceId: string): Promise<void> {
|
||||
try {
|
||||
this.logger.log(`Starting cleanup of queue jobs for invoice: ${invoiceId}`);
|
||||
|
||||
this.logger.log(`Removing recurring invoice jobs for invoice: ${invoiceId}`);
|
||||
await this.removeRecurringInvoiceJobs(invoiceId);
|
||||
|
||||
this.logger.log(`Removing reminder invoice jobs for invoice: ${invoiceId}`);
|
||||
await this.removeReminderInvoiceJobs(invoiceId);
|
||||
|
||||
this.logger.log(`Removing admin notification jobs for invoice: ${invoiceId}`);
|
||||
await this.removeAdminNotificationJobs(invoiceId);
|
||||
|
||||
this.logger.log(`Checking for additional main queue jobs for invoice: ${invoiceId}`);
|
||||
const mainQueueJobs = await this.invoiceQueue.getJobs(["active", "waiting", "delayed", "failed", "completed"]);
|
||||
|
||||
let mainJobsRemoved = 0;
|
||||
for (const job of mainQueueJobs) {
|
||||
const jobData = job.data;
|
||||
if (jobData && (jobData.invoiceId === invoiceId || (jobData.invoice && jobData.invoice.id === invoiceId))) {
|
||||
if (
|
||||
job.name !== INVOICE.RECURRING_JOB_NAME &&
|
||||
job.name !== INVOICE.REMINDER_JOB_NAME &&
|
||||
job.name !== INVOICE.SUBSCRIPTION_ADMIN_NOTIFICATION_JOB_NAME
|
||||
) {
|
||||
await job.remove();
|
||||
mainJobsRemoved++;
|
||||
this.logger.log(`Removed additional invoice queue job: ${job.name} (ID: ${job.id}) for invoice: ${invoiceId}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.log(`Checking for external invoice queue jobs for invoice: ${invoiceId}`);
|
||||
const externalQueueJobs = await this.externalInvoiceQueue.getJobs(["active", "waiting", "delayed", "failed", "completed"]);
|
||||
|
||||
let externalJobsRemoved = 0;
|
||||
for (const job of externalQueueJobs) {
|
||||
const jobData = job.data;
|
||||
if (
|
||||
jobData &&
|
||||
(jobData.invoiceId === invoiceId ||
|
||||
(jobData.invoice && jobData.invoice.id === invoiceId) ||
|
||||
(jobData.invoice && jobData.invoice.numericId === invoiceId))
|
||||
) {
|
||||
await job.remove();
|
||||
externalJobsRemoved++;
|
||||
this.logger.log(`Removed external invoice queue job: ${job.name} (ID: ${job.id}) for invoice: ${invoiceId}`);
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.log(
|
||||
`Successfully cleaned up queue jobs for invoice: ${invoiceId}. Main jobs removed: ${mainJobsRemoved}, External jobs removed: ${externalJobsRemoved}`,
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.error(`Error cleaning up queue jobs for invoice ${invoiceId}:`, error);
|
||||
// don't throw error here to avoid blocking invoice deletion
|
||||
}
|
||||
}
|
||||
|
||||
//********************************** */
|
||||
|
||||
async approveInvoiceRequest(invoiceId: string, userId: string) {
|
||||
@@ -922,6 +1021,7 @@ export class InvoicesService {
|
||||
//
|
||||
await this.removeRecurringInvoiceJobs(invoice.id);
|
||||
await this.removeReminderInvoiceJobs(invoice.id);
|
||||
await this.removeAdminNotificationJobs(invoice.id);
|
||||
|
||||
if (updateDto.isRecurring) {
|
||||
if (!updateDto.recurringPeriod) throw new BadRequestException(InvoiceMessage.RECURRING_PERIOD_REQUIRED);
|
||||
@@ -964,21 +1064,52 @@ export class InvoicesService {
|
||||
|
||||
private async removeRecurringInvoiceJobs(invoiceId: string) {
|
||||
const existingJobs = await this.invoiceQueue.getJobs(["delayed", "waiting", "active"]);
|
||||
let removedCount = 0;
|
||||
|
||||
for (const job of existingJobs) {
|
||||
if (job.name === INVOICE.RECURRING_JOB_NAME && job.data.invoiceId === invoiceId) {
|
||||
if (job.name === INVOICE.RECURRING_JOB_NAME && job.data?.invoiceId === invoiceId) {
|
||||
await job.remove();
|
||||
removedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (removedCount > 0) {
|
||||
this.logger.log(`Removed ${removedCount} recurring invoice jobs for invoice: ${invoiceId}`);
|
||||
}
|
||||
}
|
||||
//*********************************** */
|
||||
|
||||
private async removeReminderInvoiceJobs(invoiceId: string) {
|
||||
const existingJobs = await this.invoiceQueue.getJobs(["delayed", "waiting", "active"]);
|
||||
let removedCount = 0;
|
||||
|
||||
for (const job of existingJobs) {
|
||||
if (job.name === INVOICE.REMINDER_JOB_NAME && job.data.invoiceId === invoiceId) {
|
||||
if (job.name === INVOICE.REMINDER_JOB_NAME && job.data?.invoiceId === invoiceId) {
|
||||
await job.remove();
|
||||
removedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (removedCount > 0) {
|
||||
this.logger.log(`Removed ${removedCount} reminder invoice jobs for invoice: ${invoiceId}`);
|
||||
}
|
||||
}
|
||||
|
||||
//*********************************** */
|
||||
private async removeAdminNotificationJobs(invoiceId: string) {
|
||||
const existingJobs = await this.invoiceQueue.getJobs(["delayed", "waiting", "active"]);
|
||||
let removedCount = 0;
|
||||
|
||||
for (const job of existingJobs) {
|
||||
if (job.name === INVOICE.SUBSCRIPTION_ADMIN_NOTIFICATION_JOB_NAME && job.data?.invoiceId === invoiceId) {
|
||||
await job.remove();
|
||||
removedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (removedCount > 0) {
|
||||
this.logger.log(`Removed ${removedCount} admin notification jobs for invoice: ${invoiceId}`);
|
||||
}
|
||||
}
|
||||
|
||||
///********************************** */
|
||||
|
||||
Reference in New Issue
Block a user