fix: bug in thre login process

This commit is contained in:
mahyargdz
2025-03-02 16:28:07 +03:30
parent 4d0344c376
commit 28486d64ee
21 changed files with 606 additions and 588 deletions
+1 -2
View File
@@ -22,7 +22,7 @@ import { ContactUsModule } from "./modules/contact-us/contact-us.module";
import { CriticismModule } from "./modules/criticisms/criticisms.module"; import { CriticismModule } from "./modules/criticisms/criticisms.module";
import { DanakServicesModule } from "./modules/danak-services/danak-services.module"; import { DanakServicesModule } from "./modules/danak-services/danak-services.module";
import { DashboardModule } from "./modules/dashboards/dashboard.module"; import { DashboardModule } from "./modules/dashboards/dashboard.module";
import { DiscountModule } from "./modules/discounts/discounts.module"; // import { DiscountModule } from "./modules/discounts/discounts.module";
import { InvoicesModule } from "./modules/invoices/invoices.module"; import { InvoicesModule } from "./modules/invoices/invoices.module";
import { LearningModule } from "./modules/learnings/learning.module"; import { LearningModule } from "./modules/learnings/learning.module";
import { NotificationModule } from "./modules/notifications/notifications.module"; import { NotificationModule } from "./modules/notifications/notifications.module";
@@ -59,7 +59,6 @@ import { WalletsModule } from "./modules/wallets/wallets.module";
InvoicesModule, InvoicesModule,
SubscriptionsModule, SubscriptionsModule,
LearningModule, LearningModule,
DiscountModule,
AdsModule, AdsModule,
AddressModule, AddressModule,
DashboardModule, DashboardModule,
+41
View File
@@ -0,0 +1,41 @@
import {
ValidationArguments,
ValidationOptions,
ValidatorConstraint,
ValidatorConstraintInterface,
registerDecorator,
} from "class-validator";
export function isValidIranianNationalCode(nationalCode: string): boolean {
if (!/^\d{10}$/.test(nationalCode)) return false;
const digits = nationalCode.split("").map(Number);
const controlDigit = digits[9];
const sum = digits.slice(0, 9).reduce((acc, digit, index) => acc + digit * (10 - index), 0);
const remainder = sum % 11;
return (remainder < 2 && controlDigit === remainder) || (remainder >= 2 && controlDigit === 11 - remainder);
}
@ValidatorConstraint({ async: false })
export class IsNationalCodeConstraint implements ValidatorConstraintInterface {
validate(value: unknown, _args: ValidationArguments) {
return typeof value === "string" && isValidIranianNationalCode(value);
}
defaultMessage(_args: ValidationArguments) {
return `Invalid Iranian national code :${_args.value}`;
}
}
export function IsNationalCode(validationOptions?: ValidationOptions) {
return function (object: object, propertyName: string) {
registerDecorator({
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
constraints: [],
validator: IsNationalCodeConstraint,
});
};
}
+11 -13
View File
@@ -55,6 +55,7 @@ export const enum AuthMessage {
PHONE_SHOULD_BE_11_DIGIT = "شماره تلفن باید ۱۱ رقم باشد", PHONE_SHOULD_BE_11_DIGIT = "شماره تلفن باید ۱۱ رقم باشد",
TIMESTAMP_NOT_EMPTY = "زمان نمی‌تواند خالی باشد", TIMESTAMP_NOT_EMPTY = "زمان نمی‌تواند خالی باشد",
ADMIN_CAN_NOT_LOGIN = "ادمین نمی‌تواند وارد شود", ADMIN_CAN_NOT_LOGIN = "ادمین نمی‌تواند وارد شود",
NATIONAL_CODE_INVALID = "کد ملی نامعتبر است",
} }
export const enum UserMessage { export const enum UserMessage {
@@ -472,21 +473,18 @@ export const enum EmailMessage {
} }
export const enum FinancialMessage { export const enum FinancialMessage {
GENDER_TYPE_INVALID = "نوع جنسیت باید یکی از مقادیر FEMALE یا MALE باشد", GENDER_TYPE_REQUIRED = "نوع جنسیت مورد نیاز است",
ECONOMIC_CODE_INVALID = "کد اقتصادی باید یک رشته باشد", FATHER_NAME_REQUIRED = "نام پدر مورد نیاز است",
FATHER_NAME_INVALID = "نام پدر باید یک رشته باشد", FATHER_NAME_INVALID = "نام پدر باید یک رشته باشد",
NATIONALITY_INVALID = "ملیت باید یک رشته باشد", NATIONALITY_REQUIRED = "ملیت مورد نیاز است",
REGISTRATION_ID_INVALID = "شناسه ثبت باید یک رشته باشد", ADDRESS_REQUIRED = "آدرس مورد نیاز است",
ADDRESS_ID_INVALID = "ادرس باید یک رشته باشد", ADDRESS_ID_INVALID = "شناسه آدرس باید یک UUID معتبر باشد",
POSTALCODE_INVALID = "کد پستی باید یک رشته باشد", POSTAL_CODE_REQUIRED = "کد پستی مورد نیاز است",
NATIONAL_ID_INVALID = "شناسه ملی باید یک رشته باشد",
FIXED_PHONE_INVALID = "شماره تماس ثابت باید یک رشته باشد",
USER_ID_REQUIRED = "شناسه کاربر مورد نیاز است",
CITY_ID_REQUIRED = "شناسه شهر مورد نیاز است", CITY_ID_REQUIRED = "شناسه شهر مورد نیاز است",
USER_ID_SHOULD_BE_A_UUID = "شناسه کاربر باید یک UUID معتبر باشد", CITY_ID_INVALID = "شناسه شهر باید یک عددی باشد",
FINANCIAL_INFO_ALREADY_EXISTS = "دیتا اطلاعات مالی وجود دارد", BIRTH_DATE_REQUIRED = "تاریخ تولد مورد نیاز است",
FINANCIAL_INFO_NOT_FOUND = "دیتا اطلاعات مالی یافت نشد", ADDRESS_STRING = "آدرس باید یک رشته باشد",
SEARCH_QUERY_MUST_BE_A_STRING = "رشته جستجو باید یک رشته باشد", POSTAL_CODE_NUMBER_STRING = "کد پستی باید یک رشته عددی باشد",
} }
export const enum AdminMessage { export const enum AdminMessage {
@@ -4,9 +4,11 @@ import { ApiOperation } from "@nestjs/swagger";
import { SearchCitiesDto } from "./DTO/cities-search-query.dto"; import { SearchCitiesDto } from "./DTO/cities-search-query.dto";
import { SearchProvincesDto } from "./DTO/provinces-search-query.dto"; import { SearchProvincesDto } from "./DTO/provinces-search-query.dto";
import { AddressService } from "./providers/address.service"; import { AddressService } from "./providers/address.service";
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
import { ParamNumberIdDto } from "../../common/DTO/param.dto"; import { ParamNumberIdDto } from "../../common/DTO/param.dto";
@Controller("address") @Controller("address")
@AuthGuards()
export class AddressController { export class AddressController {
constructor(private readonly addressService: AddressService) {} constructor(private readonly addressService: AddressService) {}
+7 -11
View File
@@ -1,20 +1,16 @@
import { Column, Entity, ManyToOne, OneToOne } from "typeorm"; import { Column, Entity, ManyToOne } from "typeorm";
import { City } from "./city.entity"; import { City } from "./city.entity";
import { BaseEntity } from "../../../common/entities/base.entity"; import { BaseEntity } from "../../../common/entities/base.entity";
import { User } from "../../users/entities/user.entity";
@Entity() @Entity()
export class Address extends BaseEntity { export class Address extends BaseEntity {
@Column({ type: "varchar", length: 255, nullable: true }) @ManyToOne(() => City, { eager: true, onDelete: "SET NULL" })
address: string;
@Column({ type: "varchar", length: 10, nullable: true })
postalCode: string;
@ManyToOne(() => City, (city) => city.addresses, { nullable: false })
city: City; city: City;
@OneToOne(() => User, (user) => user.address, { onDelete: "CASCADE" }) @Column({ type: "varchar", length: 150 })
user: User; postalCode: string;
@Column({ type: "text" })
address: string;
} }
+1 -5
View File
@@ -1,6 +1,5 @@
import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
import { Address } from "./address.entity";
import { Province } from "./province.entity"; import { Province } from "./province.entity";
@Entity() @Entity()
@@ -11,9 +10,6 @@ export class City {
@Column({ type: "varchar", length: 255 }) @Column({ type: "varchar", length: 255 })
name: string; name: string;
@OneToMany(() => Address, (address) => address.city)
addresses: Address[];
@ManyToOne(() => Province, (province) => province.cities, { nullable: false }) @ManyToOne(() => Province, (province) => province.cities, { nullable: false })
province: Province; province: Province;
} }
@@ -1,6 +1,7 @@
import { ApiProperty } from "@nestjs/swagger"; import { ApiProperty } from "@nestjs/swagger";
import { IsMobilePhone, IsNotEmpty, IsNumberString, IsString, Length, MinLength } from "class-validator"; import { IsMobilePhone, IsNotEmpty, IsNumberString, IsString, Length, MinLength } from "class-validator";
import { IsNationalCode } from "../../../common/decorators/is-national-code.decorator";
import { AuthMessage } from "../../../common/enums/message.enum"; import { AuthMessage } from "../../../common/enums/message.enum";
export class CompleteRegistrationDto { export class CompleteRegistrationDto {
@@ -41,6 +42,7 @@ export class CompleteRegistrationDto {
@IsNotEmpty({ message: AuthMessage.NATIONAL_NOT_EMPTY }) @IsNotEmpty({ message: AuthMessage.NATIONAL_NOT_EMPTY })
@IsNumberString({ no_symbols: true }, { message: AuthMessage.NATIONAL_CODE_INCORRECT }) @IsNumberString({ no_symbols: true }, { message: AuthMessage.NATIONAL_CODE_INCORRECT })
@Length(10, 10, { message: AuthMessage.NATIONAL_CODE_INCORRECT }) @Length(10, 10, { message: AuthMessage.NATIONAL_CODE_INCORRECT })
@IsNationalCode({ message: AuthMessage.NATIONAL_CODE_INVALID })
@ApiProperty({ description: "iranian format (10 char)", example: "4569852169" }) @ApiProperty({ description: "iranian format (10 char)", example: "4569852169" })
nationalCode: string; nationalCode: string;
@@ -1,12 +1,12 @@
import { ApiPropertyOptional } from "@nestjs/swagger"; // import { ApiPropertyOptional } from "@nestjs/swagger";
import { IsOptional, IsString } from "class-validator"; // import { IsOptional, IsString } from "class-validator";
import { PaginationDto } from "../../../common/DTO/pagination.dto"; // import { PaginationDto } from "../../../common/DTO/pagination.dto";
import { FinancialMessage } from "../../../common/enums/message.enum"; // import { FinancialMessage } from "../../../common/enums/message.enum";
export class SearchDiscountsDto extends PaginationDto { // export class SearchDiscountsDto extends PaginationDto {
@IsOptional() // @IsOptional()
@IsString({ message: FinancialMessage.SEARCH_QUERY_MUST_BE_A_STRING }) // @IsString({ message: FinancialMessage.SEARCH_QUERY_MUST_BE_A_STRING })
@ApiPropertyOptional({ description: "search query", example: "search query" }) // @ApiPropertyOptional({ description: "search query", example: "search query" })
q: string; // q: string;
} // }
+45 -45
View File
@@ -1,58 +1,58 @@
import { Body, Controller, Get, Param, Patch, Post, Query } from "@nestjs/common"; // import { Body, Controller, Get, Param, Patch, Post, Query } from "@nestjs/common";
import { ApiOperation, ApiTags } from "@nestjs/swagger"; // import { ApiOperation, ApiTags } from "@nestjs/swagger";
import { CreateDiscountDto } from "./DTO/create-discount.dto"; // import { CreateDiscountDto } from "./DTO/create-discount.dto";
import { SearchDiscountsDto } from "./DTO/discount-search-query.dto"; // // import { SearchDiscountsDto } from "./DTO/discount-search-query.dto";
import { DiscountService } from "./providers/discounts.service"; // import { DiscountService } from "./providers/discounts.service";
import { AuthGuards } from "../../common/decorators/auth-guard.decorator"; // import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
import { UserDec } from "../../common/decorators/user.decorator"; // import { UserDec } from "../../common/decorators/user.decorator";
import { ParamDto } from "../../common/DTO/param.dto"; // import { ParamDto } from "../../common/DTO/param.dto";
@ApiTags("discounts") // @ApiTags("discounts")
@AuthGuards() // @AuthGuards()
@Controller("discounts") // @Controller("discounts")
export class DiscountController { // export class DiscountController {
constructor(private readonly discountService: DiscountService) {} // constructor(private readonly discountService: DiscountService) {}
//************************************ */ // //************************************ */
@ApiOperation({ summary: "Create a new discount" }) // @ApiOperation({ summary: "Create a new discount" })
@Post() // @Post()
async create(@Body() createDiscountDto: CreateDiscountDto) { // async create(@Body() createDiscountDto: CreateDiscountDto) {
return this.discountService.create(createDiscountDto); // return this.discountService.create(createDiscountDto);
} // }
//************************************ */ // //************************************ */
@ApiOperation({ summary: "Retrieve all discounts" }) // @ApiOperation({ summary: "Retrieve all discounts" })
@Get() // @Get()
async findAll(@Query() queryDto: SearchDiscountsDto) { // async findAll(@Query() queryDto: SearchDiscountsDto) {
return this.discountService.findAll(queryDto); // return this.discountService.findAll(queryDto);
} // }
//************************************ */ // //************************************ */
@ApiOperation({ summary: "Retrieve a discount by ID" }) // @ApiOperation({ summary: "Retrieve a discount by ID" })
@Get(":id") // @Get(":id")
async findOne(@Param() paramDto: ParamDto) { // async findOne(@Param() paramDto: ParamDto) {
return this.discountService.findOne(paramDto.id); // return this.discountService.findOne(paramDto.id);
} // }
//************************************ */ // //************************************ */
@ApiOperation({ summary: "Retrieve a discounts by userID" }) // @ApiOperation({ summary: "Retrieve a discounts by userID" })
@Get("/user-discounts") // @Get("/user-discounts")
async findDiscountsByUsers(@Query() queryDto: SearchDiscountsDto, @UserDec("id") userId: string) { // async findDiscountsByUsers(@Query() queryDto: SearchDiscountsDto, @UserDec("id") userId: string) {
return this.discountService.findDiscountsByUser(queryDto, userId); // return this.discountService.findDiscountsByUser(queryDto, userId);
} // }
//************************************ */ // //************************************ */
@ApiOperation({ summary: "Toggle active/deactive discount" }) // @ApiOperation({ summary: "Toggle active/deactive discount" })
@Patch(":id/toggle") // @Patch(":id/toggle")
async toggleActive(@Param() paramDto: ParamDto) { // async toggleActive(@Param() paramDto: ParamDto) {
return this.discountService.toggleActive(paramDto.id); // return this.discountService.toggleActive(paramDto.id);
} // }
//************************************ */ // //************************************ */
} // }
+14 -14
View File
@@ -1,16 +1,16 @@
import { Module } from "@nestjs/common"; // import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm"; // import { TypeOrmModule } from "@nestjs/typeorm";
import { DiscountController } from "./discounts.controller"; // import { DiscountController } from "./discounts.controller";
import { Discount } from "./entities/discount.entity"; // import { Discount } from "./entities/discount.entity";
import { DiscountService } from "./providers/discounts.service"; // import { DiscountService } from "./providers/discounts.service";
import { DiscountRepository } from "./repositories/discount.repository"; // import { DiscountRepository } from "./repositories/discount.repository";
import { SubscriptionsModule } from "../subscriptions/subscriptions.module"; // import { SubscriptionsModule } from "../subscriptions/subscriptions.module";
import { UsersModule } from "../users/users.module"; // import { UsersModule } from "../users/users.module";
@Module({ // @Module({
imports: [TypeOrmModule.forFeature([Discount]), SubscriptionsModule, UsersModule], // imports: [TypeOrmModule.forFeature([Discount]), SubscriptionsModule, UsersModule],
providers: [DiscountService, DiscountRepository], // providers: [DiscountService, DiscountRepository],
controllers: [DiscountController], // controllers: [DiscountController],
}) // })
export class DiscountModule {} // export class DiscountModule {}
@@ -1,182 +1,182 @@
import { randomBytes } from "node:crypto"; // import { randomBytes } from "node:crypto";
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; // import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
import { In } from "typeorm"; // import { In } from "typeorm";
import { CommonMessage, DiscountMessage, SubscriptionMessage, UserMessage } from "../../../common/enums/message.enum"; // import { CommonMessage, DiscountMessage, SubscriptionMessage, UserMessage } from "../../../common/enums/message.enum";
import { SubscriptionsPlanRepository } from "../../subscriptions/repositories/subscriptions.repository"; // import { SubscriptionsPlanRepository } from "../../subscriptions/repositories/subscriptions.repository";
import { UserRepository } from "../../users/repositories/users.repository"; // import { UserRepository } from "../../users/repositories/users.repository";
import { PaginationUtils } from "../../utils/providers/pagination.utils"; // import { PaginationUtils } from "../../utils/providers/pagination.utils";
import { CreateDiscountDto } from "../DTO/create-discount.dto"; // import { CreateDiscountDto } from "../DTO/create-discount.dto";
import { SearchDiscountsDto } from "../DTO/discount-search-query.dto"; // import { SearchDiscountsDto } from "../DTO/discount-search-query.dto";
import { DiscountRepository } from "../repositories/discount.repository"; // import { DiscountRepository } from "../repositories/discount.repository";
@Injectable() // @Injectable()
export class DiscountService { // export class DiscountService {
constructor( // constructor(
private readonly discountRepository: DiscountRepository, // private readonly discountRepository: DiscountRepository,
private readonly subscriptionPlanRepository: SubscriptionsPlanRepository, // private readonly subscriptionPlanRepository: SubscriptionsPlanRepository,
private readonly userRepository: UserRepository, // private readonly userRepository: UserRepository,
) {} // ) {}
/******************************************** */ // /******************************************** */
async create(createDiscountDto: CreateDiscountDto) { // async create(createDiscountDto: CreateDiscountDto) {
const { subscriptionPlanIds, ...discountData } = createDiscountDto; // const { subscriptionPlanIds, ...discountData } = createDiscountDto;
const code = await this.generateUniqueCouponCode(); // const code = await this.generateUniqueCouponCode();
const discount = this.discountRepository.create({ // const discount = this.discountRepository.create({
...discountData, // ...discountData,
code, // code,
}); // });
if (createDiscountDto.subscriptionPlanIds && createDiscountDto.subscriptionPlanIds.length) { // if (createDiscountDto.subscriptionPlanIds && createDiscountDto.subscriptionPlanIds.length) {
const subscriptionPlans = await this.subscriptionPlanRepository.find({ // const subscriptionPlans = await this.subscriptionPlanRepository.find({
where: { id: In(createDiscountDto.subscriptionPlanIds) }, // where: { id: In(createDiscountDto.subscriptionPlanIds) },
}); // });
if (subscriptionPlans.length !== createDiscountDto.subscriptionPlanIds.length) { // if (subscriptionPlans.length !== createDiscountDto.subscriptionPlanIds.length) {
throw new NotFoundException(SubscriptionMessage.NOT_FOUND); // throw new NotFoundException(SubscriptionMessage.NOT_FOUND);
} // }
discount.subscriptionPlans = subscriptionPlans; // discount.subscriptionPlans = subscriptionPlans;
} // }
if (createDiscountDto.userIds) { // if (createDiscountDto.userIds) {
const users = await this.userRepository.find({ where: { id: In(createDiscountDto.userIds) } }); // const users = await this.userRepository.find({ where: { id: In(createDiscountDto.userIds) } });
if (users.length !== createDiscountDto.userIds.length) { // if (users.length !== createDiscountDto.userIds.length) {
throw new NotFoundException(UserMessage.USER_NOT_FOUND); // throw new NotFoundException(UserMessage.USER_NOT_FOUND);
} // }
discount.users = users; // discount.users = users;
} // }
await this.discountRepository.save(discount); // await this.discountRepository.save(discount);
return { // return {
message: CommonMessage.CREATED, // message: CommonMessage.CREATED,
discount, // discount,
}; // };
} // }
/******************************************** */ // /******************************************** */
async findByCode(code: string) { // async findByCode(code: string) {
const discount = await this.discountRepository.findOne({ // const discount = await this.discountRepository.findOne({
where: { code, isActive: true }, // where: { code, isActive: true },
relations: ["subscriptionPlans"], // relations: ["subscriptionPlans"],
}); // });
if (!discount) { // if (!discount) {
throw new NotFoundException(DiscountMessage.NOT_FOUND); // throw new NotFoundException(DiscountMessage.NOT_FOUND);
} // }
return { discount }; // return { discount };
} // }
/******************************************** */ // /******************************************** */
async findDiscountsByUser(queryDto: SearchDiscountsDto, userId: string) { // async findDiscountsByUser(queryDto: SearchDiscountsDto, userId: string) {
const user = await this.userRepository.findOneBy({ // const user = await this.userRepository.findOneBy({
id: userId, // id: userId,
}); // });
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND); // if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
const { limit, skip } = PaginationUtils(queryDto); // const { limit, skip } = PaginationUtils(queryDto);
const queryBuilder = this.discountRepository.createQueryBuilder("discount"); // const queryBuilder = this.discountRepository.createQueryBuilder("discount");
queryBuilder // queryBuilder
.leftJoin("discount.users", "user") // .leftJoin("discount.users", "user")
.where("user.id = :userId", { userId: user.id }) // .where("user.id = :userId", { userId: user.id })
.leftJoinAndSelect("discount.subscriptionPlans", "subscriptionPlans") // .leftJoinAndSelect("discount.subscriptionPlans", "subscriptionPlans")
.leftJoin("subscriptionPlans.service", "service") // .leftJoin("subscriptionPlans.service", "service")
.addSelect(["service.id", "service.name", "service.title", "service.link", "service.icon", "service.description"]); // .addSelect(["service.id", "service.name", "service.title", "service.link", "service.icon", "service.description"]);
if (queryDto.q) { // if (queryDto.q) {
queryBuilder.andWhere("discount.code ILIKE :q", { q: `%${queryDto.q}%` }); // queryBuilder.andWhere("discount.code ILIKE :q", { q: `%${queryDto.q}%` });
} // }
queryBuilder.orderBy("discount.createdAt", "DESC").skip(skip).take(limit); // queryBuilder.orderBy("discount.createdAt", "DESC").skip(skip).take(limit);
const [discounts, count] = await queryBuilder.getManyAndCount(); // const [discounts, count] = await queryBuilder.getManyAndCount();
return { // return {
discounts, // discounts,
count, // count,
paginate: true, // paginate: true,
}; // };
} // }
/******************************************** */ // /******************************************** */
async findAll(queryDto: SearchDiscountsDto) { // async findAll(queryDto: SearchDiscountsDto) {
const { limit, skip } = PaginationUtils(queryDto); // const { limit, skip } = PaginationUtils(queryDto);
const queryBuilder = this.discountRepository.createQueryBuilder("discount"); // const queryBuilder = this.discountRepository.createQueryBuilder("discount");
queryBuilder // queryBuilder
.leftJoinAndSelect("discount.subscriptionPlans", "subscriptionPlans") // .leftJoinAndSelect("discount.subscriptionPlans", "subscriptionPlans")
.leftJoin("subscriptionPlans.service", "service") // .leftJoin("subscriptionPlans.service", "service")
.addSelect(["service.id", "service.name", "service.title", "service.link", "service.icon", "service.description"]) // .addSelect(["service.id", "service.name", "service.title", "service.link", "service.icon", "service.description"])
.leftJoinAndSelect("discount.users", "users"); // .leftJoinAndSelect("discount.users", "users");
if (queryDto.q) { // if (queryDto.q) {
queryBuilder.andWhere("discount.code ILIKE :q", { q: `%${queryDto.q}%` }); // queryBuilder.andWhere("discount.code ILIKE :q", { q: `%${queryDto.q}%` });
} // }
queryBuilder.orderBy("discount.createdAt", "DESC").skip(skip).take(limit); // queryBuilder.orderBy("discount.createdAt", "DESC").skip(skip).take(limit);
const [discounts, count] = await queryBuilder.getManyAndCount(); // const [discounts, count] = await queryBuilder.getManyAndCount();
return { // return {
discounts, // discounts,
count, // count,
paginate: true, // paginate: true,
}; // };
} // }
/******************************************** */ // /******************************************** */
async findOne(id: string) { // async findOne(id: string) {
const discount = await this.discountRepository.findOne({ // const discount = await this.discountRepository.findOne({
where: { id }, // where: { id },
relations: ["subscriptionPlans"], // relations: ["subscriptionPlans"],
}); // });
if (!discount) throw new BadRequestException(DiscountMessage.NOT_FOUND); // if (!discount) throw new BadRequestException(DiscountMessage.NOT_FOUND);
return { discount }; // return { discount };
} // }
/******************************************** */ // /******************************************** */
async toggleActive(id: string) { // async toggleActive(id: string) {
const discount = await this.discountRepository.findOne({ where: { id } }); // const discount = await this.discountRepository.findOne({ where: { id } });
if (!discount) { // if (!discount) {
throw new BadRequestException(DiscountMessage.NOT_FOUND); // throw new BadRequestException(DiscountMessage.NOT_FOUND);
} // }
discount.isActive = !discount.isActive; // discount.isActive = !discount.isActive;
const updatedDiscount = await this.discountRepository.save(discount); // const updatedDiscount = await this.discountRepository.save(discount);
return { // return {
message: CommonMessage.UPDATE_SUCCESS, // message: CommonMessage.UPDATE_SUCCESS,
updatedDiscount, // updatedDiscount,
}; // };
} // }
/******************************************** */ // /******************************************** */
async generateUniqueCouponCode(): Promise<string> { // async generateUniqueCouponCode(): Promise<string> {
const code = this.generateCouponCode(); // const code = this.generateCouponCode();
const exists = await this.discountRepository.findOne({ where: { code } }); // const exists = await this.discountRepository.findOne({ where: { code } });
if (exists) return this.generateUniqueCouponCode(); // if (exists) return this.generateUniqueCouponCode();
return code; // return code;
} // }
/******************************************** */ // /******************************************** */
private generateCouponCode(length = 8) { // private generateCouponCode(length = 8) {
return randomBytes(length).toString("hex").slice(0, length).toUpperCase(); // return randomBytes(length).toString("hex").slice(0, length).toUpperCase();
} // }
} // }
+37 -37
View File
@@ -1,46 +1,46 @@
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; // import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { IsNotEmpty, IsOptional, IsString } from "class-validator"; // import { IsNotEmpty, IsOptional, IsString } from "class-validator";
import { FinancialMessage } from "../../../common/enums/message.enum"; // import { FinancialMessage } from "../../../common/enums/message.enum";
export class CreateLegalUserDto { // export class CreateLegalUserDto {
@ApiProperty({ description: "Economic code" }) // @ApiProperty({ description: "Economic code" })
@IsOptional() // @IsOptional()
@IsString({ message: FinancialMessage.ECONOMIC_CODE_INVALID }) // @IsString({ message: FinancialMessage.ECONOMIC_CODE_INVALID })
economicCode?: string; // economicCode?: string;
@ApiProperty({ description: "Company registered name" }) // @ApiProperty({ description: "Company registered name" })
@IsOptional() // @IsOptional()
@IsString({ message: FinancialMessage.ECONOMIC_CODE_INVALID }) // @IsString({ message: FinancialMessage.ECONOMIC_CODE_INVALID })
companyRegisteredName?: string; // companyRegisteredName?: string;
@ApiProperty({ description: "Registration ID" }) // @ApiProperty({ description: "Registration ID" })
@IsOptional() // @IsOptional()
@IsString({ message: FinancialMessage.REGISTRATION_ID_INVALID }) // @IsString({ message: FinancialMessage.REGISTRATION_ID_INVALID })
registrationId?: string; // registrationId?: string;
@ApiProperty({ description: "national Id" }) // @ApiProperty({ description: "national Id" })
@IsOptional() // @IsOptional()
@IsString({ message: FinancialMessage.NATIONAL_ID_INVALID }) // @IsString({ message: FinancialMessage.NATIONAL_ID_INVALID })
nationalId?: string; // nationalId?: string;
@ApiPropertyOptional({ description: "number" }) // @ApiPropertyOptional({ description: "number" })
@IsOptional() // @IsOptional()
@IsString({ message: FinancialMessage.FIXED_PHONE_INVALID }) // @IsString({ message: FinancialMessage.FIXED_PHONE_INVALID })
number?: string; // number?: string;
@ApiPropertyOptional({ description: "postalCode" }) // @ApiPropertyOptional({ description: "postalCode" })
@IsOptional() // @IsOptional()
@IsString({ message: FinancialMessage.NATIONAL_ID_INVALID }) // @IsString({ message: FinancialMessage.NATIONAL_ID_INVALID })
postalCode?: string; // postalCode?: string;
@ApiPropertyOptional({ description: "address" }) // @ApiPropertyOptional({ description: "address" })
@IsOptional() // @IsOptional()
@IsString({ message: FinancialMessage.NATIONAL_ID_INVALID }) // @IsString({ message: FinancialMessage.NATIONAL_ID_INVALID })
address?: string; // address?: string;
@ApiPropertyOptional({ description: "City id", example: "1" }) // @ApiPropertyOptional({ description: "City id", example: "1" })
@IsOptional() // @IsOptional()
@IsNotEmpty({ message: FinancialMessage.CITY_ID_REQUIRED }) // @IsNotEmpty({ message: FinancialMessage.CITY_ID_REQUIRED })
cityId: string; // cityId: string;
} // }
+26 -25
View File
@@ -1,37 +1,38 @@
import { ApiPropertyOptional } from "@nestjs/swagger"; import { ApiProperty } from "@nestjs/swagger";
import { IsEnum, IsNotEmpty, IsOptional, IsString } from "class-validator"; import { IsEnum, IsInt, IsNotEmpty, IsNumberString, IsString } from "class-validator";
import { FinancialMessage } from "../../../common/enums/message.enum"; import { FinancialMessage } from "../../../common/enums/message.enum";
import { GenderType } from "../enums/gender-type.enum"; import { GenderEnum } from "../enums/gender-type.enum";
import { NationalityEnum } from "../enums/nationality.enum";
export class CreateRealUserDto { export class CreateRealUserDto {
@ApiPropertyOptional({ enum: GenderType, description: "Gender type" }) @IsNotEmpty({ message: FinancialMessage.GENDER_TYPE_REQUIRED })
@IsOptional() @IsEnum(GenderEnum)
@IsEnum(GenderType, { message: FinancialMessage.GENDER_TYPE_INVALID }) @ApiProperty({ enum: GenderEnum, description: "Gender type", example: GenderEnum.MALE })
gender?: GenderType; gender: GenderEnum;
@ApiPropertyOptional({ description: "Father name" }) @IsNotEmpty({ message: FinancialMessage.FATHER_NAME_REQUIRED })
@IsOptional()
@IsString({ message: FinancialMessage.FATHER_NAME_INVALID }) @IsString({ message: FinancialMessage.FATHER_NAME_INVALID })
fatherName?: string; @ApiProperty({ description: "Father name" })
fatherName: string;
@ApiPropertyOptional({ description: "nationality" }) @IsNotEmpty({ message: FinancialMessage.NATIONALITY_REQUIRED })
@IsOptional() @IsEnum(NationalityEnum)
@IsString({ message: FinancialMessage.NATIONALITY_INVALID }) @ApiProperty({ description: "Nationality" })
nationality?: string; nationality: string;
@ApiPropertyOptional({ description: "Address" }) @IsNotEmpty({ message: FinancialMessage.ADDRESS_REQUIRED })
@IsOptional() @IsString({ message: FinancialMessage.ADDRESS_STRING })
@IsString({ message: FinancialMessage.ADDRESS_ID_INVALID }) @ApiProperty({ description: "Address" })
address?: string; address: string;
@ApiPropertyOptional({ description: "PostalCode" }) @IsNotEmpty({ message: FinancialMessage.POSTAL_CODE_REQUIRED })
@IsOptional() @IsNumberString({ no_symbols: true }, { message: FinancialMessage.POSTAL_CODE_NUMBER_STRING })
@IsString({ message: FinancialMessage.ADDRESS_ID_INVALID }) @ApiProperty({ description: "Postal code", example: "1234567890" })
postalCode?: string; postalCode: string;
@ApiPropertyOptional({ description: "City id", example: "1" })
@IsOptional()
@IsNotEmpty({ message: FinancialMessage.CITY_ID_REQUIRED }) @IsNotEmpty({ message: FinancialMessage.CITY_ID_REQUIRED })
cityId: string; @IsInt({ message: FinancialMessage.CITY_ID_INVALID })
@ApiProperty({ description: "City ID", example: "1" })
cityId: number;
} }
+77 -75
View File
@@ -1,92 +1,94 @@
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; // import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { IsEmail, IsMobilePhone, IsNotEmpty, IsNumberString, IsOptional, IsString, IsUrl, Length, MinLength } from "class-validator"; // import { IsEmail, IsMobilePhone, IsNotEmpty, IsNumberString, IsOptional, IsString, IsUrl, Length, MinLength } from "class-validator";
import { AuthMessage, FinancialMessage, UserMessage } from "../../../common/enums/message.enum"; // import { IsNationalCode } from "../../../common/decorators/is-national-code.decorator";
// import { AuthMessage, FinancialMessage, UserMessage } from "../../../common/enums/message.enum";
export class CreateCustomerDto { // export class CreateCustomerDto {
@IsNotEmpty({ message: AuthMessage.PHONE_NOT_EMPTY }) // @IsNotEmpty({ message: AuthMessage.PHONE_NOT_EMPTY })
@IsMobilePhone("fa-IR", {}, { message: AuthMessage.INVALID_PHONE_FORMAT }) // @IsMobilePhone("fa-IR", {}, { message: AuthMessage.INVALID_PHONE_FORMAT })
@Length(11, 11, { message: AuthMessage.PHONE_SHOULD_BE_11_DIGIT }) // @Length(11, 11, { message: AuthMessage.PHONE_SHOULD_BE_11_DIGIT })
@ApiProperty({ description: "phone number", default: "09922320740" }) // @ApiProperty({ description: "phone number", default: "09922320740" })
phone: string; // phone: string;
@IsNotEmpty({ message: AuthMessage.FIRST_NAME_NOT_EMPTY }) // @IsNotEmpty({ message: AuthMessage.FIRST_NAME_NOT_EMPTY })
@IsString({ message: AuthMessage.FIRST_NAME_NOT_EMPTY }) // @IsString({ message: AuthMessage.FIRST_NAME_NOT_EMPTY })
@Length(2, 50, { message: AuthMessage.FIRST_NAME_SHOULD_BE_BETWEEN_2_AND_50 }) // @Length(2, 50, { message: AuthMessage.FIRST_NAME_SHOULD_BE_BETWEEN_2_AND_50 })
@ApiProperty({ description: "First name", example: "mahyar" }) // @ApiProperty({ description: "First name", example: "mahyar" })
firstName: string; // firstName: string;
@IsNotEmpty({ message: AuthMessage.LAST_NAME_NOT_EMPTY }) // @IsNotEmpty({ message: AuthMessage.LAST_NAME_NOT_EMPTY })
@IsString({ message: AuthMessage.LAST_NAME_NOT_EMPTY }) // @IsString({ message: AuthMessage.LAST_NAME_NOT_EMPTY })
@Length(2, 50, { message: AuthMessage.LAST_NAME_SHOULD_BE_BETWEEN_2_AND_50 }) // @Length(2, 50, { message: AuthMessage.LAST_NAME_SHOULD_BE_BETWEEN_2_AND_50 })
@ApiProperty({ description: "Last name", example: "jamshidi" }) // @ApiProperty({ description: "Last name", example: "jamshidi" })
lastName: string; // lastName: string;
@IsNotEmpty({ message: AuthMessage.EMAIL_NOT_EMPTY }) // @IsNotEmpty({ message: AuthMessage.EMAIL_NOT_EMPTY })
@IsEmail({}, { message: AuthMessage.INVALID_EMAIL_FORMAT }) // @IsEmail({}, { message: AuthMessage.INVALID_EMAIL_FORMAT })
@ApiProperty({ description: "Email", example: "ma@gmail.com" }) // @ApiProperty({ description: "Email", example: "ma@gmail.com" })
email: string; // email: string;
@IsNotEmpty({ message: AuthMessage.BIRTH_DATE_NOT_EMPTY }) // @IsNotEmpty({ message: AuthMessage.BIRTH_DATE_NOT_EMPTY })
@IsString({ message: AuthMessage.BIRTH_DATE_NOT_EMPTY }) // @IsString({ message: AuthMessage.BIRTH_DATE_NOT_EMPTY })
@ApiProperty({ description: "Birth date", example: "1403/01/01" }) // @ApiProperty({ description: "Birth date", example: "1403/01/01" })
birthDate: string; // birthDate: string;
@IsNotEmpty({ message: AuthMessage.NATIONAL_NOT_EMPTY }) // @IsNotEmpty({ message: AuthMessage.NATIONAL_NOT_EMPTY })
@IsNumberString({ no_symbols: true }, { message: AuthMessage.NATIONAL_CODE_INCORRECT }) // @IsNumberString({ no_symbols: true }, { message: AuthMessage.NATIONAL_CODE_INCORRECT })
@Length(10, 10, { message: AuthMessage.NATIONAL_CODE_INCORRECT }) // @Length(10, 10, { message: AuthMessage.NATIONAL_CODE_INCORRECT })
@ApiProperty({ description: "iranian format (10 char)", example: "4569852169" }) // @IsNationalCode({ message: AuthMessage.NATIONAL_CODE_INVALID })
nationalCode: string; // @ApiProperty({ description: "iranian format (10 char)", example: "4569852169" })
// nationalCode: string;
@IsNotEmpty({ message: AuthMessage.PASSWORD_NOT_EMPTY }) // @IsNotEmpty({ message: AuthMessage.PASSWORD_NOT_EMPTY })
@IsString({ message: AuthMessage.PASSWORD_FORMAT_INVALID }) // @IsString({ message: AuthMessage.PASSWORD_FORMAT_INVALID })
@ApiProperty({ description: "password", example: "12S345SS678" }) // @ApiProperty({ description: "password", example: "12S345SS678" })
@MinLength(8, { message: AuthMessage.PASSWORD_LENGTH }) // @MinLength(8, { message: AuthMessage.PASSWORD_LENGTH })
password: string; // password: string;
@ApiPropertyOptional({ description: "Economic code" }) // @ApiPropertyOptional({ description: "Economic code" })
@IsOptional() // @IsOptional()
@IsString({ message: FinancialMessage.ECONOMIC_CODE_INVALID }) // @IsString({ message: FinancialMessage.ECONOMIC_CODE_INVALID })
economicCode?: string; // economicCode?: string;
@ApiPropertyOptional({ description: "Company registered name" }) // @ApiPropertyOptional({ description: "Company registered name" })
@IsOptional() // @IsOptional()
@IsString({ message: FinancialMessage.ECONOMIC_CODE_INVALID }) // @IsString({ message: FinancialMessage.ECONOMIC_CODE_INVALID })
companyRegisteredName?: string; // companyRegisteredName?: string;
@ApiPropertyOptional({ description: "Registration ID" }) // @ApiPropertyOptional({ description: "Registration ID" })
@IsOptional() // @IsOptional()
@IsString({ message: FinancialMessage.REGISTRATION_ID_INVALID }) // @IsString({ message: FinancialMessage.REGISTRATION_ID_INVALID })
registrationId?: string; // registrationId?: string;
@ApiPropertyOptional({ description: "national Id" }) // @ApiPropertyOptional({ description: "national Id" })
@IsOptional() // @IsOptional()
@IsString({ message: FinancialMessage.NATIONAL_ID_INVALID }) // @IsString({ message: FinancialMessage.NATIONAL_ID_INVALID })
nationalId?: string; // nationalId?: string;
@ApiPropertyOptional({ description: "number" }) // @ApiPropertyOptional({ description: "number" })
@IsOptional() // @IsOptional()
@IsString({ message: FinancialMessage.FIXED_PHONE_INVALID }) // @IsString({ message: FinancialMessage.FIXED_PHONE_INVALID })
number?: string; // number?: string;
@ApiPropertyOptional({ description: "postalCode" }) // @ApiPropertyOptional({ description: "postalCode" })
@IsOptional() // @IsOptional()
@IsString({ message: FinancialMessage.NATIONAL_ID_INVALID }) // @IsString({ message: FinancialMessage.NATIONAL_ID_INVALID })
postalCode?: string; // postalCode?: string;
@ApiPropertyOptional({ description: "address" }) // @ApiPropertyOptional({ description: "address" })
@IsOptional() // @IsOptional()
@IsString({ message: FinancialMessage.NATIONAL_ID_INVALID }) // @IsString({ message: FinancialMessage.NATIONAL_ID_INVALID })
address?: string; // address?: string;
@ApiPropertyOptional({ description: "City id", example: "1" }) // @ApiPropertyOptional({ description: "City id", example: "1" })
@IsOptional() // @IsOptional()
@IsNotEmpty({ message: FinancialMessage.CITY_ID_REQUIRED }) // @IsNotEmpty({ message: FinancialMessage.CITY_ID_REQUIRED })
cityId: string; // cityId: string;
@IsOptional() // @IsOptional()
@IsNotEmpty({ message: UserMessage.PROFILE_PIC_REQUIRED }) // @IsNotEmpty({ message: UserMessage.PROFILE_PIC_REQUIRED })
@IsUrl({ protocols: ["http", "https"], require_protocol: true }, { message: UserMessage.PROFILE_PIC_URL }) // @IsUrl({ protocols: ["http", "https"], require_protocol: true }, { message: UserMessage.PROFILE_PIC_URL })
@ApiPropertyOptional({ description: "Profile picture", example: "https://www.google.com" }) // @ApiPropertyOptional({ description: "Profile picture", example: "https://www.google.com" })
profilePic: string; // profilePic: string;
} // }
+10 -10
View File
@@ -1,12 +1,12 @@
import { ApiPropertyOptional } from "@nestjs/swagger"; // import { ApiPropertyOptional } from "@nestjs/swagger";
import { IsOptional, IsString } from "class-validator"; // import { IsOptional, IsString } from "class-validator";
import { PaginationDto } from "../../../common/DTO/pagination.dto"; // import { PaginationDto } from "../../../common/DTO/pagination.dto";
import { FinancialMessage } from "../../../common/enums/message.enum"; // import { FinancialMessage } from "../../../common/enums/message.enum";
export class SearchCustomersDto extends PaginationDto { // export class SearchCustomersDto extends PaginationDto {
@IsOptional() // @IsOptional()
@IsString({ message: FinancialMessage.SEARCH_QUERY_MUST_BE_A_STRING }) // @IsString({ message: FinancialMessage.SEARCH_QUERY_MUST_BE_A_STRING })
@ApiPropertyOptional({ description: "search query", example: "search query" }) // @ApiPropertyOptional({ description: "search query", example: "search query" })
q: string; // q: string;
} // }
+23 -11
View File
@@ -3,29 +3,41 @@ import { Column, Entity, JoinColumn, OneToOne, Unique } from "typeorm";
import { User } from "./user.entity"; import { User } from "./user.entity";
import { BaseEntity } from "../../../common/entities/base.entity"; import { BaseEntity } from "../../../common/entities/base.entity";
import { Address } from "../../address/entities/address.entity"; import { Address } from "../../address/entities/address.entity";
import { GenderType } from "../enums/gender-type.enum"; import { GenderEnum } from "../enums/gender-type.enum";
import { NationalityEnum } from "../enums/nationality.enum";
@Unique(["user"]) @Unique(["user"])
@Entity() @Entity()
export class RealUser extends BaseEntity { export class RealUser extends BaseEntity {
@Column({ @Column({ type: "varchar", length: 150 })
type: "enum", firstName: string;
enum: GenderType,
nullable: true,
})
gender: GenderType;
@Column({ nullable: true }) @Column({ type: "varchar", length: 200 })
lastName: string;
@Column({ type: "varchar", length: 100 })
fatherName: string; fatherName: string;
@Column({ nullable: true }) @Column({ type: "varchar", length: 12, nullable: true })
nationality: string; birthDate: string;
@Column({ type: "varchar", length: 100, unique: true })
nationalCode: string;
@Column({ type: "varchar", length: 11, unique: true, nullable: false })
phone: string;
@Column({ type: "enum", enum: GenderEnum })
gender: GenderEnum;
@Column({ type: "enum", enum: NationalityEnum })
nationality: NationalityEnum;
@OneToOne(() => Address, { nullable: false, cascade: true }) @OneToOne(() => Address, { nullable: false, cascade: true })
@JoinColumn() @JoinColumn()
address: Address; address: Address;
@OneToOne(() => User, (user) => user.realUser, { onDelete: "CASCADE" }) @OneToOne(() => User, (user) => user.realUser, { onDelete: "CASCADE", nullable: false })
@JoinColumn() @JoinColumn()
user: User; user: User;
} }
+1 -16
View File
@@ -1,13 +1,11 @@
import { Exclude } from "class-transformer"; import { Exclude } from "class-transformer";
import { Column, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne, OneToMany, OneToOne } from "typeorm"; import { Column, Entity, JoinTable, ManyToMany, OneToMany, OneToOne } from "typeorm";
import { LegalUser } from "./legal-user.entity"; import { LegalUser } from "./legal-user.entity";
import { RealUser } from "./real-user.entity"; import { RealUser } from "./real-user.entity";
import { Role } from "./role.entity"; import { Role } from "./role.entity";
import { UserGroup } from "./user-group.entity"; import { UserGroup } from "./user-group.entity";
import { BaseEntity } from "../../../common/entities/base.entity"; import { BaseEntity } from "../../../common/entities/base.entity";
import { Address } from "../../address/entities/address.entity";
import { City } from "../../address/entities/city.entity";
import { UserAnnouncement } from "../../announcements/entities/user-announcement.entity"; import { UserAnnouncement } from "../../announcements/entities/user-announcement.entity";
import { Criticism } from "../../criticisms/entities/criticism.entity"; import { Criticism } from "../../criticisms/entities/criticism.entity";
import { DanakServiceReview } from "../../danak-services/entities/danak-service-review.entity"; import { DanakServiceReview } from "../../danak-services/entities/danak-service-review.entity";
@@ -57,17 +55,8 @@ export class User extends BaseEntity {
@Column({ type: "boolean", default: false }) @Column({ type: "boolean", default: false })
emailVerified: boolean; emailVerified: boolean;
@Column({ type: "varchar", length: 255, nullable: true })
userAddress: string;
@Column({ type: "varchar", length: 10, nullable: true })
postalCode: string;
//----------------------------------------- //-----------------------------------------
@ManyToOne(() => City, { eager: true, nullable: true })
city: City;
// @ManyToOne(() => Role, { eager: true, onDelete: "RESTRICT", nullable: false })
// role: Role;
@ManyToMany(() => Role, (role) => role.users) @ManyToMany(() => Role, (role) => role.users)
@JoinTable({ name: "user_role_relation" }) @JoinTable({ name: "user_role_relation" })
roles: Role[]; roles: Role[];
@@ -119,10 +108,6 @@ export class User extends BaseEntity {
@OneToOne(() => LegalUser, (legalUser) => legalUser.user, { cascade: true, nullable: true }) @OneToOne(() => LegalUser, (legalUser) => legalUser.user, { cascade: true, nullable: true })
legalUser: LegalUser; legalUser: LegalUser;
@OneToOne(() => Address, (address) => address.user, { cascade: true, nullable: true })
@JoinColumn()
address: Address;
@ManyToMany(() => UsageDiscount, (usageDiscount) => usageDiscount.users) @ManyToMany(() => UsageDiscount, (usageDiscount) => usageDiscount.users)
usageDiscounts: UsageDiscount[]; usageDiscounts: UsageDiscount[];
+3 -3
View File
@@ -1,4 +1,4 @@
export enum GenderType { export enum GenderEnum {
MALE = "male", MALE = "MALE",
FEMALE = "female", FEMALE = "FEMALE",
} }
@@ -0,0 +1,4 @@
export enum NationalityEnum {
IRANIAN = "IRANIAN",
FOREIGN = "FOREIGN",
}
+126 -142
View File
@@ -6,15 +6,7 @@ import { FastifyReply } from "fastify";
import slugify from "slugify"; import slugify from "slugify";
import { In, Not, QueryRunner } from "typeorm"; import { In, Not, QueryRunner } from "typeorm";
import { import { AdminMessage, AdsMessage, AuthMessage, CommonMessage, UserMessage } from "../../../common/enums/message.enum";
AdminMessage,
AdsMessage,
AuthMessage,
CommonMessage,
LegalUserMessage,
RealUserMessage,
UserMessage,
} from "../../../common/enums/message.enum";
import { AddressService } from "../../address/providers/address.service"; import { AddressService } from "../../address/providers/address.service";
import { CompleteRegistrationDto } from "../../auth/DTO/complete-register.dto"; import { CompleteRegistrationDto } from "../../auth/DTO/complete-register.dto";
import { RequestOtpDto } from "../../auth/DTO/request-otp.dto"; import { RequestOtpDto } from "../../auth/DTO/request-otp.dto";
@@ -30,12 +22,8 @@ import { WalletsService } from "../../wallets/providers/wallets.service";
import { ChangeEmailDto } from "../DTO/change-email.dto"; import { ChangeEmailDto } from "../DTO/change-email.dto";
import { CheckValidityDTO } from "../DTO/check-validity.dto"; import { CheckValidityDTO } from "../DTO/check-validity.dto";
import { CreateAdminDto } from "../DTO/create-admin.dto"; import { CreateAdminDto } from "../DTO/create-admin.dto";
import { CreateLegalUserDto } from "../DTO/create-legal-user.dto";
import { CreateRealUserDto } from "../DTO/create-real-user.dto";
import { CreateRoleDto } from "../DTO/create-role.dto"; import { CreateRoleDto } from "../DTO/create-role.dto";
import { CreateCustomerDto } from "../DTO/create-user.dto";
import { SearchAdminQueryDto } from "../DTO/search-admins-query.dto"; import { SearchAdminQueryDto } from "../DTO/search-admins-query.dto";
import { SearchCustomersDto } from "../DTO/search-customers.dto";
import { SearchRolesQueryDto } from "../DTO/search-roles.dto"; import { SearchRolesQueryDto } from "../DTO/search-roles.dto";
import { UpdateProfileDto } from "../DTO/update-profile.dto"; import { UpdateProfileDto } from "../DTO/update-profile.dto";
import { CreateUserGroupDto } from "../DTO/user-group.dto"; import { CreateUserGroupDto } from "../DTO/user-group.dto";
@@ -44,9 +32,7 @@ import { Role } from "../entities/role.entity";
import { User } from "../entities/user.entity"; import { User } from "../entities/user.entity";
import { RoleEnum } from "../enums/role.enum"; import { RoleEnum } from "../enums/role.enum";
import { ValidityType } from "../enums/validity-type.enum"; import { ValidityType } from "../enums/validity-type.enum";
import { LegalUserRepository } from "../repositories/legal-user.repository";
import { PermissionsRepository } from "../repositories/permissions.repository"; import { PermissionsRepository } from "../repositories/permissions.repository";
import { RealUserRepository } from "../repositories/real-user.repository";
import { RoleRepository } from "../repositories/roles.repository"; import { RoleRepository } from "../repositories/roles.repository";
import { UserGroupRepository } from "../repositories/user-group.repository"; import { UserGroupRepository } from "../repositories/user-group.repository";
import { UserRepository } from "../repositories/users.repository"; import { UserRepository } from "../repositories/users.repository";
@@ -62,8 +48,6 @@ export class UsersService {
private readonly rolesRepository: RoleRepository, private readonly rolesRepository: RoleRepository,
private readonly permissionsRepository: PermissionsRepository, private readonly permissionsRepository: PermissionsRepository,
private readonly passwordService: PasswordService, private readonly passwordService: PasswordService,
private readonly realUserRepository: RealUserRepository,
private readonly legalUserRepository: LegalUserRepository,
private readonly addressService: AddressService, private readonly addressService: AddressService,
private readonly emailService: EmailService, private readonly emailService: EmailService,
private readonly configService: ConfigService, private readonly configService: ConfigService,
@@ -90,7 +74,7 @@ export class UsersService {
/************************************************************ */ /************************************************************ */
async getMe(userId: string) { async getMe(userId: string) {
const user = await this.userRepository.findOne({ where: { id: userId }, relations: { roles: true, city: { province: true } } }); const user = await this.userRepository.findOne({ where: { id: userId }, relations: { roles: true } });
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND); if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
return { user }; return { user };
} }
@@ -405,93 +389,93 @@ export class UsersService {
/************************************************************ */ /************************************************************ */
async findAllCustomers(queryDto: SearchCustomersDto) { // async findAllCustomers(queryDto: SearchCustomersDto) {
const { limit, skip } = PaginationUtils(queryDto); // const { limit, skip } = PaginationUtils(queryDto);
const queryBuilder = this.userRepository // const queryBuilder = this.userRepository
.createQueryBuilder("user") // .createQueryBuilder("user")
.leftJoin("user.roles", "role") // .leftJoin("user.roles", "role")
.where("role.name = :roleName", { roleName: RoleEnum.USER }) // .where("role.name = :roleName", { roleName: RoleEnum.USER })
.leftJoin("user.groups", "groups") // .leftJoin("user.groups", "groups")
.addSelect(["groups.id", "groups.name"]) // .addSelect(["groups.id", "groups.name"])
.loadRelationCountAndMap("user.invoicesCount", "user.invoices") // .loadRelationCountAndMap("user.invoicesCount", "user.invoices")
.loadRelationCountAndMap("user.subscriptionsCount", "user.subscriptions") // .loadRelationCountAndMap("user.subscriptionsCount", "user.subscriptions")
.loadRelationCountAndMap("user.ticketsCount", "user.tickets"); // .loadRelationCountAndMap("user.ticketsCount", "user.tickets");
if (queryDto.q) { // if (queryDto.q) {
queryBuilder // queryBuilder
.orWhere("user.firstName ILIKE :search", { search: `%${queryDto.q}%` }) // .orWhere("user.firstName ILIKE :search", { search: `%${queryDto.q}%` })
.orWhere("user.lastName ILIKE :search", { search: `%${queryDto.q}%` }) // .orWhere("user.lastName ILIKE :search", { search: `%${queryDto.q}%` })
.orWhere("user.userName ILIKE :search", { search: `%${queryDto.q}%` }); // .orWhere("user.userName ILIKE :search", { search: `%${queryDto.q}%` });
} // }
const [customers, count] = await queryBuilder.skip(skip).take(limit).getManyAndCount(); // const [customers, count] = await queryBuilder.skip(skip).take(limit).getManyAndCount();
return { customers, count }; // return { customers, count };
} // }
/************************************************************ */ /************************************************************ */
async createCustomer(createDto: CreateCustomerDto) { // async createCustomer(createDto: CreateCustomerDto) {
const { cityId, address, postalCode, economicCode, companyRegisteredName, registrationId, nationalId, number, ...userData } = createDto; // const { cityId, address, postalCode, economicCode, companyRegisteredName, registrationId, nationalId, number, ...userData } = createDto;
const queryRunner = this.userRepository.manager.connection.createQueryRunner(); // const queryRunner = this.userRepository.manager.connection.createQueryRunner();
await queryRunner.connect(); // await queryRunner.connect();
await queryRunner.startTransaction(); // await queryRunner.startTransaction();
try { // try {
const existUser = await queryRunner.manager.findOneBy(User, { email: createDto.email }); // const existUser = await queryRunner.manager.findOneBy(User, { email: createDto.email });
if (existUser) throw new BadRequestException(UserMessage.EMAIL_EXIST); // if (existUser) throw new BadRequestException(UserMessage.EMAIL_EXIST);
const role = await queryRunner.manager.findOneBy(Role, { name: RoleEnum.USER }); // const role = await queryRunner.manager.findOneBy(Role, { name: RoleEnum.USER });
if (!role) throw new BadRequestException(UserMessage.ROLE_NOT_FOUND); // if (!role) throw new BadRequestException(UserMessage.ROLE_NOT_FOUND);
const hashedPassword = await this.passwordService.hashPassword(createDto.password); // const hashedPassword = await this.passwordService.hashPassword(createDto.password);
const legalUserData = economicCode // const legalUserData = economicCode
? { // ? {
economicCode, // economicCode,
companyRegisteredName, // companyRegisteredName,
registrationId, // registrationId,
nationalId, // nationalId,
number, // number,
address: { // address: {
address, // address,
city: cityId // city: cityId
? { // ? {
id: +cityId, // id: +cityId,
} // }
: undefined, // : undefined,
postalCode, // postalCode,
}, // },
} // }
: undefined; // : undefined;
const user = queryRunner.manager.create(User, { // const user = queryRunner.manager.create(User, {
...userData, // ...userData,
password: hashedPassword, // password: hashedPassword,
roles: [role], // roles: [role],
legalUser: legalUserData, // legalUser: legalUserData,
}); // });
await queryRunner.manager.save(user); // await queryRunner.manager.save(user);
await this.userSettingsService.createUserSettings(user.id, queryRunner); // await this.userSettingsService.createUserSettings(user.id, queryRunner);
await this.walletsService.createUserWallet(user.id, queryRunner); // await this.walletsService.createUserWallet(user.id, queryRunner);
await queryRunner.commitTransaction(); // await queryRunner.commitTransaction();
return { // return {
message: CommonMessage.CREATED, // message: CommonMessage.CREATED,
user, // user,
}; // };
} catch (error) { // } catch (error) {
await queryRunner.rollbackTransaction(); // await queryRunner.rollbackTransaction();
throw error; // throw error;
} finally { // } finally {
await queryRunner.release(); // await queryRunner.release();
} // }
} // }
/************************************************************ */ /************************************************************ */
@@ -581,74 +565,74 @@ export class UsersService {
} }
/************************************************************ */ /************************************************************ */
async createRealUserData(createDto: CreateRealUserDto, userId: string) { // async createRealUserData(createDto: CreateRealUserDto, userId: string) {
const userExist = await this.userRepository.findOneBy({ // const userExist = await this.userRepository.findOneBy({
id: userId, // id: userId,
}); // });
if (!userExist) throw new BadRequestException(UserMessage.USER_NOT_FOUND); // if (!userExist) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
console.log(userExist); // console.log(userExist);
const existingRealUser = await this.realUserRepository.findOne({ // const existingRealUser = await this.realUserRepository.findOne({
where: { user: { id: userExist.id } }, // where: { user: { id: userExist.id } },
}); // });
console.log(existingRealUser); // console.log(existingRealUser);
if (existingRealUser) throw new BadRequestException(RealUserMessage.REAL_DATA_EXIST); // if (existingRealUser) throw new BadRequestException(RealUserMessage.REAL_DATA_EXIST);
const { city } = await this.addressService.getCity(+createDto.cityId); // const { city } = await this.addressService.getCity(+createDto.cityId);
const realUser = this.realUserRepository.create({ // const realUser = this.realUserRepository.create({
...createDto, // ...createDto,
user: userExist, // user: userExist,
address: { // address: {
address: createDto.address, // address: createDto.address,
city, // city,
postalCode: createDto.postalCode, // postalCode: createDto.postalCode,
user: userExist, // user: userExist,
}, // },
}); // });
await this.realUserRepository.save(realUser); // await this.realUserRepository.save(realUser);
return { // return {
message: CommonMessage.CREATED, // message: CommonMessage.CREATED,
realUser, // realUser,
}; // };
} // }
// /************************************************************ */ // /************************************************************ */
async createLegalUserData(createDto: CreateLegalUserDto, userId: string) { // async createLegalUserData(createDto: CreateLegalUserDto, userId: string) {
const userExist = await this.userRepository.findOneBy({ // const userExist = await this.userRepository.findOneBy({
id: userId, // id: userId,
}); // });
if (!userExist) throw new BadRequestException(UserMessage.USER_NOT_FOUND); // if (!userExist) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
const existingLegalUser = await this.legalUserRepository.findOne({ // const existingLegalUser = await this.legalUserRepository.findOne({
where: { user: { id: userExist.id } }, // where: { user: { id: userExist.id } },
}); // });
if (existingLegalUser) throw new BadRequestException(LegalUserMessage.LEGAL_DATA_EXIST); // if (existingLegalUser) throw new BadRequestException(LegalUserMessage.LEGAL_DATA_EXIST);
const { city } = await this.addressService.getCity(+createDto.cityId); // const { city } = await this.addressService.getCity(+createDto.cityId);
const legalUser = this.legalUserRepository.create({ // const legalUser = this.legalUserRepository.create({
...createDto, // ...createDto,
user: userExist, // user: userExist,
address: { // address: {
address: createDto.address, // address: createDto.address,
city, // city,
postalCode: createDto.postalCode, // postalCode: createDto.postalCode,
user: userExist, // user: userExist,
}, // },
}); // });
await this.legalUserRepository.save(legalUser); // await this.legalUserRepository.save(legalUser);
return { // return {
message: CommonMessage.CREATED, // message: CommonMessage.CREATED,
legalUser, // legalUser,
}; // };
} // }
/************************************************************ */ /************************************************************ */
+28 -32
View File
@@ -5,12 +5,8 @@ import { FastifyReply } from "fastify";
import { ChangeEmailDto } from "./DTO/change-email.dto"; import { ChangeEmailDto } from "./DTO/change-email.dto";
import { CheckValidityDTO } from "./DTO/check-validity.dto"; import { CheckValidityDTO } from "./DTO/check-validity.dto";
import { CreateAdminDto } from "./DTO/create-admin.dto"; import { CreateAdminDto } from "./DTO/create-admin.dto";
import { CreateLegalUserDto } from "./DTO/create-legal-user.dto";
import { CreateRealUserDto } from "./DTO/create-real-user.dto";
import { CreateRoleDto } from "./DTO/create-role.dto"; import { CreateRoleDto } from "./DTO/create-role.dto";
import { CreateCustomerDto } from "./DTO/create-user.dto";
import { SearchAdminQueryDto } from "./DTO/search-admins-query.dto"; import { SearchAdminQueryDto } from "./DTO/search-admins-query.dto";
import { SearchCustomersDto } from "./DTO/search-customers.dto";
import { SearchRolesQueryDto } from "./DTO/search-roles.dto"; import { SearchRolesQueryDto } from "./DTO/search-roles.dto";
import { UpdateProfileDto } from "./DTO/update-profile.dto"; import { UpdateProfileDto } from "./DTO/update-profile.dto";
import { CreateUserGroupDto } from "./DTO/user-group.dto"; import { CreateUserGroupDto } from "./DTO/user-group.dto";
@@ -112,13 +108,13 @@ export class UsersController {
return this.usersService.findAllUsers(); return this.usersService.findAllUsers();
} }
@ApiOperation({ summary: "get all customers ==> admin route" }) // @ApiOperation({ summary: "get all customers ==> admin route" })
@AuthGuards() // @AuthGuards()
@PermissionsDec(PermissionEnum.ADMINS, PermissionEnum.CUSTOMERS) // @PermissionsDec(PermissionEnum.ADMINS, PermissionEnum.CUSTOMERS)
@Get("customers") // @Get("customers")
customers(@Query() queryDto: SearchCustomersDto) { // customers(@Query() queryDto: SearchCustomersDto) {
return this.usersService.findAllCustomers(queryDto); // return this.usersService.findAllCustomers(queryDto);
} // }
@ApiOperation({ summary: "get all customers ==> admin route" }) @ApiOperation({ summary: "get all customers ==> admin route" })
@AuthGuards() @AuthGuards()
@@ -169,27 +165,27 @@ export class UsersController {
} }
// //
@ApiOperation({ summary: "Create real user data" }) // @ApiOperation({ summary: "Create real user data" })
@AuthGuards() // @AuthGuards()
@HttpCode(HttpStatus.CREATED) // @HttpCode(HttpStatus.CREATED)
@Post("real-user") // @Post("real-user")
createRealUser(@Body() createDto: CreateRealUserDto, @UserDec("id") userId: string) { // createRealUser(@Body() createDto: CreateRealUserDto, @UserDec("id") userId: string) {
return this.usersService.createRealUserData(createDto, userId); // return this.usersService.createRealUserData(createDto, userId);
} // }
@ApiOperation({ summary: "Create legal user data" }) // @ApiOperation({ summary: "Create legal user data" })
@AuthGuards() // @AuthGuards()
@HttpCode(HttpStatus.CREATED) // @HttpCode(HttpStatus.CREATED)
@Post("legal-user") // @Post("legal-user")
createLegalUser(@Body() createDto: CreateLegalUserDto, @UserDec("id") userId: string) { // createLegalUser(@Body() createDto: CreateLegalUserDto, @UserDec("id") userId: string) {
return this.usersService.createLegalUserData(createDto, userId); // return this.usersService.createLegalUserData(createDto, userId);
} // }
@ApiOperation({ summary: "Create customer" }) // @ApiOperation({ summary: "Create customer" })
@AuthGuards() // @AuthGuards()
@HttpCode(HttpStatus.CREATED) // @HttpCode(HttpStatus.CREATED)
@Post("customer") // @Post("customer")
createCustomer(@Body() createDto: CreateCustomerDto) { // createCustomer(@Body() createDto: CreateCustomerDto) {
return this.usersService.createCustomer(createDto); // return this.usersService.createCustomer(createDto);
} // }
} }