update: commit for the new file

This commit is contained in:
mahyargdz
2025-04-19 09:46:10 +03:30
parent cd77db93d0
commit d198800362
101 changed files with 112 additions and 17 deletions
Regular → Executable
View File
View File
Regular → Executable
View File
Regular → Executable
View File
View File
+3
View File
@@ -518,6 +518,9 @@ export const enum DiscountMessage {
VALUE_REQUIRED = "مقدار تخفیف الزامی است",
IS_ACTIVE_REQUIRED = "وضعیت فعال بودن الزامی است",
CODE_GENERATION_FAILED = "ایجاد کد تخفیف با خطا مواجه شد",
START_DATE_MUST_BE_BEFORE_END_DATE = "تاریخ شروع باید قبل از تاریخ پایان باشد",
START_DATE_MUST_BE_AFTER_NOW = "تاریخ شروع باید بعد از تاریخ فعلی باشد",
END_DATE_MUST_BE_AFTER_NOW = "تاریخ پایان باید بعد از تاریخ فعلی باشد",
}
export const enum EmailMessage {
Regular → Executable
View File
+1 -1
View File
@@ -13,7 +13,7 @@ export function databaseConfigs(): TypeOrmModuleAsyncOptions {
username: configService.getOrThrow<string>("DB_USER"),
password: configService.getOrThrow<string>("DB_PASS"),
autoLoadEntities: true,
synchronize: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : true,
synchronize: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : false,
logging: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : true,
migrationsTableName: "typeorm_migrations",
migrationsRun: false,
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
+5 -1
View File
@@ -12,7 +12,11 @@ export class CreateDiscountDto {
@IsNotEmpty({ message: DiscountMessage.DISCOUNT_TYPE_REQUIRED })
@IsEnum(DiscountType, { message: DiscountMessage.INVALID_DISCOUNT_TYPE })
@ApiProperty({ description: "The type of discount", example: DiscountType.PERCENTAGE, enum: DiscountType })
@ApiProperty({
description: "The type of discount",
example: `${DiscountType.PERCENTAGE} ==> type 0 means percentage, type 1 means fixed amount`,
enum: DiscountType,
})
type: DiscountType;
@IsNotEmpty({ message: DiscountMessage.DIRECT_REQUIRED })
View File
View File
View File
View File
+4 -3
View File
@@ -7,13 +7,14 @@ 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],
imports: [TypeOrmModule.forFeature([Discount, UsageDiscount, DirectDiscount]), UsersModule, SubscriptionsModule],
controllers: [DiscountsController],
providers: [DiscountsService, DiscountRepository, UsageDiscountRepository],
providers: [DiscountsService, DiscountRepository, DirectDiscountRepository, UsageDiscountRepository],
exports: [DiscountsService],
})
export class DiscountsModule {}
View File
View File
View File
View File
View File
+42 -5
View File
@@ -1,22 +1,28 @@
import { randomBytes } from "node:crypto";
import { BadRequestException, Injectable } from "@nestjs/common";
import dayjs from "dayjs";
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 { 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();
@@ -24,6 +30,8 @@ export class DiscountsService {
await queryRunner.connect();
await queryRunner.startTransaction();
await this.validateDates(createDiscountDto.startDate, createDiscountDto.endDate);
if (!createDiscountDto.direct) {
await this.createCodeBasedDiscount(createDiscountDto, queryRunner);
} else {
@@ -42,7 +50,7 @@ export class DiscountsService {
await queryRunner.release();
}
}
//***************************************** */
private async createCodeBasedDiscount(createDiscountDto: CreateDiscountDto, queryRunner: QueryRunner) {
const discount = queryRunner.manager.create(this.discountRepository.target, createDiscountDto);
@@ -63,12 +71,21 @@ export class DiscountsService {
await queryRunner.manager.save(discount);
return discount;
}
//**************************** */
//***************************************** */
private async createDirectBasedDiscount(createDiscountDto: CreateDiscountDto, queryRunner: QueryRunner) {
const discount = queryRunner.manager.create(this.discountRepository.target, createDiscountDto);
// 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;
}
//**************************** */
@@ -91,4 +108,24 @@ 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);
}
if (dayjs(startDate).isBefore(dayjs().subtract(1, "day"))) {
throw new BadRequestException(DiscountMessage.START_DATE_MUST_BE_AFTER_NOW);
}
if (dayjs(startDate).isSame(dayjs(endDate))) {
throw new BadRequestException(DiscountMessage.START_DATE_MUST_BE_BEFORE_END_DATE);
}
if (dayjs(endDate).isBefore(dayjs())) {
throw new BadRequestException(DiscountMessage.END_DATE_MUST_BE_AFTER_NOW);
}
}
}
@@ -0,0 +1,12 @@
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<DirectDiscount> {
constructor(@InjectRepository(DirectDiscount) repository: Repository<DirectDiscount>) {
super(repository.target, repository.manager, repository.queryRunner);
}
}
View File
View File
View File
View File
View File
View File
-2
View File
@@ -10,7 +10,6 @@ import { InvoicesService } from "./providers/invoices.service";
import { InvoiceProcessor } from "./queue/invoice.processor";
import { InvoiceItemsRepository } from "./repositories/invoice-items.repository";
import { InvoicesRepository } from "./repositories/invoices.repository";
import { DiscountsModule } from "../discounts/discounts.module";
import { Discount } from "../discounts/entities/discount.entity";
import { UsageDiscount } from "../discounts/entities/usage-discount.entity";
import { DiscountRepository } from "../discounts/repositories/discounts.repository";
@@ -26,7 +25,6 @@ import { WalletsModule } from "../wallets/wallets.module";
LoggerModule,
BullModule.registerQueue({ name: INVOICE.INVOICE_QUEUE_NAME }),
TypeOrmModule.forFeature([Invoice, InvoiceItem, Discount, UsageDiscount]),
DiscountsModule,
UsersModule,
WalletsModule,
UtilsModule,
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
@@ -1,6 +1,6 @@
// eslint-disable-next-line import/no-named-as-default
import Decimal from "decimal.js";
import { Column, Entity, Index, ManyToOne, OneToMany } from "typeorm";
import { Column, DeleteDateColumn, Entity, Index, ManyToOne, OneToMany } from "typeorm";
import { UserSubscription } from "./user-subscription.entity";
import { BaseEntity } from "../../../common/entities/base.entity";
@@ -31,4 +31,7 @@ export class SubscriptionPlan extends BaseEntity {
@ManyToOne(() => DirectDiscount, (directDiscount) => directDiscount.subscriptionPlans, { nullable: true })
directDiscount?: DirectDiscount;
@DeleteDateColumn({ nullable: true })
deletedAt?: Date;
}
View File
@@ -144,7 +144,7 @@ export class SubscriptionsService {
}
//************************************ */
async getServiceSubscriptions(serviceId: string, queryDto: ServiceSubsQueryDto) {
async All(serviceId: string, queryDto: ServiceSubsQueryDto) {
const service = await this.danakServices.findServiceById(serviceId);
const { limit, skip } = PaginationUtils(queryDto);
@@ -237,6 +237,45 @@ export class SubscriptionsService {
}
}
//************************************ */
async getServiceSubscriptions(serviceId: string, queryDto: ServiceSubsQueryDto) {
const service = await this.danakServices.findServiceById(serviceId);
const { limit, skip } = PaginationUtils(queryDto);
const queryBuilder = this.subscriptionsPlanRepository
.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) {
queryBuilder.andWhere("subscription.name ILIKE :query", { query: `%${queryDto.q}%` });
}
if (queryDto.isActive !== undefined) {
queryBuilder.andWhere("subscription.isActive = :isActive", { isActive: queryDto.isActive === 1 });
}
const [subscriptions, count] = await queryBuilder.skip(skip).take(limit).getManyAndCount();
return {
service,
subscriptions,
count,
pagination: true,
};
}
//************************************ */
async getAllServiceSubscriptions(serviceIds: string[]) {
const subscriptions = await this.subscriptionsPlanRepository.find({
where: { service: { id: In(serviceIds) }, isActive: true },
});
return subscriptions;
}
//************************************ */
async countUserSubscriptions(userId: string) {
const subscriptionCount = await this.userSubscriptionsRepository.count({
@@ -335,8 +374,6 @@ export class SubscriptionsService {
const startOfWeek = dayjs().startOf("week").toDate();
const endOfWeek = dayjs().endOf("week").toDate();
console.log(startOfWeek, endOfWeek);
const sales = await this.userSubscriptionsRepository
.createQueryBuilder("userSubscription")
.select("DATE(userSubscription.startDate)", "date")
View File
@@ -31,6 +31,6 @@ import { UserQuickAccessRepository } from "./repositories/users-quick-access.rep
UserQuickAccessService,
],
controllers: [SubscriptionsController],
exports: [SubscriptionsService, TypeOrmModule, SubscriptionsPlanRepository, UserSubscriptionsRepository],
exports: [SubscriptionsService, SubscriptionsPlanRepository, UserSubscriptionsRepository],
})
export class SubscriptionsModule {}
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File

Some files were not shown because too many files have changed in this diff Show More