subscription:upgrade
This commit is contained in:
@@ -9,6 +9,7 @@ import { DataSource, In, Not, QueryRunner } from "typeorm";
|
|||||||
|
|
||||||
import { ServiceMessage, SubscriptionMessage } from "../../../common/enums/message.enum";
|
import { ServiceMessage, SubscriptionMessage } from "../../../common/enums/message.enum";
|
||||||
import { DanakServicesService } from "../../danak-services/providers/danak-services.service";
|
import { DanakServicesService } from "../../danak-services/providers/danak-services.service";
|
||||||
|
import { RestaurantService } from "../../dmenu/providers/restaurant.service";
|
||||||
import { INVOICE } from "../../invoices/constants";
|
import { INVOICE } from "../../invoices/constants";
|
||||||
import { InvoicesService } from "../../invoices/providers/invoices.service";
|
import { InvoicesService } from "../../invoices/providers/invoices.service";
|
||||||
import { User } from "../../users/entities/user.entity";
|
import { User } from "../../users/entities/user.entity";
|
||||||
@@ -47,18 +48,31 @@ export class SubscriptionsService {
|
|||||||
return this.moduleRef.get(InvoicesService, { strict: false });
|
return this.moduleRef.get(InvoicesService, { strict: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get restaurantService(): RestaurantService {
|
||||||
|
return this.moduleRef.get(RestaurantService, { strict: false });
|
||||||
|
}
|
||||||
|
|
||||||
//************************************ */
|
//************************************ */
|
||||||
async createSubscriptionsPlan(createDto: AddSubscriptionsToServiceDto) {
|
async createSubscriptionsPlan(createDto: AddSubscriptionsToServiceDto) {
|
||||||
const danakService = await this.danakServices.findServiceById(createDto.serviceId);
|
const danakService = await this.danakServices.findServiceById(createDto.serviceId);
|
||||||
if (!danakService) throw new BadRequestException(ServiceMessage.SERVICE_NOT_FOUND_BY_ID);
|
if (!danakService) throw new BadRequestException(ServiceMessage.SERVICE_NOT_FOUND_BY_ID);
|
||||||
|
|
||||||
const subscriptions = createDto.subs.map((sub) => ({
|
const subscriptions = createDto.subs.map((sub) => {
|
||||||
|
const adjustedPrice = sub.price < 100_000 ? new Decimal(0) : new Decimal(sub.price);
|
||||||
|
const isFree = sub.price < 100_000;
|
||||||
|
if (isFree) {
|
||||||
|
console.log(`🎁 Plan "${sub.name}" marked as FREE (price ${sub.price} < 100,000 IRR)`);
|
||||||
|
} else {
|
||||||
|
console.log(`💰 Plan "${sub.name}" price: ${adjustedPrice.toNumber()} IRR`);
|
||||||
|
}
|
||||||
|
return {
|
||||||
...sub,
|
...sub,
|
||||||
service: { id: createDto.serviceId },
|
service: { id: createDto.serviceId },
|
||||||
price: sub.price < 100_000 ? new Decimal(0) : new Decimal(sub.price),
|
price: adjustedPrice,
|
||||||
originalPrice: sub.price < 100_000 ? new Decimal(0) : new Decimal(sub.price),
|
originalPrice: adjustedPrice,
|
||||||
isFree: sub.price < 100_000,
|
isFree,
|
||||||
}));
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const subscriptionNames = createDto.subs.map((sub) => sub.name);
|
const subscriptionNames = createDto.subs.map((sub) => sub.name);
|
||||||
const existingSubscriptions = await this.subscriptionsPlanRepository.find({
|
const existingSubscriptions = await this.subscriptionsPlanRepository.find({
|
||||||
@@ -143,8 +157,15 @@ export class SubscriptionsService {
|
|||||||
|
|
||||||
if (updateDto.duration) subscription.duration = updateDto.duration;
|
if (updateDto.duration) subscription.duration = updateDto.duration;
|
||||||
if (updateDto.price) {
|
if (updateDto.price) {
|
||||||
subscription.price = updateDto.price < 100_000 ? new Decimal(0) : new Decimal(updateDto.price);
|
const adjustedPrice = updateDto.price < 100_000 ? new Decimal(0) : new Decimal(updateDto.price);
|
||||||
subscription.originalPrice = updateDto.price < 100_000 ? new Decimal(0) : new Decimal(updateDto.price);
|
const isFree = updateDto.price < 100_000;
|
||||||
|
if (isFree) {
|
||||||
|
console.log(`🎁 Plan "${subscription.name}" updated to FREE (price ${updateDto.price} < 100,000 IRR)`);
|
||||||
|
} else {
|
||||||
|
console.log(`💰 Plan "${subscription.name}" price updated to: ${adjustedPrice.toNumber()} IRR`);
|
||||||
|
}
|
||||||
|
subscription.price = adjustedPrice;
|
||||||
|
subscription.originalPrice = adjustedPrice;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.subscriptionsPlanRepository.save({
|
await this.subscriptionsPlanRepository.save({
|
||||||
@@ -377,13 +398,25 @@ export class SubscriptionsService {
|
|||||||
|
|
||||||
const userSubscription = await queryRunner.manager.findOne(UserSubscription, {
|
const userSubscription = await queryRunner.manager.findOne(UserSubscription, {
|
||||||
where: { id: userSubscriptionId, user: { id: userId } },
|
where: { id: userSubscriptionId, user: { id: userId } },
|
||||||
relations: { plan: true }
|
relations: { plan: { service: true }, user: true }
|
||||||
});
|
});
|
||||||
if (!userSubscription) throw new BadRequestException(SubscriptionMessage.USER_SUBS_NOT_FOUND);
|
if (!userSubscription) throw new BadRequestException(SubscriptionMessage.USER_SUBS_NOT_FOUND);
|
||||||
|
|
||||||
const newPlan = await this.subscriptionsPlanRepository.findOneBy({ id: planId });
|
// Check if restaurant exists for dmenu service
|
||||||
|
if (userSubscription.plan.service.name.includes('دی منو')) {
|
||||||
|
try {
|
||||||
|
const rest = await this.restaurantService.getRestaurantSubscription(userSubscriptionId);
|
||||||
|
console.log('rest', rest)
|
||||||
|
} catch (error) {
|
||||||
|
throw new BadRequestException('Restaurant* not found for this subscription');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const newPlan = await this.subscriptionsPlanRepository.findById(planId);
|
||||||
if (!newPlan) throw new BadRequestException(SubscriptionMessage.PLAN_NOT_FOUND);
|
if (!newPlan) throw new BadRequestException(SubscriptionMessage.PLAN_NOT_FOUND);
|
||||||
|
|
||||||
|
console.log('new plan', newPlan.service.id)
|
||||||
|
console.log('userSubscription.plan.service.id',userSubscription.plan.service.id)
|
||||||
if (userSubscription.plan.service.id !== newPlan.service.id) {
|
if (userSubscription.plan.service.id !== newPlan.service.id) {
|
||||||
throw new BadRequestException(SubscriptionMessage.SERVICE_MISMATCH);
|
throw new BadRequestException(SubscriptionMessage.SERVICE_MISMATCH);
|
||||||
}
|
}
|
||||||
@@ -397,6 +430,8 @@ export class SubscriptionsService {
|
|||||||
const today = dayjs();
|
const today = dayjs();
|
||||||
const endDate = dayjs(userSubscription.endDate);
|
const endDate = dayjs(userSubscription.endDate);
|
||||||
const remainingDays = Math.max(0, endDate.diff(today, 'day'));
|
const remainingDays = Math.max(0, endDate.diff(today, 'day'));
|
||||||
|
console.log('🔢 CALCULATION LOGS:');
|
||||||
|
console.log(`📅 Remaining days: ${remainingDays} (endDate: ${endDate.format('YYYY-MM-DD')}, today: ${today.format('YYYY-MM-DD')})`);
|
||||||
|
|
||||||
if (remainingDays <= 0) {
|
if (remainingDays <= 0) {
|
||||||
throw new BadRequestException(SubscriptionMessage.SUBSCRIPTION_EXPIRED_CANNOT_UPGRADE);
|
throw new BadRequestException(SubscriptionMessage.SUBSCRIPTION_EXPIRED_CANNOT_UPGRADE);
|
||||||
@@ -408,16 +443,21 @@ export class SubscriptionsService {
|
|||||||
|
|
||||||
const currentDailyRate = new Decimal(userSubscription.plan.price).div(currentPlanDuration);
|
const currentDailyRate = new Decimal(userSubscription.plan.price).div(currentPlanDuration);
|
||||||
const newDailyRate = new Decimal(newPlan.price).div(newPlanDuration);
|
const newDailyRate = new Decimal(newPlan.price).div(newPlanDuration);
|
||||||
|
console.log(`💰 Current plan: ${userSubscription.plan.price} IRR for ${currentPlanDuration} days = ${currentDailyRate.toNumber()} IRR/day`);
|
||||||
|
console.log(`💰 New plan: ${newPlan.price} IRR for ${newPlanDuration} days = ${newDailyRate.toNumber()} IRR/day`);
|
||||||
|
|
||||||
// Calculate price difference per day
|
// Calculate price difference per day
|
||||||
const dailyPriceDifference = newDailyRate.sub(currentDailyRate);
|
const dailyPriceDifference = newDailyRate.sub(currentDailyRate);
|
||||||
|
console.log(`📈 Daily price difference: ${dailyPriceDifference.toNumber()} IRR/day`);
|
||||||
|
|
||||||
// Calculate prorated upgrade cost for remaining days
|
// Calculate prorated upgrade cost for remaining days
|
||||||
const proratedUpgradeCost = dailyPriceDifference.mul(remainingDays);
|
const proratedUpgradeCost = dailyPriceDifference.mul(remainingDays);
|
||||||
|
console.log(`🧮 Prorated upgrade cost: ${dailyPriceDifference.toNumber()} IRR/day × ${remainingDays} days = ${proratedUpgradeCost.toNumber()} IRR`);
|
||||||
|
|
||||||
// Ensure minimum charge amount
|
// Ensure minimum charge amount
|
||||||
const minimumCharge = new Decimal(1000); // 1000 IRR minimum
|
const minimumCharge = new Decimal(1000); // 1000 IRR minimum
|
||||||
const finalUpgradeCost = Decimal.max(proratedUpgradeCost, minimumCharge);
|
const finalUpgradeCost = Decimal.max(proratedUpgradeCost, minimumCharge);
|
||||||
|
console.log(`✅ Final upgrade cost: max(${proratedUpgradeCost.toNumber()} IRR, ${minimumCharge.toNumber()} IRR minimum) = ${finalUpgradeCost.toNumber()} IRR`);
|
||||||
|
|
||||||
const user = await this.usersService.findOneByIdWithQueryRunner(userSubscription.user.id, queryRunner);
|
const user = await this.usersService.findOneByIdWithQueryRunner(userSubscription.user.id, queryRunner);
|
||||||
|
|
||||||
|
|||||||
@@ -13,4 +13,11 @@ export class SubscriptionsPlanRepository extends Repository<SubscriptionPlan> {
|
|||||||
async findOneByName(name: string, id?: string): Promise<SubscriptionPlan | null> {
|
async findOneByName(name: string, id?: string): Promise<SubscriptionPlan | null> {
|
||||||
return this.findOneBy({ name, ...(id && { id: Not(id) }) });
|
return this.findOneBy({ name, ...(id && { id: Not(id) }) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async findById(id: string): Promise<SubscriptionPlan | null> {
|
||||||
|
return this.findOne({
|
||||||
|
where: { id },
|
||||||
|
relations: { service: true }
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { SubscriptionsPlanRepository } from "./repositories/subscriptions.reposi
|
|||||||
import { UserSubscriptionsRepository } from "./repositories/user-subscriptions.repository";
|
import { UserSubscriptionsRepository } from "./repositories/user-subscriptions.repository";
|
||||||
import { SubscriptionsController } from "./subscriptions.controller";
|
import { SubscriptionsController } from "./subscriptions.controller";
|
||||||
import { DanakServicesModule } from "../danak-services/danak-services.module";
|
import { DanakServicesModule } from "../danak-services/danak-services.module";
|
||||||
|
import { DmenuModule } from "../dmenu/dmenu.module";
|
||||||
import { InvoicesModule } from "../invoices/invoices.module";
|
import { InvoicesModule } from "../invoices/invoices.module";
|
||||||
import { UsersModule } from "../users/users.module";
|
import { UsersModule } from "../users/users.module";
|
||||||
import { WalletsModule } from "../wallets/wallets.module";
|
import { WalletsModule } from "../wallets/wallets.module";
|
||||||
@@ -26,6 +27,7 @@ import { UserQuickAccessRepository } from "./repositories/users-quick-access.rep
|
|||||||
}),
|
}),
|
||||||
forwardRef(() => InvoicesModule),
|
forwardRef(() => InvoicesModule),
|
||||||
DanakServicesModule,
|
DanakServicesModule,
|
||||||
|
forwardRef(() => DmenuModule),
|
||||||
UsersModule,
|
UsersModule,
|
||||||
WalletsModule,
|
WalletsModule,
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user