diff --git a/src/common/enums/message.enum.ts b/src/common/enums/message.enum.ts index 46a0365..45cd3d8 100644 --- a/src/common/enums/message.enum.ts +++ b/src/common/enums/message.enum.ts @@ -358,6 +358,7 @@ export const enum LearningMessage { } export const enum DiscountMessage { + CAN_NOT_APPLICABLE = "این کد تخفیف برای این فاکتور قابل اعمال نیست", TYPE_REQUIRED = "وارد کردن نوع تخفیف الزامی است", TYPE_INVALID = "نوع تخفیف نامعتبر است", CALCULATION_TYPE_REQUIRED = "وارد کردن نوع محاسبه تخفیف الزامی است", diff --git a/src/modules/invoices/invoices.controller.ts b/src/modules/invoices/invoices.controller.ts index 386bd8a..d21f3b4 100644 --- a/src/modules/invoices/invoices.controller.ts +++ b/src/modules/invoices/invoices.controller.ts @@ -59,9 +59,4 @@ export class InvoicesController { async applyDiscount(@Param() paramDto: ParamDto, @Body() applyDiscountDto: ApplyDiscountDto) { return this.invoiceService.applyDiscount(paramDto.id, applyDiscountDto); } - - @Get(":id/final-price") - async getFinalPrice(@Param() paramDto: ParamDto) { - return this.invoiceService.calculateFinalPrice(paramDto.id); - } } diff --git a/src/modules/invoices/providers/invoices.service.ts b/src/modules/invoices/providers/invoices.service.ts index 4dc9db8..abe78e5 100644 --- a/src/modules/invoices/providers/invoices.service.ts +++ b/src/modules/invoices/providers/invoices.service.ts @@ -4,7 +4,7 @@ import dayjs from "dayjs"; import Decimal from "decimal.js"; import { QueryRunner } from "typeorm"; -import { InvoiceMessage } from "../../../common/enums/message.enum"; +import { DiscountMessage, InvoiceMessage } from "../../../common/enums/message.enum"; import { DiscountRepository } from "../../discounts/repositories/discount.repository"; import { SubscriptionPlan } from "../../subscriptions/entities/subscription.entity"; import { PaginationUtils } from "../../utils/providers/pagination.utils"; @@ -207,7 +207,7 @@ export class InvoicesService { relations: ["subscriptionPlans", "users"], }); if (!discount) { - throw new BadRequestException(`Discount with code ${applyDiscountDto.discountCode} not found or inactive`); + throw new BadRequestException(DiscountMessage.NOT_FOUND); } if (discount.subscriptionPlans && discount.subscriptionPlans.length > 0) { @@ -217,7 +217,7 @@ export class InvoicesService { return discount.subscriptionPlans.some((plan) => plan.id === subscriptionPlan.id); }); if (!isApplicable) { - throw new BadRequestException("This discount is not applicable to any subscription plan in the invoice."); + throw new BadRequestException(DiscountMessage.CAN_NOT_APPLICABLE); } } @@ -233,18 +233,18 @@ export class InvoicesService { return { invoice }; } - async calculateFinalPrice(invoiceId: string): Promise { - const invoice = await this.invoiceRepository.findOneBy({ id: invoiceId }); - if (!invoice) throw new BadRequestException(InvoiceMessage.NOT_FOUND_BY_ID); + // async calculateFinalPrice(invoiceId: string): Promise { + // const invoice = await this.invoiceRepository.findOneBy({ id: invoiceId }); + // if (!invoice) throw new BadRequestException(InvoiceMessage.NOT_FOUND_BY_ID); - let discountValue = new Decimal(0); - if (invoice.discount) { - if (invoice.discount.calculationType === "PERCENTAGE") { - discountValue = invoice.totalPrice.mul(invoice.discount.amount).div(100); - } else if (invoice.discount.calculationType === "FIXED") { - discountValue = new Decimal(invoice.discount.amount); - } - } - return invoice.totalPrice.minus(discountValue); - } + // let discountValue = new Decimal(0); + // if (invoice.discount) { + // if (invoice.discount.calculationType === "PERCENTAGE") { + // discountValue = invoice.totalPrice.mul(invoice.discount.amount).div(100); + // } else if (invoice.discount.calculationType === "FIXED") { + // discountValue = new Decimal(invoice.discount.amount); + // } + // } + // return invoice.totalPrice.minus(discountValue); + // } } diff --git a/src/modules/subscriptions/providers/subscriptions.service.ts b/src/modules/subscriptions/providers/subscriptions.service.ts index 4f00a12..0954581 100644 --- a/src/modules/subscriptions/providers/subscriptions.service.ts +++ b/src/modules/subscriptions/providers/subscriptions.service.ts @@ -5,6 +5,7 @@ import { DataSource, In } from "typeorm"; import { ServiceMessage, SubscriptionMessage, WalletMessage } from "../../../common/enums/message.enum"; import { DanakServicesService } from "../../danak-services/providers/danak-services.service"; +import { DiscountCalculationType } from "../../discounts/enums/discount-type.enum"; import { InvoicesService } from "../../invoices/providers/invoices.service"; import { UsersService } from "../../users/providers/users.service"; import { PaginationUtils } from "../../utils/providers/pagination.utils"; @@ -146,6 +147,8 @@ export class SubscriptionsService { .createQueryBuilder("subscription") .leftJoin("subscription.service", "service") .addSelect(["service.id"]) + .leftJoin("subscription.discounts", "discount") + .addSelect(["discount.calculationType", "discount.amount", "discount.startDate", "discount.endDate", "discount.isActive"]) .where("service.id = :serviceId", { serviceId }); if (queryDto.q) { @@ -158,9 +161,31 @@ export class SubscriptionsService { const [subscriptions, count] = await queryBuilder.skip(skip).take(limit).getManyAndCount(); + const transformedSubscriptions = subscriptions.map((subscription) => { + let price = new Decimal(subscription.price); + + if (subscription.discounts && subscription.discounts.length > 0) { + subscription.discounts.forEach((discount) => { + if (discount.isActive && new Date() >= discount.startDate && new Date() <= discount.endDate) { + if (discount.calculationType === DiscountCalculationType.PERCENTAGE) { + const discountAmount = price.mul(discount.amount).div(100); + price = price.sub(discountAmount); + } else if (discount.calculationType === DiscountCalculationType.FIXED) { + price = price.sub(discount.amount); + } + } + }); + } + + return { + ...subscription, + finalPrice: price.toNumber(), + }; + }); + return { service, - subscriptions, + subscriptions: transformedSubscriptions, count, pagination: true, }; @@ -193,6 +218,7 @@ export class SubscriptionsService { where: { id: subscribeDto.planId, service: { id: serviceId } }, relations: { service: true, + discounts: true, }, }); @@ -208,15 +234,34 @@ export class SubscriptionsService { const userWallet = await this.walletsService.getWalletByUserId(user.id, queryRunner); - if (userWallet.balance < plan.price) throw new BadRequestException(WalletMessage.INSUFFICIENT_BALANCE); + let finalPrice = new Decimal(plan.price); + + if (plan.discounts && plan.discounts.length > 0) { + plan.discounts.forEach((discount) => { + if (discount.isActive && new Date() >= discount.startDate && new Date() <= discount.endDate) { + if (discount.calculationType === DiscountCalculationType.PERCENTAGE) { + const discountAmount = finalPrice.mul(discount.amount).div(100); + finalPrice = finalPrice.sub(discountAmount); + } else if (discount.calculationType === DiscountCalculationType.FIXED) { + finalPrice = finalPrice.sub(discount.amount); + } + } + }); + } + + plan.price = finalPrice; + + if (new Decimal(userWallet.balance).lessThan(finalPrice)) throw new BadRequestException(WalletMessage.INSUFFICIENT_BALANCE); + + // if (userWallet.balance < plan.price) throw new BadRequestException(WalletMessage.INSUFFICIENT_BALANCE); // - userWallet.balance = new Decimal(userWallet.balance).sub(plan.price); + userWallet.balance = new Decimal(userWallet.balance).sub(finalPrice); await queryRunner.manager.save(Wallet, userWallet); // - await this.walletsService.createSubscriptionTransaction(plan.price, userWallet.id, queryRunner); + await this.walletsService.createSubscriptionTransaction(finalPrice, userWallet.id, queryRunner); //TODO: need queue handling for notification const invoiceDueDate = new Date(userSubscription.endDate); invoiceDueDate.setDate(userSubscription.endDate.getDate() - 3);