|
|
|
@@ -8,6 +8,7 @@ import { DiscountMessage } from "../../../common/enums/message.enum";
|
|
|
|
|
import { SubscriptionsService } from "../../subscriptions/providers/subscriptions.service";
|
|
|
|
|
import { CreateDiscountDto } from "../DTO/create-discount.dto";
|
|
|
|
|
import { SearchDiscountQueryDto } from "../DTO/search-discount-query.dto";
|
|
|
|
|
import { UpdateDiscountDto } from "../DTO/update-discount.dto";
|
|
|
|
|
import { Discount } from "../entities/discount.entity";
|
|
|
|
|
import { DiscountApplicationType } from "../enums/discount-application-type.enum";
|
|
|
|
|
import { DiscountType } from "../enums/discount-type.enum";
|
|
|
|
@@ -47,7 +48,7 @@ export class DiscountsService {
|
|
|
|
|
await queryRunner.release();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//************************************ */
|
|
|
|
|
async getDiscountsForAdmin(queryDto: SearchDiscountQueryDto) {
|
|
|
|
|
const [discounts, count] = await this.discountRepository.findDiscountForAdmin(queryDto);
|
|
|
|
|
|
|
|
|
@@ -57,53 +58,33 @@ export class DiscountsService {
|
|
|
|
|
pagination: true,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async updateDiscount(id: string, updateDiscountDto: CreateDiscountDto) {
|
|
|
|
|
//************************************ */
|
|
|
|
|
async getDiscountById(id: string) {
|
|
|
|
|
return this.discountRepository.findById(id);
|
|
|
|
|
}
|
|
|
|
|
//************************************ */
|
|
|
|
|
async updateDiscount(id: string, updateDiscountDto: UpdateDiscountDto) {
|
|
|
|
|
const queryRunner = this.dataSource.createQueryRunner();
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await queryRunner.connect();
|
|
|
|
|
await queryRunner.startTransaction();
|
|
|
|
|
|
|
|
|
|
const existingDiscount = await this.discountRepository.findOne({
|
|
|
|
|
where: { id },
|
|
|
|
|
relations: ["subscriptionPlans"],
|
|
|
|
|
withDeleted: false,
|
|
|
|
|
});
|
|
|
|
|
const existingDiscount = await this.discountRepository.findById(id);
|
|
|
|
|
|
|
|
|
|
if (!existingDiscount) {
|
|
|
|
|
throw new BadRequestException(DiscountMessage.NOT_FOUND);
|
|
|
|
|
}
|
|
|
|
|
if (!existingDiscount) throw new BadRequestException(DiscountMessage.NOT_FOUND);
|
|
|
|
|
|
|
|
|
|
await this.validateDates(updateDiscountDto.startDate, updateDiscountDto.endDate);
|
|
|
|
|
if (updateDiscountDto.startDate && updateDiscountDto.endDate)
|
|
|
|
|
await this.validateDates(updateDiscountDto.startDate, updateDiscountDto.endDate);
|
|
|
|
|
|
|
|
|
|
// Update basic discount properties
|
|
|
|
|
Object.assign(existingDiscount, {
|
|
|
|
|
if (updateDiscountDto.value) existingDiscount.value = updateDiscountDto.value;
|
|
|
|
|
|
|
|
|
|
await queryRunner.manager.save({
|
|
|
|
|
...existingDiscount,
|
|
|
|
|
...updateDiscountDto,
|
|
|
|
|
applicationType: updateDiscountDto.direct ? DiscountApplicationType.DIRECT : DiscountApplicationType.CODE_BASED,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Handle code-based discount updates
|
|
|
|
|
if (!updateDiscountDto.direct) {
|
|
|
|
|
if (updateDiscountDto.code && updateDiscountDto.code !== existingDiscount.code) {
|
|
|
|
|
const existingCode = await this.findDiscountByCode(updateDiscountDto.code, queryRunner);
|
|
|
|
|
if (existingCode) {
|
|
|
|
|
throw new BadRequestException(DiscountMessage.CODE_ALREADY_EXISTS);
|
|
|
|
|
}
|
|
|
|
|
existingDiscount.code = updateDiscountDto.code;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Clear code for direct discounts
|
|
|
|
|
existingDiscount.code = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Calculate and update value
|
|
|
|
|
existingDiscount.value = this.calculateDiscountValue(updateDiscountDto);
|
|
|
|
|
|
|
|
|
|
// Save the updated discount
|
|
|
|
|
await queryRunner.manager.save(existingDiscount);
|
|
|
|
|
|
|
|
|
|
// Handle direct discount updates
|
|
|
|
|
if (updateDiscountDto.direct) {
|
|
|
|
|
await this.updateDirectDiscount(updateDiscountDto, existingDiscount, queryRunner);
|
|
|
|
|
}
|
|
|
|
@@ -121,11 +102,11 @@ export class DiscountsService {
|
|
|
|
|
await queryRunner.release();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//************************************ */
|
|
|
|
|
async softDeleteDiscount(id: string): Promise<void> {
|
|
|
|
|
await this.discountRepository.softDeleteDiscount(id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//************************************ */
|
|
|
|
|
private async createDiscount(createDiscountDto: CreateDiscountDto, queryRunner: QueryRunner) {
|
|
|
|
|
const discount = queryRunner.manager.create(this.discountRepository.target, {
|
|
|
|
|
...createDiscountDto,
|
|
|
|
@@ -145,7 +126,7 @@ export class DiscountsService {
|
|
|
|
|
|
|
|
|
|
return discount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//************************************ */
|
|
|
|
|
private async applyDirectDiscount(createDiscountDto: CreateDiscountDto, discount: Discount, queryRunner: QueryRunner) {
|
|
|
|
|
const subscriptionPlans = await this.subscriptionService.getAllServiceSubscriptions(
|
|
|
|
|
createDiscountDto.targetServices ?? [],
|
|
|
|
@@ -163,7 +144,10 @@ export class DiscountsService {
|
|
|
|
|
subscriptionPlan.directDiscount = discount;
|
|
|
|
|
await queryRunner.manager.save(subscriptionPlan);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await queryRunner.manager.save(discount);
|
|
|
|
|
}
|
|
|
|
|
//************************************ */
|
|
|
|
|
|
|
|
|
|
private async generateDiscountCode(discount: Discount, queryRunner: QueryRunner) {
|
|
|
|
|
let attempts = 0;
|
|
|
|
@@ -180,6 +164,7 @@ export class DiscountsService {
|
|
|
|
|
|
|
|
|
|
discount.code = code;
|
|
|
|
|
}
|
|
|
|
|
//************************************ */
|
|
|
|
|
|
|
|
|
|
private async findDiscountByCode(code: string, queryRunner: QueryRunner) {
|
|
|
|
|
return queryRunner.manager.findOne(this.discountRepository.target, {
|
|
|
|
@@ -187,10 +172,12 @@ export class DiscountsService {
|
|
|
|
|
withDeleted: false,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
//************************************ */
|
|
|
|
|
|
|
|
|
|
private async generateRandomCode(length: number = 4) {
|
|
|
|
|
return randomBytes(length).toString("hex").toUpperCase();
|
|
|
|
|
}
|
|
|
|
|
//************************************ */
|
|
|
|
|
|
|
|
|
|
private calculateDiscountValue(createDiscountDto: CreateDiscountDto): number {
|
|
|
|
|
if (createDiscountDto.type === DiscountType.PERCENTAGE) return createDiscountDto.value;
|
|
|
|
@@ -198,7 +185,7 @@ export class DiscountsService {
|
|
|
|
|
|
|
|
|
|
throw new BadRequestException(DiscountMessage.INVALID_DISCOUNT_TYPE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//************************************ */
|
|
|
|
|
private async validateDates(startDate: string, endDate: string) {
|
|
|
|
|
if (dayjs(startDate).isAfter(dayjs(endDate))) {
|
|
|
|
|
throw new BadRequestException(DiscountMessage.START_DATE_MUST_BE_BEFORE_END_DATE);
|
|
|
|
@@ -216,14 +203,13 @@ export class DiscountsService {
|
|
|
|
|
throw new BadRequestException(DiscountMessage.END_DATE_MUST_BE_AFTER_NOW);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async updateDirectDiscount(updateDiscountDto: CreateDiscountDto, discount: Discount, queryRunner: QueryRunner) {
|
|
|
|
|
//************************************ */
|
|
|
|
|
private async updateDirectDiscount(updateDiscountDto: UpdateDiscountDto, discount: Discount, queryRunner: QueryRunner) {
|
|
|
|
|
const subscriptionPlans = await this.subscriptionService.getAllServiceSubscriptions(
|
|
|
|
|
updateDiscountDto.targetServices ?? [],
|
|
|
|
|
queryRunner,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Remove discount from plans that are no longer in the target services
|
|
|
|
|
const currentPlans = await this.subscriptionService.getAllServiceSubscriptions(
|
|
|
|
|
discount.subscriptionPlans.map((plan) => plan.service.id),
|
|
|
|
|
queryRunner,
|
|
|
|
@@ -231,12 +217,11 @@ export class DiscountsService {
|
|
|
|
|
|
|
|
|
|
for (const plan of currentPlans) {
|
|
|
|
|
if (!subscriptionPlans.some((newPlan) => newPlan.id === plan.id)) {
|
|
|
|
|
plan.directDiscount = null;
|
|
|
|
|
plan.directDiscount = undefined;
|
|
|
|
|
await queryRunner.manager.save(plan);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check for existing discounts in new target plans
|
|
|
|
|
const plansWithExistingDiscount = subscriptionPlans.filter((plan) => plan.directDiscount && plan.directDiscount.id !== discount.id);
|
|
|
|
|
|
|
|
|
|
if (plansWithExistingDiscount.length > 0) {
|
|
|
|
@@ -245,10 +230,11 @@ export class DiscountsService {
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Apply discount to new target plans
|
|
|
|
|
for (const subscriptionPlan of subscriptionPlans) {
|
|
|
|
|
subscriptionPlan.directDiscount = discount;
|
|
|
|
|
await queryRunner.manager.save(subscriptionPlan);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await queryRunner.manager.save(discount);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|