|
|
|
@@ -7,7 +7,7 @@ import dayjs from "dayjs";
|
|
|
|
|
import { Decimal } from "decimal.js";
|
|
|
|
|
import { DataSource, QueryRunner } from "typeorm";
|
|
|
|
|
|
|
|
|
|
import { DiscountMessage } from "../../../common/enums/message.enum";
|
|
|
|
|
import { CommonMessage, DiscountMessage } from "../../../common/enums/message.enum";
|
|
|
|
|
import { SubscriptionPlan } from "../../subscriptions/entities/subscription.entity";
|
|
|
|
|
import { SubscriptionsService } from "../../subscriptions/providers/subscriptions.service";
|
|
|
|
|
import { DISCOUNT } from "../constants";
|
|
|
|
@@ -30,6 +30,13 @@ export class DiscountsService {
|
|
|
|
|
private readonly dataSource: DataSource,
|
|
|
|
|
) {}
|
|
|
|
|
|
|
|
|
|
//======================================
|
|
|
|
|
// PUBLIC METHODS
|
|
|
|
|
//======================================
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a new discount
|
|
|
|
|
*/
|
|
|
|
|
async create(createDiscountDto: CreateDiscountDto) {
|
|
|
|
|
const queryRunner = this.dataSource.createQueryRunner();
|
|
|
|
|
|
|
|
|
@@ -55,7 +62,10 @@ export class DiscountsService {
|
|
|
|
|
await queryRunner.release();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//************************************ */
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get all discounts for admin panel with pagination
|
|
|
|
|
*/
|
|
|
|
|
async getDiscountsForAdmin(queryDto: SearchDiscountQueryDto) {
|
|
|
|
|
const [discounts, count] = await this.discountRepository.findDiscountForAdmin(queryDto);
|
|
|
|
|
|
|
|
|
@@ -65,12 +75,30 @@ export class DiscountsService {
|
|
|
|
|
pagination: true,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
//************************************ */
|
|
|
|
|
async getDiscountById(id: string) {
|
|
|
|
|
return this.discountRepository.findById(id);
|
|
|
|
|
}
|
|
|
|
|
//************************************ */
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get a discount by ID
|
|
|
|
|
*/
|
|
|
|
|
async getDiscountById(id: string) {
|
|
|
|
|
const discount = await this.discountRepository.findById(id);
|
|
|
|
|
if (!discount) throw new BadRequestException(DiscountMessage.NOT_FOUND);
|
|
|
|
|
|
|
|
|
|
return { discount };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get a discount with distinct services for client
|
|
|
|
|
*/
|
|
|
|
|
async getDiscountWithDistinctServicesForClient(id: string) {
|
|
|
|
|
const discount = await this.discountRepository.findDiscountWithDistinctServicesForClient(id);
|
|
|
|
|
if (!discount) throw new BadRequestException(DiscountMessage.NOT_FOUND);
|
|
|
|
|
|
|
|
|
|
return { discount };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Toggle discount active status
|
|
|
|
|
*/
|
|
|
|
|
async toggleDiscountStatus(id: string) {
|
|
|
|
|
const discount = await this.discountRepository.findById(id);
|
|
|
|
|
if (!discount) throw new BadRequestException(DiscountMessage.NOT_FOUND);
|
|
|
|
@@ -83,7 +111,10 @@ export class DiscountsService {
|
|
|
|
|
discount,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
//************************************ */
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Update an existing discount
|
|
|
|
|
*/
|
|
|
|
|
async updateDiscount(id: string, updateDiscountDto: UpdateDiscountDto) {
|
|
|
|
|
const queryRunner = this.dataSource.createQueryRunner();
|
|
|
|
|
|
|
|
|
@@ -101,23 +132,42 @@ export class DiscountsService {
|
|
|
|
|
await this.addDiscountDeactivationJob(existingDiscount.id, updateDiscountDto.endDate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (updateDiscountDto.value) existingDiscount.value = updateDiscountDto.value;
|
|
|
|
|
// Check if discount is being converted from code-based to direct
|
|
|
|
|
const isConvertingToDirectDiscount =
|
|
|
|
|
updateDiscountDto.direct === true && existingDiscount.applicationType === DiscountApplicationType.CODE_BASED;
|
|
|
|
|
|
|
|
|
|
await queryRunner.manager.save({
|
|
|
|
|
...existingDiscount,
|
|
|
|
|
// When converting to direct discount, target services are required
|
|
|
|
|
if (isConvertingToDirectDiscount && (!updateDiscountDto.targetServices || updateDiscountDto.targetServices.length === 0)) {
|
|
|
|
|
throw new BadRequestException(DiscountMessage.TARGET_SERVICES_REQUIRED_FOR_DIRECT_DISCOUNT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Normal direct discount update or first-time conversion to direct discount with services
|
|
|
|
|
if (updateDiscountDto.direct && updateDiscountDto.targetServices && updateDiscountDto.targetServices.length > 0) {
|
|
|
|
|
await this.updateDirectDiscount(updateDiscountDto, existingDiscount, queryRunner);
|
|
|
|
|
}
|
|
|
|
|
// When changing from direct to code-based, clear all associated plans
|
|
|
|
|
else if (updateDiscountDto.direct === false && existingDiscount.applicationType === DiscountApplicationType.DIRECT) {
|
|
|
|
|
await this.clearDirectDiscountFromPlans(existingDiscount, queryRunner);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update discount properties directly on existingDiscount to preserve relationships
|
|
|
|
|
// This is done AFTER handling subscription plans to prevent overwriting relationship changes
|
|
|
|
|
Object.assign(existingDiscount, {
|
|
|
|
|
...updateDiscountDto,
|
|
|
|
|
applicationType: updateDiscountDto.direct ? DiscountApplicationType.DIRECT : DiscountApplicationType.CODE_BASED,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (updateDiscountDto.direct) {
|
|
|
|
|
await this.updateDirectDiscount(updateDiscountDto, existingDiscount, queryRunner);
|
|
|
|
|
}
|
|
|
|
|
// Save the updated discount
|
|
|
|
|
await queryRunner.manager.save(this.discountRepository.target, existingDiscount);
|
|
|
|
|
|
|
|
|
|
await queryRunner.commitTransaction();
|
|
|
|
|
|
|
|
|
|
// Get the updated discount with all relations
|
|
|
|
|
const updatedDiscount = await this.discountRepository.findById(id);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
message: DiscountMessage.UPDATED,
|
|
|
|
|
discount: existingDiscount,
|
|
|
|
|
discount: updatedDiscount,
|
|
|
|
|
};
|
|
|
|
|
} catch (error) {
|
|
|
|
|
await queryRunner.rollbackTransaction();
|
|
|
|
@@ -126,12 +176,21 @@ export class DiscountsService {
|
|
|
|
|
await queryRunner.release();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//************************************ */
|
|
|
|
|
async softDeleteDiscount(id: string): Promise<void> {
|
|
|
|
|
await this.discountRepository.softDeleteDiscount(id);
|
|
|
|
|
}
|
|
|
|
|
//************************************ */
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Soft delete a discount
|
|
|
|
|
*/
|
|
|
|
|
async softDeleteDiscount(id: string) {
|
|
|
|
|
await this.discountRepository.softDeleteDiscount(id);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
message: CommonMessage.DELETED,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Deactivate a discount and update related subscription plans
|
|
|
|
|
*/
|
|
|
|
|
async deactivateDiscount(id: string, queryRunner: QueryRunner) {
|
|
|
|
|
const discount = await queryRunner.manager.findOne(this.discountRepository.target, {
|
|
|
|
|
where: { id, isActive: true },
|
|
|
|
@@ -143,16 +202,14 @@ export class DiscountsService {
|
|
|
|
|
if (!discount) throw new BadRequestException(DiscountMessage.NOT_FOUND);
|
|
|
|
|
|
|
|
|
|
discount.isActive = false;
|
|
|
|
|
await queryRunner.manager.save(discount);
|
|
|
|
|
await queryRunner.manager.save(this.discountRepository.target, discount);
|
|
|
|
|
const subscriptionPlans = discount.subscriptionPlans;
|
|
|
|
|
|
|
|
|
|
if (discount.applicationType === DiscountApplicationType.DIRECT) {
|
|
|
|
|
for (const subscriptionPlan of subscriptionPlans) {
|
|
|
|
|
subscriptionPlan.directDiscount = undefined;
|
|
|
|
|
subscriptionPlan.price = subscriptionPlan.originalPrice
|
|
|
|
|
? new Decimal(subscriptionPlan.originalPrice)
|
|
|
|
|
: new Decimal(subscriptionPlan.price);
|
|
|
|
|
await queryRunner.manager.save(subscriptionPlan);
|
|
|
|
|
subscriptionPlan.directDiscount = null!;
|
|
|
|
|
subscriptionPlan.price = new Decimal(subscriptionPlan.originalPrice);
|
|
|
|
|
await queryRunner.manager.save(SubscriptionPlan, subscriptionPlan);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -161,7 +218,14 @@ export class DiscountsService {
|
|
|
|
|
discount,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
//************************************ */
|
|
|
|
|
|
|
|
|
|
//======================================
|
|
|
|
|
// PRIVATE METHODS
|
|
|
|
|
//======================================
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a discount entity and handle code generation or direct discount application
|
|
|
|
|
*/
|
|
|
|
|
private async createDiscount(createDiscountDto: CreateDiscountDto, queryRunner: QueryRunner) {
|
|
|
|
|
const discount = queryRunner.manager.create(this.discountRepository.target, {
|
|
|
|
|
...createDiscountDto,
|
|
|
|
@@ -173,7 +237,7 @@ export class DiscountsService {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
discount.value = this.calculateDiscountValue(createDiscountDto);
|
|
|
|
|
await queryRunner.manager.save(discount);
|
|
|
|
|
await queryRunner.manager.save(this.discountRepository.target, discount);
|
|
|
|
|
|
|
|
|
|
if (createDiscountDto.direct) {
|
|
|
|
|
await this.applyDirectDiscount(createDiscountDto, discount, queryRunner);
|
|
|
|
@@ -181,7 +245,10 @@ export class DiscountsService {
|
|
|
|
|
|
|
|
|
|
return discount;
|
|
|
|
|
}
|
|
|
|
|
//************************************ */
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Apply a direct discount to subscription plans
|
|
|
|
|
*/
|
|
|
|
|
private async applyDirectDiscount(createDiscountDto: CreateDiscountDto, discount: Discount, queryRunner: QueryRunner) {
|
|
|
|
|
const subscriptionPlans = await this.subscriptionService.getAllServiceSubscriptions(
|
|
|
|
|
createDiscountDto.targetServices ?? [],
|
|
|
|
@@ -195,16 +262,120 @@ export class DiscountsService {
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Calculate prices for all subscription plans
|
|
|
|
|
for (const subscriptionPlan of subscriptionPlans) {
|
|
|
|
|
subscriptionPlan.directDiscount = discount;
|
|
|
|
|
await this.updatePlanPrice(subscriptionPlan, discount);
|
|
|
|
|
await queryRunner.manager.save(subscriptionPlan);
|
|
|
|
|
if (!subscriptionPlan.originalPrice) {
|
|
|
|
|
subscriptionPlan.originalPrice = new Decimal(subscriptionPlan.price);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let discountAmount: Decimal;
|
|
|
|
|
if (discount.type === DiscountType.PERCENTAGE) {
|
|
|
|
|
discountAmount = new Decimal(subscriptionPlan.originalPrice).mul(discount.value).div(100);
|
|
|
|
|
} else {
|
|
|
|
|
discountAmount = new Decimal(discount.value);
|
|
|
|
|
}
|
|
|
|
|
subscriptionPlan.price = new Decimal(subscriptionPlan.originalPrice).sub(discountAmount);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await queryRunner.manager.save(discount);
|
|
|
|
|
// Update the discount and set subscription plans in one operation
|
|
|
|
|
discount.subscriptionPlans = subscriptionPlans;
|
|
|
|
|
await queryRunner.manager.save(this.discountRepository.target, discount);
|
|
|
|
|
}
|
|
|
|
|
//************************************ */
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Update a direct discount's subscription plans
|
|
|
|
|
*/
|
|
|
|
|
private async updateDirectDiscount(updateDiscountDto: UpdateDiscountDto, discount: Discount, queryRunner: QueryRunner) {
|
|
|
|
|
// Get all subscription plans for the target services
|
|
|
|
|
const subscriptionPlans = await this.subscriptionService.getAllServiceSubscriptions(
|
|
|
|
|
updateDiscountDto.targetServices ?? [],
|
|
|
|
|
queryRunner,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
console.log(`Found ${subscriptionPlans.length} subscription plans for target services`);
|
|
|
|
|
|
|
|
|
|
// Get current plans with direct discount regardless of loaded relations
|
|
|
|
|
let currentPlans: SubscriptionPlan[] = [];
|
|
|
|
|
if (discount.applicationType === DiscountApplicationType.DIRECT) {
|
|
|
|
|
// Find all subscription plans that have this discount directly applied
|
|
|
|
|
currentPlans = await queryRunner.manager.find(SubscriptionPlan, {
|
|
|
|
|
where: { directDiscount: { id: discount.id } },
|
|
|
|
|
relations: { directDiscount: true, service: true },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
console.log(`Found ${currentPlans.length} current plans with this discount applied`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove discount from plans that are no longer targeted
|
|
|
|
|
for (const plan of currentPlans) {
|
|
|
|
|
if (!subscriptionPlans.some((newPlan) => newPlan.id === plan.id)) {
|
|
|
|
|
console.log(`Removing discount from plan ${plan.id}`);
|
|
|
|
|
plan.directDiscount = null!;
|
|
|
|
|
plan.price = new Decimal(plan.originalPrice);
|
|
|
|
|
await queryRunner.manager.save(SubscriptionPlan, plan);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check for existing discounts on new plans
|
|
|
|
|
const plansWithExistingDiscount = subscriptionPlans.filter((plan) => plan.directDiscount && plan.directDiscount.id !== discount.id);
|
|
|
|
|
|
|
|
|
|
if (plansWithExistingDiscount.length > 0) {
|
|
|
|
|
throw new BadRequestException(
|
|
|
|
|
DiscountMessage.DIRECT_DISCOUNT_ALREADY_EXISTS.replace("[serviceName]", plansWithExistingDiscount[0].service.name),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Calculate prices for all subscription plans
|
|
|
|
|
for (const subscriptionPlan of subscriptionPlans) {
|
|
|
|
|
if (!subscriptionPlan.originalPrice) {
|
|
|
|
|
subscriptionPlan.originalPrice = new Decimal(subscriptionPlan.price);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let discountAmount: Decimal;
|
|
|
|
|
if (discount.type === DiscountType.PERCENTAGE) {
|
|
|
|
|
discountAmount = new Decimal(subscriptionPlan.originalPrice).mul(discount.value).div(100);
|
|
|
|
|
} else {
|
|
|
|
|
discountAmount = new Decimal(discount.value);
|
|
|
|
|
}
|
|
|
|
|
subscriptionPlan.price = new Decimal(subscriptionPlan.originalPrice).sub(discountAmount);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log(`Setting ${subscriptionPlans.length} subscription plans on discount ${discount.id}`);
|
|
|
|
|
|
|
|
|
|
// Update the discount and set subscription plans in one operation
|
|
|
|
|
discount.subscriptionPlans = subscriptionPlans;
|
|
|
|
|
|
|
|
|
|
// First save each subscription plan with explicit reference to the discount
|
|
|
|
|
for (const plan of subscriptionPlans) {
|
|
|
|
|
plan.directDiscount = discount;
|
|
|
|
|
await queryRunner.manager.save(SubscriptionPlan, plan);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Then save the discount with the relationship
|
|
|
|
|
await queryRunner.manager.save(this.discountRepository.target, discount);
|
|
|
|
|
|
|
|
|
|
return subscriptionPlans;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Clear direct discount from plans
|
|
|
|
|
*/
|
|
|
|
|
private async clearDirectDiscountFromPlans(discount: Discount, queryRunner: QueryRunner) {
|
|
|
|
|
const subscriptionPlans = await queryRunner.manager.find(SubscriptionPlan, {
|
|
|
|
|
where: { directDiscount: { id: discount.id } },
|
|
|
|
|
relations: { directDiscount: true },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for (const plan of subscriptionPlans) {
|
|
|
|
|
plan.directDiscount = null!;
|
|
|
|
|
plan.price = new Decimal(plan.originalPrice);
|
|
|
|
|
await queryRunner.manager.save(SubscriptionPlan, plan);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Generate a discount code for code-based discounts
|
|
|
|
|
*/
|
|
|
|
|
private async generateDiscountCode(discount: Discount, queryRunner: QueryRunner) {
|
|
|
|
|
let attempts = 0;
|
|
|
|
|
let code: string;
|
|
|
|
@@ -220,28 +391,37 @@ export class DiscountsService {
|
|
|
|
|
|
|
|
|
|
discount.code = code;
|
|
|
|
|
}
|
|
|
|
|
//************************************ */
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Generate a random discount code
|
|
|
|
|
*/
|
|
|
|
|
private async generateRandomCode(length: number = 4) {
|
|
|
|
|
return randomBytes(length).toString("hex").toUpperCase();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Find a discount by code
|
|
|
|
|
*/
|
|
|
|
|
private async findDiscountByCode(code: string, queryRunner: QueryRunner) {
|
|
|
|
|
return queryRunner.manager.findOne(this.discountRepository.target, {
|
|
|
|
|
where: { code, isActive: true },
|
|
|
|
|
withDeleted: false,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
//************************************ */
|
|
|
|
|
|
|
|
|
|
private async generateRandomCode(length: number = 4) {
|
|
|
|
|
return randomBytes(length).toString("hex").toUpperCase();
|
|
|
|
|
}
|
|
|
|
|
//************************************ */
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Calculate discount value based on discount type
|
|
|
|
|
*/
|
|
|
|
|
private calculateDiscountValue(createDiscountDto: CreateDiscountDto): number {
|
|
|
|
|
if (createDiscountDto.type === DiscountType.PERCENTAGE) return createDiscountDto.value;
|
|
|
|
|
if (createDiscountDto.type === DiscountType.FIXED_AMOUNT) return createDiscountDto.value;
|
|
|
|
|
|
|
|
|
|
throw new BadRequestException(DiscountMessage.INVALID_DISCOUNT_TYPE);
|
|
|
|
|
}
|
|
|
|
|
//************************************ */
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Validate discount start and end dates
|
|
|
|
|
*/
|
|
|
|
|
private async validateDates(startDate: string, endDate: string) {
|
|
|
|
|
if (dayjs(startDate).isAfter(dayjs(endDate))) {
|
|
|
|
|
throw new BadRequestException(DiscountMessage.START_DATE_MUST_BE_BEFORE_END_DATE);
|
|
|
|
@@ -259,60 +439,19 @@ export class DiscountsService {
|
|
|
|
|
throw new BadRequestException(DiscountMessage.END_DATE_MUST_BE_AFTER_NOW);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//************************************ */
|
|
|
|
|
private async updateDirectDiscount(updateDiscountDto: UpdateDiscountDto, discount: Discount, queryRunner: QueryRunner) {
|
|
|
|
|
const subscriptionPlans = await this.subscriptionService.getAllServiceSubscriptions(
|
|
|
|
|
updateDiscountDto.targetServices ?? [],
|
|
|
|
|
queryRunner,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const currentPlans = await this.subscriptionService.getAllServiceSubscriptions(
|
|
|
|
|
discount.subscriptionPlans.map((plan) => plan.service.id),
|
|
|
|
|
queryRunner,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
for (const plan of currentPlans) {
|
|
|
|
|
if (!subscriptionPlans.some((newPlan) => newPlan.id === plan.id)) {
|
|
|
|
|
plan.directDiscount = undefined;
|
|
|
|
|
await queryRunner.manager.save(plan);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const plansWithExistingDiscount = subscriptionPlans.filter((plan) => plan.directDiscount && plan.directDiscount.id !== discount.id);
|
|
|
|
|
|
|
|
|
|
if (plansWithExistingDiscount.length > 0) {
|
|
|
|
|
throw new BadRequestException(
|
|
|
|
|
DiscountMessage.DIRECT_DISCOUNT_ALREADY_EXISTS.replace("[serviceName]", plansWithExistingDiscount[0].service.name),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const subscriptionPlan of subscriptionPlans) {
|
|
|
|
|
subscriptionPlan.directDiscount = discount;
|
|
|
|
|
await this.updatePlanPrice(subscriptionPlan, discount);
|
|
|
|
|
await queryRunner.manager.save(subscriptionPlan);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await queryRunner.manager.save(discount);
|
|
|
|
|
}
|
|
|
|
|
//************************************ */
|
|
|
|
|
private async updatePlanPrice(subscriptionPlan: SubscriptionPlan, discount: Discount) {
|
|
|
|
|
if (!subscriptionPlan.originalPrice) subscriptionPlan.originalPrice = new Decimal(subscriptionPlan.price);
|
|
|
|
|
|
|
|
|
|
let discountAmount: Decimal;
|
|
|
|
|
if (discount.type === DiscountType.PERCENTAGE) {
|
|
|
|
|
discountAmount = subscriptionPlan.originalPrice.mul(discount.value).div(100);
|
|
|
|
|
} else {
|
|
|
|
|
discountAmount = new Decimal(discount.value);
|
|
|
|
|
}
|
|
|
|
|
subscriptionPlan.price = subscriptionPlan.originalPrice.sub(discountAmount);
|
|
|
|
|
}
|
|
|
|
|
//************************************ */
|
|
|
|
|
/**
|
|
|
|
|
* Remove discount deactivation job
|
|
|
|
|
*/
|
|
|
|
|
private async removeDiscountDeactivationJob(discountId: string) {
|
|
|
|
|
const jobs = await this.discountQueue.getJobs(["delayed", "waiting", "active"]);
|
|
|
|
|
const jobToRemove = jobs.find((job) => job.name === DISCOUNT.DISCOUNT_DEACTIVATE_JOB_NAME && job.data.discountId === discountId);
|
|
|
|
|
if (jobToRemove) await jobToRemove.remove();
|
|
|
|
|
}
|
|
|
|
|
//************************************ */
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add discount deactivation job
|
|
|
|
|
*/
|
|
|
|
|
private async addDiscountDeactivationJob(discountId: string, endDate: string) {
|
|
|
|
|
return this.discountQueue.add(
|
|
|
|
|
DISCOUNT.DISCOUNT_DEACTIVATE_JOB_NAME,
|
|
|
|
|