feat: add delete route for the invoice

This commit is contained in:
mahyargdz
2025-08-21 11:35:51 +03:30
parent 6c1f8d6358
commit 1d76138ead
5 changed files with 161 additions and 4 deletions
@@ -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}`);
}
}
///********************************** */