diff --git a/docker-compose.yaml b/docker-compose.yaml index c95313f..5da5945 100755 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -16,64 +16,5 @@ services: POSTGRES_USER: ${DB_USER} POSTGRES_DB: ${DB_NAME} - nestjs: - build: . - ports: - - "3000:3000" - depends_on: - - pg_db - environment: - - NODE_ENV=development - volumes: - - ./src:/app/src - - ./dist:/app/dist - - prometheus: - image: prom/prometheus:latest - container_name: prometheus - ports: - - "9090:9090" - volumes: - - ./prometheus:/etc/prometheus - - prometheus_data:/prometheus - command: - - "--config.file=/etc/prometheus/prometheus.yml" - - "--storage.tsdb.path=/prometheus" - - "--web.console.libraries=/usr/share/prometheus/console_libraries" - - "--web.console.templates=/usr/share/prometheus/consoles" - restart: always - - grafana: - image: grafana/grafana:latest - container_name: grafana - ports: - - "3001:3000" - volumes: - - grafana_data:/var/lib/grafana - environment: - - GF_SECURITY_ADMIN_USER=admin - - GF_SECURITY_ADMIN_PASSWORD=admin - - GF_USERS_ALLOW_SIGN_UP=false - depends_on: - - prometheus - restart: always - -# pgadmin: -# image: dpage/pgadmin4 -# restart: always -# container_name: pgadmin -# ports: -# - 5050:80 -# environment: -# PGADMIN_DEFAULT_EMAIL: ${PGADMIN_EMAIL} -# PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_PASSWORD} -# depends_on: -# - pg_db - volumes: postgres_data: - prometheus_data: - grafana_data: -# networks: -# app_network: -# driver: bridge diff --git a/src/common/enums/message.enum.ts b/src/common/enums/message.enum.ts index fa7b6f9..b218fb0 100755 --- a/src/common/enums/message.enum.ts +++ b/src/common/enums/message.enum.ts @@ -106,6 +106,7 @@ export const enum UserMessage { INVALID_ADMIN_ID = "شناسه ادمین نامعتبر است", ADMIN_ID_SHOULD_BE_UUID = "شناسه ادمین باید یک UUID باشد", ADMIN_NOT_FOUND = "ادمین یافت نشد", + PHONE_MUST_BE_A_STRING = "شماره تلفن باید یک رشته باشد", } export const enum CommonMessage { @@ -523,6 +524,7 @@ export const enum InvoiceMessage { COUNT_MUST_BE_POSITIVE = "تعداد باید مثبت باشد", DISCOUNT_MUST_BE_BETWEEN_0_AND_100 = "تخفیف باید بین ۰ تا ۱۰۰ باشد", TOTAL_PRICE_MUST_BE_POSITIVE = "قیمت کل باید مثبت باشد", + DISCOUNT_NOT_FOR_THIS_USER = "این تخفیف برای شما معتبر نیست", } export const enum LearningMessage { diff --git a/src/modules/discounts/DTO/create-discount.dto.ts b/src/modules/discounts/DTO/create-discount.dto.ts index 8dfeb8b..1717a3c 100755 --- a/src/modules/discounts/DTO/create-discount.dto.ts +++ b/src/modules/discounts/DTO/create-discount.dto.ts @@ -13,7 +13,7 @@ import { ValidateIf, } from "class-validator"; -import { DiscountMessage } from "../../../common/enums/message.enum"; +import { DiscountMessage, UserMessage } from "../../../common/enums/message.enum"; import { DiscountType } from "../enums/discount-type.enum"; export class CreateDiscountDto { @@ -74,12 +74,12 @@ export class CreateDiscountDto { @IsOptional() @ValidateIf((o) => !o.direct) - @IsUUID("4", { message: DiscountMessage.USER_ID_MUST_BE_A_UUID }) + @IsString({ message: UserMessage.PHONE_MUST_BE_A_STRING }) @ApiPropertyOptional({ - description: "The ID of the user this discount is specific to. If not provided, the discount will be available to all users", - example: "123e4567-e89b-12d3-a456-426614174000", + description: "The phone number of the user this discount is specific to. If not provided, the discount will be available to all users", + example: "09123456789", }) - userId?: string; + userPhone?: string; // @IsString() // @IsOptional() diff --git a/src/modules/discounts/discounts.controller.ts b/src/modules/discounts/discounts.controller.ts index e2082b7..1cb83f0 100755 --- a/src/modules/discounts/discounts.controller.ts +++ b/src/modules/discounts/discounts.controller.ts @@ -8,9 +8,9 @@ import { DiscountsService } from "./providers/discounts.service"; import { AdminRoute } from "../../common/decorators/admin.decorator"; import { AuthGuards } from "../../common/decorators/auth-guard.decorator"; import { PermissionsDec } from "../../common/decorators/permission.decorator"; +import { UserDec } from "../../common/decorators/user.decorator"; import { ParamDto } from "../../common/DTO/param.dto"; import { PermissionEnum } from "../users/enums/permission.enum"; - @ApiTags("Discounts") @Controller("discounts") @AuthGuards() @@ -70,7 +70,7 @@ export class DiscountsController { @ApiOperation({ summary: "Get discount list for user" }) @Get("user-discounts") - getDiscountListForUser(@Query() queryDto: SearchDiscountQueryDto) { - return this.discountsService.getDiscountListForUser(queryDto); + getDiscountListForUser(@Query() queryDto: SearchDiscountQueryDto, @UserDec("id") userId: string) { + return this.discountsService.getDiscountListForUser(queryDto, userId); } } diff --git a/src/modules/discounts/providers/discounts.service.ts b/src/modules/discounts/providers/discounts.service.ts index 0caee1f..26e9382 100755 --- a/src/modules/discounts/providers/discounts.service.ts +++ b/src/modules/discounts/providers/discounts.service.ts @@ -7,10 +7,10 @@ import dayjs from "dayjs"; import { Decimal } from "decimal.js"; import { DataSource, FindOptionsWhere, QueryRunner } from "typeorm"; -import { CommonMessage, DiscountMessage } from "../../../common/enums/message.enum"; +import { CommonMessage, DiscountMessage, UserMessage } from "../../../common/enums/message.enum"; import { SubscriptionPlan } from "../../subscriptions/entities/subscription.entity"; import { SubscriptionsService } from "../../subscriptions/providers/subscriptions.service"; -import { UsersService } from "../../users/providers/users.service"; +import { User } from "../../users/entities/user.entity"; import { DISCOUNT } from "../constants"; import { CreateDiscountDto } from "../DTO/create-discount.dto"; import { SearchDiscountQueryDto } from "../DTO/search-discount-query.dto"; @@ -28,7 +28,6 @@ export class DiscountsService { @InjectQueue(DISCOUNT.DISCOUNT_QUEUE_NAME) private readonly discountQueue: Queue, private readonly discountRepository: DiscountRepository, private readonly subscriptionService: SubscriptionsService, - private readonly usersService: UsersService, private readonly dataSource: DataSource, ) {} @@ -159,8 +158,8 @@ export class DiscountsService { } } //************************************************************ */ - async getDiscountListForUser(queryDto: SearchDiscountQueryDto) { - const [discounts, count] = await this.discountRepository.findDiscountForUser(queryDto); + async getDiscountListForUser(queryDto: SearchDiscountQueryDto, userId: string) { + const [discounts, count] = await this.discountRepository.findDiscountForUser(queryDto, userId); return { discounts, @@ -237,15 +236,19 @@ export class DiscountsService { //************************************************************ */ private async createDiscount(createDiscountDto: CreateDiscountDto, queryRunner: QueryRunner) { - const { userId } = createDiscountDto; - if (userId) { - await this.usersService.findOneByIdWithQueryRunner(userId, queryRunner); + const { userPhone } = createDiscountDto; + let user: null | User = null; + + // + if (userPhone) { + user = await queryRunner.manager.findOne(User, { where: { phone: userPhone } }); + if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND); } const discount = queryRunner.manager.create(this.discountRepository.target, { ...createDiscountDto, applicationType: createDiscountDto.direct ? DiscountApplicationType.DIRECT : DiscountApplicationType.CODE_BASED, - user: !createDiscountDto.direct && userId ? { id: userId } : null, + user: !createDiscountDto.direct && user ? { id: user.id } : null, }); if (!createDiscountDto.direct) { diff --git a/src/modules/discounts/repositories/discounts.repository.ts b/src/modules/discounts/repositories/discounts.repository.ts index b47e94f..c6d2096 100755 --- a/src/modules/discounts/repositories/discounts.repository.ts +++ b/src/modules/discounts/repositories/discounts.repository.ts @@ -93,7 +93,7 @@ export class DiscountRepository extends Repository { return this.softDelete(id); } //************************************************************ */ - async findDiscountForUser(queryDto: SearchDiscountQueryDto, userId?: string) { + async findDiscountForUser(queryDto: SearchDiscountQueryDto, userId: string) { const { limit, skip } = PaginationUtils(queryDto); const queryBuilder = this.createQueryBuilder("discount") .where("discount.deletedAt IS NULL") @@ -118,9 +118,7 @@ export class DiscountRepository extends Repository { "user.email", ]); - if (userId) { - queryBuilder.andWhere("(discount.userId = :userId OR discount.userId IS NULL)", { userId }); - } + queryBuilder.andWhere("(discount.userId = :userId OR discount.userId IS NULL)", { userId }); if (queryDto.q) { queryBuilder.andWhere("discount.title ILIKE :query", { query: `%${queryDto.q}%` }); diff --git a/src/modules/invoices/providers/invoices.service.ts b/src/modules/invoices/providers/invoices.service.ts index a0959cf..29374ec 100755 --- a/src/modules/invoices/providers/invoices.service.ts +++ b/src/modules/invoices/providers/invoices.service.ts @@ -686,8 +686,11 @@ export class InvoicesService { const discount = await queryRunner.manager.findOne(Discount, { where: { code: discountCode, isActive: true }, + relations: { user: true }, }); + if (discount?.user?.id && discount?.user?.id !== userId) throw new BadRequestException(InvoiceMessage.DISCOUNT_NOT_FOR_THIS_USER); + if (!discount) throw new BadRequestException(InvoiceMessage.INVALID_DISCOUNT_CODE); if (discount.endDate && dayjs(discount.endDate).isBefore(dayjs())) throw new BadRequestException(InvoiceMessage.DISCOUNT_EXPIRED); diff --git a/src/templates/.gitkeep b/src/modules/templates/.gitkeep similarity index 100% rename from src/templates/.gitkeep rename to src/modules/templates/.gitkeep diff --git a/src/templates/email/admin-notification.hbs b/src/modules/templates/email/admin-notification.hbs similarity index 100% rename from src/templates/email/admin-notification.hbs rename to src/modules/templates/email/admin-notification.hbs diff --git a/src/templates/email/announcement.hbs b/src/modules/templates/email/announcement.hbs similarity index 100% rename from src/templates/email/announcement.hbs rename to src/modules/templates/email/announcement.hbs diff --git a/src/templates/email/email-verify.hbs b/src/modules/templates/email/email-verify.hbs similarity index 100% rename from src/templates/email/email-verify.hbs rename to src/modules/templates/email/email-verify.hbs diff --git a/src/templates/email/exam-cancellation.hbs b/src/modules/templates/email/exam-cancellation.hbs similarity index 100% rename from src/templates/email/exam-cancellation.hbs rename to src/modules/templates/email/exam-cancellation.hbs diff --git a/src/templates/email/invoice.hbs b/src/modules/templates/email/invoice.hbs similarity index 100% rename from src/templates/email/invoice.hbs rename to src/modules/templates/email/invoice.hbs diff --git a/src/templates/email/login.hbs b/src/modules/templates/email/login.hbs similarity index 100% rename from src/templates/email/login.hbs rename to src/modules/templates/email/login.hbs diff --git a/src/templates/email/payment-cancellation.hbs b/src/modules/templates/email/payment-cancellation.hbs similarity index 100% rename from src/templates/email/payment-cancellation.hbs rename to src/modules/templates/email/payment-cancellation.hbs diff --git a/src/templates/email/payment-reminder.hbs b/src/modules/templates/email/payment-reminder.hbs similarity index 100% rename from src/templates/email/payment-reminder.hbs rename to src/modules/templates/email/payment-reminder.hbs diff --git a/src/templates/email/ticket.hbs b/src/modules/templates/email/ticket.hbs similarity index 100% rename from src/templates/email/ticket.hbs rename to src/modules/templates/email/ticket.hbs diff --git a/src/templates/email/wallet.hbs b/src/modules/templates/email/wallet.hbs similarity index 100% rename from src/templates/email/wallet.hbs rename to src/modules/templates/email/wallet.hbs