fix : extend and upgrade user subscription
This commit is contained in:
@@ -35,7 +35,7 @@ import { UpdateInvoiceDto } from "../DTO/update-invoice.dto";
|
||||
import { Invoice } from "../entities/invoice.entity";
|
||||
import { RecurringPeriodEnum } from "../enums/invoice-recurring-period.enum";
|
||||
import { InvoiceStatus } from "../enums/invoice-status.enum";
|
||||
import { IExternalInvoiceJob } from "../interfaces/external-invoice.interface";
|
||||
import { IExternalInvoiceJob, InvoicePurpose } from "../interfaces/external-invoice.interface";
|
||||
import { InvoiceItemsRepository } from "../repositories/invoice-items.repository";
|
||||
import { InvoicesRepository } from "../repositories/invoices.repository";
|
||||
@Injectable()
|
||||
@@ -55,7 +55,7 @@ export class InvoicesService {
|
||||
private readonly dataSource: DataSource,
|
||||
private readonly accessLogService: AccessLogService,
|
||||
private readonly restaurantService: RestaurantService,
|
||||
) {}
|
||||
) { }
|
||||
|
||||
///********************************** */
|
||||
|
||||
@@ -489,7 +489,9 @@ export class InvoicesService {
|
||||
|
||||
///********************************** */
|
||||
|
||||
async createInvoiceForSubscription(user: User, plan: SubscriptionPlan, userSub: UserSubscription, dueDate: Date, qryRnr: QueryRunner) {
|
||||
async createInvoiceForSubscription(user: User, plan: SubscriptionPlan, userSub: UserSubscription,
|
||||
dueDate: Date, qryRnr: QueryRunner, purpose: InvoicePurpose) {
|
||||
|
||||
const discount = plan.directDiscount;
|
||||
const originalPrice = plan.originalPrice || plan.price;
|
||||
const finalPrice = plan.price;
|
||||
@@ -500,7 +502,7 @@ export class InvoicesService {
|
||||
unitPrice: originalPrice,
|
||||
discount: discount ? new Decimal(originalPrice).sub(finalPrice).toNumber() : 0,
|
||||
subscriptionPlan: userSub,
|
||||
plan: plan,
|
||||
plan,
|
||||
totalPrice: finalPrice,
|
||||
};
|
||||
|
||||
@@ -516,6 +518,7 @@ export class InvoicesService {
|
||||
dueDate,
|
||||
items: [invoiceItem],
|
||||
discount: discount || undefined,
|
||||
purpose
|
||||
});
|
||||
|
||||
await qryRnr.manager.save(Invoice, invoice);
|
||||
@@ -545,33 +548,33 @@ export class InvoicesService {
|
||||
}
|
||||
|
||||
//********************************** */
|
||||
async createInvoiceForSubscriptionUpgrade(user: User, plan: SubscriptionPlan, userSub: UserSubscription, dueDate: Date, qryRnr: QueryRunner, upgradeCost?: Decimal) {
|
||||
async createInvoiceForSubscriptionUpgrade(user: User, plan: SubscriptionPlan, userSub: UserSubscription, dueDate: Date, qryRnr: QueryRunner, upgradeCost: number) {
|
||||
const discount = plan.directDiscount;
|
||||
const originalPrice = upgradeCost || (plan.originalPrice || plan.price);
|
||||
const finalPrice = upgradeCost || plan.price;
|
||||
|
||||
|
||||
const invoiceItem = {
|
||||
name: `${plan.service.name} - Upgrade`,
|
||||
count: 1,
|
||||
unitPrice: originalPrice,
|
||||
discount: discount ? new Decimal(originalPrice).sub(finalPrice).toNumber() : 0,
|
||||
unitPrice: upgradeCost,
|
||||
discount: 0,
|
||||
subscriptionPlan: userSub,
|
||||
plan: plan,
|
||||
totalPrice: finalPrice,
|
||||
totalPrice: upgradeCost,
|
||||
};
|
||||
|
||||
const taxAmount = new Decimal(finalPrice).mul(0.1);
|
||||
const totalPrice = new Decimal(finalPrice).add(taxAmount);
|
||||
const taxAmount = new Decimal(upgradeCost).mul(0.1);
|
||||
const totalPrice = new Decimal(upgradeCost).add(taxAmount);
|
||||
|
||||
const invoice = qryRnr.manager.create(Invoice, {
|
||||
user,
|
||||
totalPrice: totalPrice,
|
||||
originalPrice: new Decimal(originalPrice).add(new Decimal(originalPrice).mul(0.1)),
|
||||
originalPrice: new Decimal(upgradeCost).add(new Decimal(upgradeCost).mul(0.1)),
|
||||
tax: taxAmount.toNumber(),
|
||||
status: InvoiceStatus.WAIT_PAYMENT,
|
||||
dueDate,
|
||||
items: [invoiceItem],
|
||||
discount: discount || undefined,
|
||||
purpose: InvoicePurpose.UPGRADE
|
||||
});
|
||||
|
||||
await qryRnr.manager.save(Invoice, invoice);
|
||||
@@ -798,15 +801,19 @@ export class InvoicesService {
|
||||
const invoiceItem = invoice.items[0];
|
||||
|
||||
// Check if this is an upgrade (subscription is ACTIVE and plan in invoice differs from current plan)
|
||||
const isUpgrade = userSubscription.status === SubscriptionStatus.ACTIVE &&
|
||||
invoiceItem.plan &&
|
||||
invoiceItem.plan.id !== userSubscription.plan.id;
|
||||
const purpose = invoice.purpose
|
||||
|
||||
if (isUpgrade) {
|
||||
|
||||
if (purpose == InvoicePurpose.UPGRADE) {
|
||||
// This is an upgrade - update subscription with the new plan
|
||||
const upgradePlan = invoiceItem.plan!;
|
||||
|
||||
userSubscription.plan = upgradePlan;
|
||||
const diffrence = upgradePlan.duration - userSubscription.plan.duration
|
||||
|
||||
if (diffrence > 0) {
|
||||
userSubscription.endDate = dayjs(userSubscription.endDate).add(diffrence, "day").toDate();
|
||||
}
|
||||
this.logger.log(`Subscription ${userSubscription.id} upgraded to plan ${upgradePlan.name}`);
|
||||
await this.scheduleNextRenewalJob(userSubscription);
|
||||
|
||||
@@ -818,7 +825,7 @@ export class InvoicesService {
|
||||
await this.restaurantService.upgradeSubscription(
|
||||
userSubscription.id,
|
||||
dmenuPlan,
|
||||
userSubscription.endDate.toISOString()
|
||||
userSubscription.endDate
|
||||
);
|
||||
this.logger.log(`External API call completed for dmenu upgrade: subscription ${userSubscription.id}`);
|
||||
} catch (error) {
|
||||
@@ -826,22 +833,46 @@ export class InvoicesService {
|
||||
// Don't fail the transaction if external API call fails
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Regular subscription logic (renewal or activation)
|
||||
const isRenewal = userSubscription.status === SubscriptionStatus.ACTIVE;
|
||||
} else if (purpose == InvoicePurpose.RENEW) {
|
||||
|
||||
if (isRenewal) {
|
||||
const newEndDate = dayjs(userSubscription.endDate).add(userSubscription.plan.duration, "day").toDate();
|
||||
userSubscription.endDate = newEndDate;
|
||||
if (userSubscription.status == SubscriptionStatus.ACTIVE) {
|
||||
userSubscription.endDate = dayjs(userSubscription.endDate).add(userSubscription.plan.duration, "day").toDate();
|
||||
|
||||
this.logger.log(`Subscription ${userSubscription.id} renewed until ${newEndDate}`);
|
||||
await this.scheduleNextRenewalJob(userSubscription);
|
||||
} else {
|
||||
userSubscription.status = SubscriptionStatus.ACTIVE;
|
||||
await this.scheduleNextRenewalJob(userSubscription);
|
||||
userSubscription.endDate = dayjs().add(userSubscription.plan.duration, "day").toDate();
|
||||
}
|
||||
}
|
||||
|
||||
if (invoiceItem.plan) {
|
||||
userSubscription.plan = invoiceItem.plan
|
||||
}
|
||||
|
||||
this.logger.log(`Subscription ${userSubscription.id} renewed until ${userSubscription.endDate}`);
|
||||
await this.scheduleNextRenewalJob(userSubscription);
|
||||
|
||||
// Call external API for dmenu service upgrades
|
||||
if (userSubscription.plan.service.name.toLowerCase().includes('dmenu') || userSubscription.plan.service.slug?.toLowerCase().includes('dmenu')) {
|
||||
try {
|
||||
this.logger.log(`Calling external API for dmenu upgrade: subscription ${userSubscription.id}`);
|
||||
const dmenuPlan = userSubscription.plan.name.includes("دلیوری") ? "premium" : "base";
|
||||
await this.restaurantService.upgradeSubscription(
|
||||
userSubscription.id,
|
||||
dmenuPlan,
|
||||
userSubscription.endDate
|
||||
);
|
||||
this.logger.log(`External API call completed for dmenu upgrade: subscription ${userSubscription.id}`);
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to call external API for dmenu upgrade: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
// Don't fail the transaction if external API call fails
|
||||
}
|
||||
}
|
||||
|
||||
} else if (purpose == InvoicePurpose.NEW) {
|
||||
userSubscription.status = SubscriptionStatus.ACTIVE;
|
||||
await this.scheduleNextRenewalJob(userSubscription);
|
||||
this.logger.log(`Subscription ${userSubscription.id} activated `);
|
||||
|
||||
}
|
||||
// if serivce is dmenu for renew or upgrade it must call dmenu api
|
||||
//
|
||||
await queryRunner.manager.save(UserSubscription, userSubscription);
|
||||
await this.walletsService.createSubscriptionTransaction(invoice.totalPrice, userWallet.id, queryRunner);
|
||||
@@ -942,7 +973,9 @@ export class InvoicesService {
|
||||
subscriptionPlan: {
|
||||
plan: true,
|
||||
},
|
||||
plan: true,
|
||||
plan: {
|
||||
service: true
|
||||
},
|
||||
supportPlan: {
|
||||
supportPlan: true,
|
||||
},
|
||||
@@ -1144,7 +1177,7 @@ export class InvoicesService {
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//*********************************** */
|
||||
private async getInvoiceByIdWithQueryRunner(invoiceId: string, userId: string, queryRunner: QueryRunner) {
|
||||
|
||||
Reference in New Issue
Block a user