diff --git a/src/common/enums/message.enum.ts b/src/common/enums/message.enum.ts index a7bc8cf..10f6cd9 100755 --- a/src/common/enums/message.enum.ts +++ b/src/common/enums/message.enum.ts @@ -114,6 +114,7 @@ export const enum CommonMessage { ID_REQUIRED = "شناسه مورد نیاز است", ID_SHOULD_BE_UUID = "شناسه باید یک یو یو آی دی باشد", PaymentTypeQueryNotEmpty = "نوع پرداخت نمی‌تواند خالی باشد", + SEARCH_QUERY_STRING = "رشته جستجو باید یک رشته باشد", } export const enum CategoryMessage { @@ -521,6 +522,8 @@ export const enum DiscountMessage { START_DATE_MUST_BE_BEFORE_END_DATE = "تاریخ شروع باید قبل از تاریخ پایان باشد", START_DATE_MUST_BE_AFTER_NOW = "تاریخ شروع باید بعد از تاریخ فعلی باشد", END_DATE_MUST_BE_AFTER_NOW = "تاریخ پایان باید بعد از تاریخ فعلی باشد", + EACH_TARGET_PRODUCT_MUST_BE_A_UUID = "هر محصول باید یک UUID معتبر باشد", + DIRECT_DISCOUNT_ALREADY_EXISTS = "تخفیف مستقیم قبلا برای سرویس [serviceName] ثبت شده است", } export const enum EmailMessage { diff --git a/src/modules/discounts/DTO/create-discount.dto.ts b/src/modules/discounts/DTO/create-discount.dto.ts index c872f8c..e6afc37 100755 --- a/src/modules/discounts/DTO/create-discount.dto.ts +++ b/src/modules/discounts/DTO/create-discount.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; -import { IsArray, IsBoolean, IsDateString, IsEnum, IsNotEmpty, IsNumber, IsOptional, IsString, ValidateIf } from "class-validator"; +import { IsArray, IsBoolean, IsDateString, IsEnum, IsNotEmpty, IsNumber, IsOptional, IsString, IsUUID, ValidateIf } from "class-validator"; import { DiscountMessage } from "../../../common/enums/message.enum"; import { DiscountType } from "../enums/discount-type.enum"; @@ -51,7 +51,7 @@ export class CreateDiscountDto { @IsNotEmpty({ message: DiscountMessage.TARGET_PRODUCTS_MUST_BE_AN_ARRAY }) @IsArray({ message: DiscountMessage.TARGET_PRODUCTS_MUST_BE_AN_ARRAY }) @ValidateIf((o) => o.direct) - @IsString({ each: true, message: DiscountMessage.EACH_TARGET_PRODUCT_MUST_BE_A_STRING }) + @IsUUID("4", { each: true, message: DiscountMessage.EACH_TARGET_PRODUCT_MUST_BE_A_UUID }) @ApiPropertyOptional({ description: "Array of service IDs this discount applies to (empty array means applies to all services)", example: ["service-id-1", "service-id-2"], diff --git a/src/modules/discounts/DTO/search-discount-query.dto.ts b/src/modules/discounts/DTO/search-discount-query.dto.ts new file mode 100644 index 0000000..8dd3601 --- /dev/null +++ b/src/modules/discounts/DTO/search-discount-query.dto.ts @@ -0,0 +1,12 @@ +import { ApiPropertyOptional } from "@nestjs/swagger"; +import { IsOptional, IsString } from "class-validator"; + +import { PaginationDto } from "../../../common/DTO/pagination.dto"; +import { CommonMessage } from "../../../common/enums/message.enum"; + +export class SearchDiscountQueryDto extends PaginationDto { + @IsOptional() + @IsString({ message: CommonMessage.SEARCH_QUERY_STRING }) + @ApiPropertyOptional({ description: "Search query", example: "search query" }) + q?: string; +} diff --git a/src/modules/discounts/discounts.controller.ts b/src/modules/discounts/discounts.controller.ts index 66a5725..74f7ad3 100755 --- a/src/modules/discounts/discounts.controller.ts +++ b/src/modules/discounts/discounts.controller.ts @@ -1,7 +1,8 @@ -import { Body, Controller, Post } from "@nestjs/common"; +import { Body, Controller, Get, Post, Query } from "@nestjs/common"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { CreateDiscountDto } from "./DTO/create-discount.dto"; +import { SearchDiscountQueryDto } from "./DTO/search-discount-query.dto"; import { DiscountsService } from "./providers/discounts.service"; import { AdminRoute } from "../../common/decorators/admin.decorator"; import { AuthGuards } from "../../common/decorators/auth-guard.decorator"; @@ -22,4 +23,13 @@ export class DiscountsController { create(@Body() createDiscountDto: CreateDiscountDto) { return this.discountsService.create(createDiscountDto); } + + @ApiOperation({ summary: "Get all discounts for admin" }) + @AdminRoute() + @PermissionsDec(PermissionEnum.DISCOUNTS) + @Get() + @ApiResponse({ status: 200, description: "Discounts fetched successfully" }) + getDiscountsForAdmin(@Query() queryDto: SearchDiscountQueryDto) { + return this.discountsService.getDiscountsForAdmin(queryDto); + } } diff --git a/src/modules/discounts/discounts.module.ts b/src/modules/discounts/discounts.module.ts index 993dd63..f355d35 100755 --- a/src/modules/discounts/discounts.module.ts +++ b/src/modules/discounts/discounts.module.ts @@ -3,18 +3,16 @@ import { TypeOrmModule } from "@nestjs/typeorm"; import { DiscountsController } from "./discounts.controller"; import { UsersModule } from "../users/users.module"; -import { DirectDiscount } from "./entities/direct-discount.entity"; import { Discount } from "./entities/discount.entity"; import { UsageDiscount } from "./entities/usage-discount.entity"; import { DiscountsService } from "./providers/discounts.service"; -import { DirectDiscountRepository } from "./repositories/direct-discounts.repository"; import { DiscountRepository } from "./repositories/discounts.repository"; import { UsageDiscountRepository } from "./repositories/usage-discounts.repository"; import { SubscriptionsModule } from "../subscriptions/subscriptions.module"; @Module({ - imports: [TypeOrmModule.forFeature([Discount, UsageDiscount, DirectDiscount]), UsersModule, SubscriptionsModule], + imports: [TypeOrmModule.forFeature([Discount, UsageDiscount]), UsersModule, SubscriptionsModule], controllers: [DiscountsController], - providers: [DiscountsService, DiscountRepository, DirectDiscountRepository, UsageDiscountRepository], + providers: [DiscountsService, DiscountRepository, UsageDiscountRepository], exports: [DiscountsService], }) export class DiscountsModule {} diff --git a/src/modules/discounts/entities/direct-discount.entity.ts b/src/modules/discounts/entities/direct-discount.entity.ts deleted file mode 100755 index f72cd23..0000000 --- a/src/modules/discounts/entities/direct-discount.entity.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Column, DeleteDateColumn, Entity, OneToMany } from "typeorm"; - -import { BaseEntity } from "../../../common/entities/base.entity"; -import { DecimalTransformer } from "../../../common/transformers/decimal.transformer"; -import { SubscriptionPlan } from "../../subscriptions/entities/subscription.entity"; -import { DiscountType } from "../enums/discount-type.enum"; -@Entity() -export class DirectDiscount extends BaseEntity { - @Column({ type: "varchar", length: 150 }) - title: string; - - @Column({ type: "enum", enum: DiscountType, default: DiscountType.PERCENTAGE }) - type: DiscountType; - - @Column({ type: "decimal", precision: 16, scale: 2, transformer: new DecimalTransformer() }) - value: number; - - @Column({ type: "timestamptz" }) - startDate: Date; - - @Column({ type: "timestamptz" }) - endDate: Date; - - @Column({ type: "boolean", default: true }) - isActive: boolean; - - @DeleteDateColumn({ nullable: true }) - deletedAt?: Date; - - @OneToMany(() => SubscriptionPlan, (subscriptionPlan) => subscriptionPlan.directDiscount) - subscriptionPlans: SubscriptionPlan[]; -} diff --git a/src/modules/discounts/entities/discount.entity.ts b/src/modules/discounts/entities/discount.entity.ts index 6c082ee..447bd75 100755 --- a/src/modules/discounts/entities/discount.entity.ts +++ b/src/modules/discounts/entities/discount.entity.ts @@ -3,6 +3,8 @@ import { Column, DeleteDateColumn, Entity, OneToMany } from "typeorm"; import { UsageDiscount } from "./usage-discount.entity"; import { BaseEntity } from "../../../common/entities/base.entity"; import { DecimalTransformer } from "../../../common/transformers/decimal.transformer"; +import { SubscriptionPlan } from "../../subscriptions/entities/subscription.entity"; +import { DiscountApplicationType } from "../enums/discount-application-type.enum"; import { DiscountType } from "../enums/discount-type.enum"; @Entity() @@ -10,8 +12,11 @@ export class Discount extends BaseEntity { @Column({ type: "varchar", length: 150 }) title: string; - @Column({ type: "varchar", length: 100, unique: true }) - code: string; + @Column({ type: "enum", enum: DiscountApplicationType }) + applicationType: DiscountApplicationType; + + @Column({ type: "varchar", length: 100, unique: true, nullable: true }) + code?: string; @Column({ type: "enum", enum: DiscountType, default: DiscountType.PERCENTAGE }) type: DiscountType; @@ -31,9 +36,13 @@ export class Discount extends BaseEntity { @Column({ type: "boolean", default: true }) isActive: boolean; + @DeleteDateColumn({ nullable: true }) + deletedAt?: Date; + + // Relations @OneToMany(() => UsageDiscount, (usageDiscount) => usageDiscount.discount) usages: UsageDiscount[]; - @DeleteDateColumn({ nullable: true }) - deletedAt?: Date; + @OneToMany(() => SubscriptionPlan, (subscriptionPlan) => subscriptionPlan.directDiscount) + subscriptionPlans: SubscriptionPlan[]; } diff --git a/src/modules/discounts/enums/discount-application-type.enum.ts b/src/modules/discounts/enums/discount-application-type.enum.ts new file mode 100644 index 0000000..a5a4778 --- /dev/null +++ b/src/modules/discounts/enums/discount-application-type.enum.ts @@ -0,0 +1,4 @@ +export enum DiscountApplicationType { + CODE_BASED = "CODE_BASED", + DIRECT = "DIRECT", +} diff --git a/src/modules/discounts/providers/discounts.service.ts b/src/modules/discounts/providers/discounts.service.ts index 87fb491..3fea5a7 100755 --- a/src/modules/discounts/providers/discounts.service.ts +++ b/src/modules/discounts/providers/discounts.service.ts @@ -7,22 +7,22 @@ import { DataSource, QueryRunner } from "typeorm"; 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 { Discount } from "../entities/discount.entity"; +import { DiscountApplicationType } from "../enums/discount-application-type.enum"; import { DiscountType } from "../enums/discount-type.enum"; -import { DirectDiscountRepository } from "../repositories/direct-discounts.repository"; import { DiscountRepository } from "../repositories/discounts.repository"; + @Injectable() export class DiscountsService { private MAX_ATTEMPTS = 10; constructor( private readonly discountRepository: DiscountRepository, - private readonly directDiscountRepository: DirectDiscountRepository, private readonly subscriptionService: SubscriptionsService, private readonly dataSource: DataSource, ) {} - //***************************************** */ - async create(createDiscountDto: CreateDiscountDto) { const queryRunner = this.dataSource.createQueryRunner(); @@ -32,16 +32,13 @@ export class DiscountsService { await this.validateDates(createDiscountDto.startDate, createDiscountDto.endDate); - if (!createDiscountDto.direct) { - await this.createCodeBasedDiscount(createDiscountDto, queryRunner); - } else { - await this.createDirectBasedDiscount(createDiscountDto, queryRunner); - } + const discount = await this.createDiscount(createDiscountDto, queryRunner); await queryRunner.commitTransaction(); return { message: DiscountMessage.CREATED, + discount, }; } catch (error) { await queryRunner.rollbackTransaction(); @@ -50,10 +47,125 @@ export class DiscountsService { await queryRunner.release(); } } - //***************************************** */ - private async createCodeBasedDiscount(createDiscountDto: CreateDiscountDto, queryRunner: QueryRunner) { - const discount = queryRunner.manager.create(this.discountRepository.target, createDiscountDto); + async getDiscountsForAdmin(queryDto: SearchDiscountQueryDto) { + const [discounts, count] = await this.discountRepository.findDiscountForAdmin(queryDto); + + return { + discounts, + count, + pagination: true, + }; + } + + async updateDiscount(id: string, updateDiscountDto: CreateDiscountDto) { + const queryRunner = this.dataSource.createQueryRunner(); + + try { + await queryRunner.connect(); + await queryRunner.startTransaction(); + + const existingDiscount = await this.discountRepository.findOne({ + where: { id }, + relations: ["subscriptionPlans"], + withDeleted: false, + }); + + if (!existingDiscount) { + throw new BadRequestException(DiscountMessage.NOT_FOUND); + } + + await this.validateDates(updateDiscountDto.startDate, updateDiscountDto.endDate); + + // Update basic discount properties + Object.assign(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); + } + + await queryRunner.commitTransaction(); + + return { + message: DiscountMessage.UPDATED, + discount: existingDiscount, + }; + } catch (error) { + await queryRunner.rollbackTransaction(); + throw error; + } finally { + await queryRunner.release(); + } + } + + async softDeleteDiscount(id: string): Promise { + await this.discountRepository.softDeleteDiscount(id); + } + + private async createDiscount(createDiscountDto: CreateDiscountDto, queryRunner: QueryRunner) { + const discount = queryRunner.manager.create(this.discountRepository.target, { + ...createDiscountDto, + applicationType: createDiscountDto.direct ? DiscountApplicationType.DIRECT : DiscountApplicationType.CODE_BASED, + }); + + if (!createDiscountDto.direct) { + await this.generateDiscountCode(discount, queryRunner); + } + + discount.value = this.calculateDiscountValue(createDiscountDto); + await queryRunner.manager.save(discount); + + if (createDiscountDto.direct) { + await this.applyDirectDiscount(createDiscountDto, discount, queryRunner); + } + + return discount; + } + + private async applyDirectDiscount(createDiscountDto: CreateDiscountDto, discount: Discount, queryRunner: QueryRunner) { + const subscriptionPlans = await this.subscriptionService.getAllServiceSubscriptions( + createDiscountDto.targetServices ?? [], + queryRunner, + ); + + const plansWithExistingDiscount = subscriptionPlans.filter((plan) => plan.directDiscount); + 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 queryRunner.manager.save(subscriptionPlan); + } + } + + private async generateDiscountCode(discount: Discount, queryRunner: QueryRunner) { let attempts = 0; let code: string; let existCode; @@ -61,46 +173,24 @@ export class DiscountsService { do { if (attempts >= this.MAX_ATTEMPTS) throw new BadRequestException(DiscountMessage.CODE_GENERATION_FAILED); - code = await this.generateDiscountCode(); + code = await this.generateRandomCode(); existCode = await this.findDiscountByCode(code, queryRunner); attempts++; } while (existCode && attempts < this.MAX_ATTEMPTS); discount.code = code; - discount.value = this.calculateDiscountValue(createDiscountDto); - await queryRunner.manager.save(discount); - return discount; } - //***************************************** */ - private async createDirectBasedDiscount(createDiscountDto: CreateDiscountDto, queryRunner: QueryRunner) { - // Create the discount entity - const discount = queryRunner.manager.create(this.directDiscountRepository.target, createDiscountDto); - discount.value = this.calculateDiscountValue(createDiscountDto); - - await queryRunner.manager.save(discount); - - const subscriptionPlans = await this.subscriptionService.getAllServiceSubscriptions(createDiscountDto.targetServices ?? []); - - for (const subscriptionPlan of subscriptionPlans) { - subscriptionPlan.directDiscount = discount; - await queryRunner.manager.save(subscriptionPlan); - } - - return discount; - } - //**************************** */ private async findDiscountByCode(code: string, queryRunner: QueryRunner) { - const discount = await queryRunner.manager.findOne(this.discountRepository.target, { where: { code } }); - return discount; + return queryRunner.manager.findOne(this.discountRepository.target, { + where: { code }, + withDeleted: false, + }); } - //**************************** */ - private async generateDiscountCode(length: number = 8) { - const code = randomBytes(length).toString("hex"); - return code; + 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; @@ -109,8 +199,6 @@ 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); @@ -128,4 +216,39 @@ export class DiscountsService { throw new BadRequestException(DiscountMessage.END_DATE_MUST_BE_AFTER_NOW); } } + + private async updateDirectDiscount(updateDiscountDto: CreateDiscountDto, 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, + ); + + for (const plan of currentPlans) { + if (!subscriptionPlans.some((newPlan) => newPlan.id === plan.id)) { + plan.directDiscount = null; + 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) { + throw new BadRequestException( + DiscountMessage.DIRECT_DISCOUNT_ALREADY_EXISTS.replace("[serviceName]", plansWithExistingDiscount[0].service.name), + ); + } + + // Apply discount to new target plans + for (const subscriptionPlan of subscriptionPlans) { + subscriptionPlan.directDiscount = discount; + await queryRunner.manager.save(subscriptionPlan); + } + } } diff --git a/src/modules/discounts/repositories/direct-discounts.repository.ts b/src/modules/discounts/repositories/direct-discounts.repository.ts deleted file mode 100755 index 65c4908..0000000 --- a/src/modules/discounts/repositories/direct-discounts.repository.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { InjectRepository } from "@nestjs/typeorm"; -import { Repository } from "typeorm"; - -import { DirectDiscount } from "../entities/direct-discount.entity"; - -@Injectable() -export class DirectDiscountRepository extends Repository { - constructor(@InjectRepository(DirectDiscount) repository: Repository) { - super(repository.target, repository.manager, repository.queryRunner); - } -} diff --git a/src/modules/discounts/repositories/discounts.repository.ts b/src/modules/discounts/repositories/discounts.repository.ts index 853a0f2..99ed1e6 100755 --- a/src/modules/discounts/repositories/discounts.repository.ts +++ b/src/modules/discounts/repositories/discounts.repository.ts @@ -2,6 +2,8 @@ import { Injectable } from "@nestjs/common"; import { InjectRepository } from "@nestjs/typeorm"; import { Repository } from "typeorm"; +import { PaginationUtils } from "../../utils/providers/pagination.utils"; +import { SearchDiscountQueryDto } from "../DTO/search-discount-query.dto"; import { Discount } from "../entities/discount.entity"; @Injectable() @@ -13,14 +15,14 @@ export class DiscountRepository extends Repository { async findById(id: string): Promise { return this.findOne({ where: { id }, - relations: ["productRelations"], + withDeleted: false, }); } async findByCode(code: string): Promise { return this.findOne({ where: { code }, - relations: ["productRelations"], + withDeleted: false, }); } @@ -30,43 +32,27 @@ export class DiscountRepository extends Repository { code, isActive: true, }, - relations: ["productRelations"], + withDeleted: false, }); } - async findAll(options?: any): Promise<[Discount[], number]> { - const query = this.createQueryBuilder("discount").leftJoinAndSelect("discount.productRelations", "productRelations"); + async findDiscountForAdmin(queryDto: SearchDiscountQueryDto) { + const { limit, skip } = PaginationUtils(queryDto); + const queryBuilder = this.createQueryBuilder("discount") + .leftJoin("discount.subscriptionPlans", "plan") + .addSelect(["plan.id", "plan.name", "plan.price", "plan.duration"]) + .leftJoin("plan.service", "service") + .addSelect(["service.id", "service.name"]) + .where("discount.deletedAt IS NULL"); - if (options?.status) { - query.andWhere("discount.status = :status", { status: options.status }); + if (queryDto.q) { + queryBuilder.andWhere("discount.title ILIKE :query", { query: `%${queryDto.q}%` }); } - if (options?.isActive !== undefined) { - query.andWhere("discount.isActive = :isActive", { isActive: options.isActive }); - } + return queryBuilder.skip(skip).take(limit).orderBy("discount.createdAt", "DESC").getManyAndCount(); + } - if (options?.search) { - query.andWhere("(discount.title ILIKE :search OR discount.code ILIKE :search)", { - search: `%${options.search}%`, - }); - } - - if (options?.productId) { - query.andWhere("EXISTS (SELECT 1 FROM discount_product dp WHERE dp.discountId = discount.id AND dp.productId = :productId)", { - productId: options.productId, - }); - } - - if (options?.sort) { - const [field, order] = options.sort.split(","); - query.orderBy(`discount.${field}`, order.toUpperCase()); - } else { - query.orderBy("discount.createdAt", "DESC"); - } - - return query - .skip(options?.skip || 0) - .take(options?.take || 10) - .getManyAndCount(); + async softDeleteDiscount(id: string) { + return this.softDelete(id); } } diff --git a/src/modules/subscriptions/entities/subscription.entity.ts b/src/modules/subscriptions/entities/subscription.entity.ts index bc37488..0bb215d 100755 --- a/src/modules/subscriptions/entities/subscription.entity.ts +++ b/src/modules/subscriptions/entities/subscription.entity.ts @@ -6,7 +6,7 @@ import { UserSubscription } from "./user-subscription.entity"; import { BaseEntity } from "../../../common/entities/base.entity"; import { DecimalTransformer } from "../../../common/transformers/decimal.transformer"; import { DanakService } from "../../danak-services/entities/danak-service.entity"; -import { DirectDiscount } from "../../discounts/entities/direct-discount.entity"; +import { Discount } from "../../discounts/entities/discount.entity"; @Entity() @Index(["service", "name"], { unique: true }) @@ -29,8 +29,8 @@ export class SubscriptionPlan extends BaseEntity { @OneToMany(() => UserSubscription, (userSubscription) => userSubscription.plan) userSubscriptions: UserSubscription[]; - @ManyToOne(() => DirectDiscount, (directDiscount) => directDiscount.subscriptionPlans, { nullable: true }) - directDiscount?: DirectDiscount; + @ManyToOne(() => Discount, (discount) => discount.subscriptionPlans, { nullable: true }) + directDiscount?: Discount; @DeleteDateColumn({ nullable: true }) deletedAt?: Date; diff --git a/src/modules/subscriptions/providers/subscriptions.service.ts b/src/modules/subscriptions/providers/subscriptions.service.ts index ba441c4..6518911 100755 --- a/src/modules/subscriptions/providers/subscriptions.service.ts +++ b/src/modules/subscriptions/providers/subscriptions.service.ts @@ -2,7 +2,7 @@ import { BadRequestException, Injectable } from "@nestjs/common"; import dayjs from "dayjs"; // eslint-disable-next-line import/no-named-as-default import Decimal from "decimal.js"; -import { DataSource, In, Not } from "typeorm"; +import { DataSource, In, Not, QueryRunner } from "typeorm"; import { ServiceMessage, SubscriptionMessage } from "../../../common/enums/message.enum"; import { DanakServicesService } from "../../danak-services/providers/danak-services.service"; @@ -269,9 +269,10 @@ export class SubscriptionsService { } //************************************ */ - async getAllServiceSubscriptions(serviceIds: string[]) { - const subscriptions = await this.subscriptionsPlanRepository.find({ + async getAllServiceSubscriptions(serviceIds: string[], queryRunner: QueryRunner) { + const subscriptions = await queryRunner.manager.find(this.subscriptionsPlanRepository.target, { where: { service: { id: In(serviceIds) }, isActive: true }, + relations: { directDiscount: true, service: true }, }); return subscriptions; }