chore: add user to the invoice
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -93,7 +93,7 @@ export class DiscountRepository extends Repository<Discount> {
|
||||
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<Discount> {
|
||||
"user.email",
|
||||
]);
|
||||
|
||||
if (userId) {
|
||||
queryBuilder.andWhere("(discount.userId = :userId OR discount.userId IS NULL)", { userId });
|
||||
}
|
||||
|
||||
if (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, {
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user