chore: apply discount on plan
This commit is contained in:
@@ -358,6 +358,7 @@ export const enum LearningMessage {
|
||||
}
|
||||
|
||||
export const enum DiscountMessage {
|
||||
CAN_NOT_APPLICABLE = "این کد تخفیف برای این فاکتور قابل اعمال نیست",
|
||||
TYPE_REQUIRED = "وارد کردن نوع تخفیف الزامی است",
|
||||
TYPE_INVALID = "نوع تخفیف نامعتبر است",
|
||||
CALCULATION_TYPE_REQUIRED = "وارد کردن نوع محاسبه تخفیف الزامی است",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Decimal> {
|
||||
const invoice = await this.invoiceRepository.findOneBy({ id: invoiceId });
|
||||
if (!invoice) throw new BadRequestException(InvoiceMessage.NOT_FOUND_BY_ID);
|
||||
// async calculateFinalPrice(invoiceId: string): Promise<Decimal> {
|
||||
// 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);
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user