update: the invoice update by admin logic
This commit is contained in:
@@ -50,30 +50,6 @@ export class InvoicesService {
|
||||
|
||||
///********************************** */
|
||||
|
||||
private validateInvoiceItems(items: InvoiceItemDto[]): void {
|
||||
if (!items || items.length === 0) throw new BadRequestException(InvoiceMessage.ITEMS_REQUIRED);
|
||||
|
||||
items.forEach((item) => {
|
||||
if (item.unitPrice <= 0) throw new BadRequestException(InvoiceMessage.UNIT_PRICE_MUST_BE_POSITIVE);
|
||||
if (item.count <= 0) throw new BadRequestException(InvoiceMessage.COUNT_MUST_BE_POSITIVE);
|
||||
if (item.discount && (item.discount < 0 || item.discount > 100)) {
|
||||
throw new BadRequestException(InvoiceMessage.DISCOUNT_MUST_BE_BETWEEN_0_AND_100);
|
||||
}
|
||||
});
|
||||
}
|
||||
///********************************** */
|
||||
|
||||
private validateRecurringInvoice(createDto: CreateInvoiceDto): void {
|
||||
if (createDto.isRecurring) {
|
||||
if (!createDto.recurringPeriod) throw new BadRequestException(InvoiceMessage.RECURRING_PERIOD_REQUIRED);
|
||||
|
||||
if (createDto.maxRecurringCycles && createDto.maxRecurringCycles < 1) {
|
||||
throw new BadRequestException(InvoiceMessage.MAX_RECURRING_CYCLES_MUST_BE_POSITIVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
///********************************** */
|
||||
|
||||
async createInvoiceAdmin(createDto: CreateInvoiceDto) {
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
|
||||
@@ -167,6 +143,8 @@ export class InvoicesService {
|
||||
if (invoice.status !== InvoiceStatus.PENDING) throw new BadRequestException(InvoiceMessage.INVOICE_CAN_NOT_UPDATE);
|
||||
|
||||
if (updateDto.items) {
|
||||
this.validateInvoiceItems(updateDto.items);
|
||||
|
||||
const invoiceItemsData = updateDto.items.map((item) => ({
|
||||
name: item.name,
|
||||
count: item.count,
|
||||
@@ -176,6 +154,9 @@ export class InvoicesService {
|
||||
}));
|
||||
// calculate total price and tax
|
||||
const totalPrice = invoiceItemsData.reduce((sum, item) => new Decimal(item.totalPrice).add(sum), new Decimal(0));
|
||||
|
||||
if (totalPrice.lessThanOrEqualTo(0)) throw new BadRequestException(InvoiceMessage.TOTAL_PRICE_MUST_BE_POSITIVE);
|
||||
|
||||
const tax = totalPrice.mul(0.1);
|
||||
invoice.totalPrice = totalPrice.add(tax);
|
||||
invoice.tax = tax;
|
||||
@@ -194,6 +175,15 @@ export class InvoicesService {
|
||||
invoice.user = user;
|
||||
}
|
||||
|
||||
if (updateDto.isRecurring) {
|
||||
this.validateRecurringInvoice(updateDto);
|
||||
await this.updateScheduleInvoiceJobs(invoice, updateDto);
|
||||
|
||||
invoice.isRecurring = updateDto.isRecurring;
|
||||
invoice.recurringPeriod = updateDto.recurringPeriod;
|
||||
invoice.maxRecurringCycles = updateDto.maxRecurringCycles ? updateDto.maxRecurringCycles : invoice.maxRecurringCycles;
|
||||
}
|
||||
|
||||
// Save the updated invoice
|
||||
await queryRunner.manager.save(this.invoiceRepository.target, invoice);
|
||||
|
||||
@@ -678,34 +668,6 @@ export class InvoicesService {
|
||||
return invoiceCount;
|
||||
}
|
||||
|
||||
//********************************** */
|
||||
|
||||
private async calculateRecurringDelay(recurringPeriod: RecurringPeriodEnum) {
|
||||
let delayMs: number;
|
||||
switch (recurringPeriod) {
|
||||
case RecurringPeriodEnum.WEEKLY:
|
||||
delayMs = dayjs().add(1, "week").subtract(INVOICE.DAYS_BEFORE_OVERDUE, "day").diff(dayjs());
|
||||
break;
|
||||
case RecurringPeriodEnum.MONTHLY:
|
||||
delayMs = dayjs().add(1, "month").subtract(7, "day").diff(dayjs());
|
||||
break;
|
||||
case RecurringPeriodEnum.QUARTERLY:
|
||||
delayMs = dayjs().add(3, "month").subtract(7, "day").diff(dayjs());
|
||||
break;
|
||||
case RecurringPeriodEnum.BIANNUALLY:
|
||||
delayMs = dayjs().add(6, "month").subtract(7, "day").diff(dayjs());
|
||||
break;
|
||||
case RecurringPeriodEnum.ANNUALLY:
|
||||
delayMs = dayjs().add(1, "year").subtract(7, "day").diff(dayjs());
|
||||
break;
|
||||
default:
|
||||
delayMs = dayjs().add(1, "month").subtract(7, "day").diff(dayjs());
|
||||
}
|
||||
|
||||
this.logger.log(`Calculated recurring delay: ${delayMs}ms for period: ${recurringPeriod}`);
|
||||
return delayMs;
|
||||
}
|
||||
|
||||
//*********************************** */
|
||||
async applyDiscount(invoiceId: string, discountCode: string, userId: string) {
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
@@ -800,7 +762,7 @@ export class InvoicesService {
|
||||
}
|
||||
|
||||
//*********************************** */
|
||||
private async scheduleInvoiceJobs(invoice: Invoice, createDto?: CreateInvoiceDto) {
|
||||
private async scheduleInvoiceJobs(invoice: Invoice, createDto?: CreateInvoiceDto | UpdateInvoiceDto) {
|
||||
// Add to queue for billing reminder
|
||||
await this.invoiceQueue.add(
|
||||
INVOICE.REMINDER_JOB_NAME,
|
||||
@@ -834,6 +796,25 @@ export class InvoicesService {
|
||||
this.logger.log(`Scheduled recurring invoice for user ${createDto.userId} with interval ${createDto.recurringPeriod}`);
|
||||
}
|
||||
}
|
||||
|
||||
//*********************************** */
|
||||
private async updateScheduleInvoiceJobs(invoice: Invoice, updateDto: UpdateInvoiceDto) {
|
||||
//
|
||||
await this.removeRecurringInvoiceJobs(invoice.id);
|
||||
await this.removeReminderInvoiceJobs(invoice.id);
|
||||
|
||||
if (updateDto.isRecurring) {
|
||||
if (!updateDto.recurringPeriod) throw new BadRequestException(InvoiceMessage.RECURRING_PERIOD_REQUIRED);
|
||||
|
||||
if (updateDto.maxRecurringCycles && updateDto.maxRecurringCycles < 1) {
|
||||
throw new BadRequestException(InvoiceMessage.MAX_RECURRING_CYCLES_MUST_BE_POSITIVE);
|
||||
}
|
||||
|
||||
await this.scheduleInvoiceJobs(invoice, updateDto);
|
||||
|
||||
this.logger.log(`Updated recurring invoice for user ${updateDto.userId} with interval ${updateDto.recurringPeriod}`);
|
||||
}
|
||||
}
|
||||
//*********************************** */
|
||||
private async addNotifyAdminForSubscriptionPaymentToQueue(invoice: Invoice) {
|
||||
await this.invoiceQueue.add(
|
||||
@@ -859,4 +840,79 @@ export class InvoicesService {
|
||||
userEmail: user.email,
|
||||
});
|
||||
}
|
||||
//*********************************** */
|
||||
|
||||
private async removeRecurringInvoiceJobs(invoiceId: string) {
|
||||
const existingJobs = await this.invoiceQueue.getJobs(["delayed", "waiting", "active"]);
|
||||
for (const job of existingJobs) {
|
||||
if (job.name === INVOICE.RECURRING_JOB_NAME && job.data.invoiceId === invoiceId) {
|
||||
await job.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
//*********************************** */
|
||||
|
||||
private async removeReminderInvoiceJobs(invoiceId: string) {
|
||||
const existingJobs = await this.invoiceQueue.getJobs(["delayed", "waiting", "active"]);
|
||||
for (const job of existingJobs) {
|
||||
if (job.name === INVOICE.REMINDER_JOB_NAME && job.data.invoiceId === invoiceId) {
|
||||
await job.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///********************************** */
|
||||
|
||||
private validateInvoiceItems(items: InvoiceItemDto[]): void {
|
||||
if (!items || items.length === 0) throw new BadRequestException(InvoiceMessage.ITEMS_REQUIRED);
|
||||
|
||||
items.forEach((item) => {
|
||||
if (item.unitPrice <= 0) throw new BadRequestException(InvoiceMessage.UNIT_PRICE_MUST_BE_POSITIVE);
|
||||
if (item.count <= 0) throw new BadRequestException(InvoiceMessage.COUNT_MUST_BE_POSITIVE);
|
||||
if (item.discount && (item.discount < 0 || item.discount > 100)) {
|
||||
throw new BadRequestException(InvoiceMessage.DISCOUNT_MUST_BE_BETWEEN_0_AND_100);
|
||||
}
|
||||
});
|
||||
}
|
||||
///********************************** */
|
||||
|
||||
private validateRecurringInvoice(createDto: CreateInvoiceDto | UpdateInvoiceDto) {
|
||||
if (createDto.isRecurring) {
|
||||
if (!createDto.recurringPeriod) throw new BadRequestException(InvoiceMessage.RECURRING_PERIOD_REQUIRED);
|
||||
|
||||
if (createDto.maxRecurringCycles && createDto.maxRecurringCycles < 1) {
|
||||
throw new BadRequestException(InvoiceMessage.MAX_RECURRING_CYCLES_MUST_BE_POSITIVE);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//********************************** */
|
||||
|
||||
private async calculateRecurringDelay(recurringPeriod: RecurringPeriodEnum) {
|
||||
let delayMs: number;
|
||||
switch (recurringPeriod) {
|
||||
case RecurringPeriodEnum.WEEKLY:
|
||||
delayMs = dayjs().add(1, "week").subtract(INVOICE.DAYS_BEFORE_OVERDUE, "day").diff(dayjs());
|
||||
break;
|
||||
case RecurringPeriodEnum.MONTHLY:
|
||||
delayMs = dayjs().add(1, "month").subtract(7, "day").diff(dayjs());
|
||||
break;
|
||||
case RecurringPeriodEnum.QUARTERLY:
|
||||
delayMs = dayjs().add(3, "month").subtract(7, "day").diff(dayjs());
|
||||
break;
|
||||
case RecurringPeriodEnum.BIANNUALLY:
|
||||
delayMs = dayjs().add(6, "month").subtract(7, "day").diff(dayjs());
|
||||
break;
|
||||
case RecurringPeriodEnum.ANNUALLY:
|
||||
delayMs = dayjs().add(1, "year").subtract(7, "day").diff(dayjs());
|
||||
break;
|
||||
default:
|
||||
delayMs = dayjs().add(1, "month").subtract(7, "day").diff(dayjs());
|
||||
}
|
||||
|
||||
this.logger.log(`Calculated recurring delay: ${delayMs}ms for period: ${recurringPeriod}`);
|
||||
return delayMs;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user