chore: add user to the invoice
This commit is contained in:
@@ -16,64 +16,5 @@ services:
|
|||||||
POSTGRES_USER: ${DB_USER}
|
POSTGRES_USER: ${DB_USER}
|
||||||
POSTGRES_DB: ${DB_NAME}
|
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:
|
volumes:
|
||||||
postgres_data:
|
postgres_data:
|
||||||
prometheus_data:
|
|
||||||
grafana_data:
|
|
||||||
# networks:
|
|
||||||
# app_network:
|
|
||||||
# driver: bridge
|
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ export const enum UserMessage {
|
|||||||
INVALID_ADMIN_ID = "شناسه ادمین نامعتبر است",
|
INVALID_ADMIN_ID = "شناسه ادمین نامعتبر است",
|
||||||
ADMIN_ID_SHOULD_BE_UUID = "شناسه ادمین باید یک UUID باشد",
|
ADMIN_ID_SHOULD_BE_UUID = "شناسه ادمین باید یک UUID باشد",
|
||||||
ADMIN_NOT_FOUND = "ادمین یافت نشد",
|
ADMIN_NOT_FOUND = "ادمین یافت نشد",
|
||||||
|
PHONE_MUST_BE_A_STRING = "شماره تلفن باید یک رشته باشد",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum CommonMessage {
|
export const enum CommonMessage {
|
||||||
@@ -523,6 +524,7 @@ export const enum InvoiceMessage {
|
|||||||
COUNT_MUST_BE_POSITIVE = "تعداد باید مثبت باشد",
|
COUNT_MUST_BE_POSITIVE = "تعداد باید مثبت باشد",
|
||||||
DISCOUNT_MUST_BE_BETWEEN_0_AND_100 = "تخفیف باید بین ۰ تا ۱۰۰ باشد",
|
DISCOUNT_MUST_BE_BETWEEN_0_AND_100 = "تخفیف باید بین ۰ تا ۱۰۰ باشد",
|
||||||
TOTAL_PRICE_MUST_BE_POSITIVE = "قیمت کل باید مثبت باشد",
|
TOTAL_PRICE_MUST_BE_POSITIVE = "قیمت کل باید مثبت باشد",
|
||||||
|
DISCOUNT_NOT_FOR_THIS_USER = "این تخفیف برای شما معتبر نیست",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum LearningMessage {
|
export const enum LearningMessage {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
ValidateIf,
|
ValidateIf,
|
||||||
} from "class-validator";
|
} 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";
|
import { DiscountType } from "../enums/discount-type.enum";
|
||||||
|
|
||||||
export class CreateDiscountDto {
|
export class CreateDiscountDto {
|
||||||
@@ -74,12 +74,12 @@ export class CreateDiscountDto {
|
|||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ValidateIf((o) => !o.direct)
|
@ValidateIf((o) => !o.direct)
|
||||||
@IsUUID("4", { message: DiscountMessage.USER_ID_MUST_BE_A_UUID })
|
@IsString({ message: UserMessage.PHONE_MUST_BE_A_STRING })
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
description: "The ID of the user this discount is specific to. If not provided, the discount will be available to all users",
|
description: "The phone number 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",
|
example: "09123456789",
|
||||||
})
|
})
|
||||||
userId?: string;
|
userPhone?: string;
|
||||||
|
|
||||||
// @IsString()
|
// @IsString()
|
||||||
// @IsOptional()
|
// @IsOptional()
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ import { DiscountsService } from "./providers/discounts.service";
|
|||||||
import { AdminRoute } from "../../common/decorators/admin.decorator";
|
import { AdminRoute } from "../../common/decorators/admin.decorator";
|
||||||
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
||||||
import { PermissionsDec } from "../../common/decorators/permission.decorator";
|
import { PermissionsDec } from "../../common/decorators/permission.decorator";
|
||||||
|
import { UserDec } from "../../common/decorators/user.decorator";
|
||||||
import { ParamDto } from "../../common/DTO/param.dto";
|
import { ParamDto } from "../../common/DTO/param.dto";
|
||||||
import { PermissionEnum } from "../users/enums/permission.enum";
|
import { PermissionEnum } from "../users/enums/permission.enum";
|
||||||
|
|
||||||
@ApiTags("Discounts")
|
@ApiTags("Discounts")
|
||||||
@Controller("discounts")
|
@Controller("discounts")
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@@ -70,7 +70,7 @@ export class DiscountsController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Get discount list for user" })
|
@ApiOperation({ summary: "Get discount list for user" })
|
||||||
@Get("user-discounts")
|
@Get("user-discounts")
|
||||||
getDiscountListForUser(@Query() queryDto: SearchDiscountQueryDto) {
|
getDiscountListForUser(@Query() queryDto: SearchDiscountQueryDto, @UserDec("id") userId: string) {
|
||||||
return this.discountsService.getDiscountListForUser(queryDto);
|
return this.discountsService.getDiscountListForUser(queryDto, userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import dayjs from "dayjs";
|
|||||||
import { Decimal } from "decimal.js";
|
import { Decimal } from "decimal.js";
|
||||||
import { DataSource, FindOptionsWhere, QueryRunner } from "typeorm";
|
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 { SubscriptionPlan } from "../../subscriptions/entities/subscription.entity";
|
||||||
import { SubscriptionsService } from "../../subscriptions/providers/subscriptions.service";
|
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 { DISCOUNT } from "../constants";
|
||||||
import { CreateDiscountDto } from "../DTO/create-discount.dto";
|
import { CreateDiscountDto } from "../DTO/create-discount.dto";
|
||||||
import { SearchDiscountQueryDto } from "../DTO/search-discount-query.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,
|
@InjectQueue(DISCOUNT.DISCOUNT_QUEUE_NAME) private readonly discountQueue: Queue,
|
||||||
private readonly discountRepository: DiscountRepository,
|
private readonly discountRepository: DiscountRepository,
|
||||||
private readonly subscriptionService: SubscriptionsService,
|
private readonly subscriptionService: SubscriptionsService,
|
||||||
private readonly usersService: UsersService,
|
|
||||||
private readonly dataSource: DataSource,
|
private readonly dataSource: DataSource,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@@ -159,8 +158,8 @@ export class DiscountsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//************************************************************ */
|
//************************************************************ */
|
||||||
async getDiscountListForUser(queryDto: SearchDiscountQueryDto) {
|
async getDiscountListForUser(queryDto: SearchDiscountQueryDto, userId: string) {
|
||||||
const [discounts, count] = await this.discountRepository.findDiscountForUser(queryDto);
|
const [discounts, count] = await this.discountRepository.findDiscountForUser(queryDto, userId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
discounts,
|
discounts,
|
||||||
@@ -237,15 +236,19 @@ export class DiscountsService {
|
|||||||
//************************************************************ */
|
//************************************************************ */
|
||||||
|
|
||||||
private async createDiscount(createDiscountDto: CreateDiscountDto, queryRunner: QueryRunner) {
|
private async createDiscount(createDiscountDto: CreateDiscountDto, queryRunner: QueryRunner) {
|
||||||
const { userId } = createDiscountDto;
|
const { userPhone } = createDiscountDto;
|
||||||
if (userId) {
|
let user: null | User = null;
|
||||||
await this.usersService.findOneByIdWithQueryRunner(userId, queryRunner);
|
|
||||||
|
//
|
||||||
|
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, {
|
const discount = queryRunner.manager.create(this.discountRepository.target, {
|
||||||
...createDiscountDto,
|
...createDiscountDto,
|
||||||
applicationType: createDiscountDto.direct ? DiscountApplicationType.DIRECT : DiscountApplicationType.CODE_BASED,
|
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) {
|
if (!createDiscountDto.direct) {
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ export class DiscountRepository extends Repository<Discount> {
|
|||||||
return this.softDelete(id);
|
return this.softDelete(id);
|
||||||
}
|
}
|
||||||
//************************************************************ */
|
//************************************************************ */
|
||||||
async findDiscountForUser(queryDto: SearchDiscountQueryDto, userId?: string) {
|
async findDiscountForUser(queryDto: SearchDiscountQueryDto, userId: string) {
|
||||||
const { limit, skip } = PaginationUtils(queryDto);
|
const { limit, skip } = PaginationUtils(queryDto);
|
||||||
const queryBuilder = this.createQueryBuilder("discount")
|
const queryBuilder = this.createQueryBuilder("discount")
|
||||||
.where("discount.deletedAt IS NULL")
|
.where("discount.deletedAt IS NULL")
|
||||||
@@ -118,9 +118,7 @@ export class DiscountRepository extends Repository<Discount> {
|
|||||||
"user.email",
|
"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) {
|
if (queryDto.q) {
|
||||||
queryBuilder.andWhere("discount.title ILIKE :query", { query: `%${queryDto.q}%` });
|
queryBuilder.andWhere("discount.title ILIKE :query", { query: `%${queryDto.q}%` });
|
||||||
|
|||||||
@@ -686,8 +686,11 @@ export class InvoicesService {
|
|||||||
|
|
||||||
const discount = await queryRunner.manager.findOne(Discount, {
|
const discount = await queryRunner.manager.findOne(Discount, {
|
||||||
where: { code: discountCode, isActive: true },
|
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) throw new BadRequestException(InvoiceMessage.INVALID_DISCOUNT_CODE);
|
||||||
|
|
||||||
if (discount.endDate && dayjs(discount.endDate).isBefore(dayjs())) throw new BadRequestException(InvoiceMessage.DISCOUNT_EXPIRED);
|
if (discount.endDate && dayjs(discount.endDate).isBefore(dayjs())) throw new BadRequestException(InvoiceMessage.DISCOUNT_EXPIRED);
|
||||||
|
|||||||
Reference in New Issue
Block a user