diff --git a/src/common/enums/message.enum.ts b/src/common/enums/message.enum.ts index a82ba92..876fa66 100644 --- a/src/common/enums/message.enum.ts +++ b/src/common/enums/message.enum.ts @@ -371,6 +371,8 @@ export const enum FinancialMessage { USER_ID_REQUIRED = "شناسه کاربر مورد نیاز است", USER_ID_SHOULD_BE_A_UUID = "شناسه کاربر باید یک UUID معتبر باشد", FINANCIAL_INFO_ALREADY_EXISTS = "دیتا اطلاعات مالی وجود دارد", + FINANCIAL_INFO_NOT_FOUND = "دیتا اطلاعات مالی یافت نشد", + SEARCH_QUERY_MUST_BE_A_STRING = "رشته جستجو باید یک رشته باشد", } export const enum AdsMessage { TITLE_REQUIRED = "عنوان الزامی است", diff --git a/src/modules/users/DTO/search-admins.dto.ts b/src/modules/users/DTO/search-admins.dto.ts new file mode 100644 index 0000000..d81bcc1 --- /dev/null +++ b/src/modules/users/DTO/search-admins.dto.ts @@ -0,0 +1,12 @@ +import { ApiPropertyOptional } from "@nestjs/swagger"; +import { IsOptional, IsString } from "class-validator"; + +import { PaginationDto } from "../../../common/DTO/pagination.dto"; +import { FinancialMessage } from "../../../common/enums/message.enum"; + +export class SearchAdminsDto extends PaginationDto { + @IsOptional() + @IsString({ message: FinancialMessage.SEARCH_QUERY_MUST_BE_A_STRING }) + @ApiPropertyOptional({ description: "search query", example: "search query" }) + q: string; +} diff --git a/src/modules/users/DTO/search-customers.dto.ts b/src/modules/users/DTO/search-customers.dto.ts new file mode 100644 index 0000000..7987e61 --- /dev/null +++ b/src/modules/users/DTO/search-customers.dto.ts @@ -0,0 +1,12 @@ +import { ApiPropertyOptional } from "@nestjs/swagger"; +import { IsOptional, IsString } from "class-validator"; + +import { PaginationDto } from "../../../common/DTO/pagination.dto"; +import { FinancialMessage } from "../../../common/enums/message.enum"; + +export class SearchCustomersDto extends PaginationDto { + @IsOptional() + @IsString({ message: FinancialMessage.SEARCH_QUERY_MUST_BE_A_STRING }) + @ApiPropertyOptional({ description: "search query", example: "search query" }) + q: string; +} diff --git a/src/modules/users/providers/users.service.ts b/src/modules/users/providers/users.service.ts index 5f0e7c3..9807aa9 100644 --- a/src/modules/users/providers/users.service.ts +++ b/src/modules/users/providers/users.service.ts @@ -5,9 +5,12 @@ import { In, Not, QueryRunner } from "typeorm"; import { CommonMessage, FinancialMessage, UserMessage } from "../../../common/enums/message.enum"; import { CompleteRegistrationDto } from "../../auth/DTO/complete-register.dto"; import { UserSettingsService } from "../../settings/providers/user-settings.service"; +import { PaginationUtils } from "../../utils/providers/pagination.utils"; import { WalletsService } from "../../wallets/providers/wallets.service"; import { CheckValidityDTO } from "../DTO/check-validity.dto"; import { CreateFinancialDto } from "../DTO/create-user-financial.dto"; +import { SearchAdminsDto } from "../DTO/search-admins.dto"; +import { SearchCustomersDto } from "../DTO/search-customers.dto"; import { UpdateProfileDto } from "../DTO/update-profile.dto"; import { CreateUserGroupDto } from "../DTO/user-group.dto"; import { Role } from "../entities/role.entity"; @@ -204,9 +207,60 @@ export class UsersService { /************************************************************ */ - async findAllCustomers() { - const customers = await this.userRepository.find({ + async findAllAdmins(queryDto: SearchAdminsDto) { + const { limit, skip } = PaginationUtils(queryDto); + + const queryBuilder = this.userRepository + .createQueryBuilder("user") + .leftJoin("user.role", "role") + .where("role.name = :roleName", { roleName: RoleEnum.ADMIN }) + .leftJoin("user.groups", "groups") + .addSelect(["groups.id", "groups.name"]); + + if (queryDto.q) { + queryBuilder + .orWhere("user.firstName ILIKE :search", { search: `%${queryDto.q}%` }) + .orWhere("user.lastName ILIKE :search", { search: `%${queryDto.q}%` }) + .orWhere("user.userName ILIKE :search", { search: `%${queryDto.q}%` }); + } + + const [admins, count] = await queryBuilder.skip(skip).take(limit).getManyAndCount(); + + return { admins, count }; + } + + /************************************************************ */ + + async findAllCustomers(queryDto: SearchCustomersDto) { + const { limit, skip } = PaginationUtils(queryDto); + + const queryBuilder = this.userRepository + .createQueryBuilder("user") + .leftJoin("user.role", "role") + .where("role.name = :roleName", { roleName: RoleEnum.USER }) + .leftJoin("user.groups", "groups") + .addSelect(["groups.id", "groups.name"]) + .leftJoinAndSelect("user.invoices", "invoices") + .leftJoinAndSelect("user.subscriptions", "subscriptions") + .leftJoinAndSelect("user.tickets", "tickets"); + + if (queryDto.q) { + queryBuilder + .orWhere("user.firstName ILIKE :search", { search: `%${queryDto.q}%` }) + .orWhere("user.lastName ILIKE :search", { search: `%${queryDto.q}%` }); + } + + const [customers, count] = await queryBuilder.skip(skip).take(limit).getManyAndCount(); + + return { customers, count }; + } + + /************************************************************ */ + + async findOneCustomer(userId: string) { + const customer = await this.userRepository.findOne({ where: { + id: userId, role: { name: RoleEnum.USER, }, @@ -214,7 +268,7 @@ export class UsersService { relations: ["tickets", "subscriptions", "invoices"], }); - return { customers }; + return { customer }; } /************************************************************ */ @@ -248,6 +302,32 @@ export class UsersService { /************************************************************ */ + async updateUserFinancial(updateDto: CreateFinancialDto, user: User, userFinancialId: string) { + const userExist = await this.userRepository.findOneBy({ + id: user.role.name === RoleEnum.USER ? user.id : updateDto.userId, + }); + + if (!userExist) throw new BadRequestException(UserMessage.USER_NOT_FOUND); + + const existingFinancial = await this.userFinancialRepository.findOne({ + where: { id: userFinancialId, user: { id: userExist.id }, type: updateDto.type }, + }); + + if (!existingFinancial) throw new BadRequestException(FinancialMessage.FINANCIAL_INFO_NOT_FOUND); + + await this.userFinancialRepository.save({ + ...existingFinancial, + updateDto, + }); + + return { + message: CommonMessage.UPDATE_SUCCESS, + userFinancial: existingFinancial, + }; + } + + /************************************************************ */ + async getUserFinancial(userId: string) { const user = await this.userRepository.findOne({ where: { diff --git a/src/modules/users/users.controller.ts b/src/modules/users/users.controller.ts index 99fd15c..a76c2b4 100644 --- a/src/modules/users/users.controller.ts +++ b/src/modules/users/users.controller.ts @@ -1,8 +1,10 @@ -import { Body, Controller, Get, HttpCode, HttpStatus, Patch, Post } from "@nestjs/common"; +import { Body, Controller, Get, HttpCode, HttpStatus, Param, Patch, Post, Query } from "@nestjs/common"; import { ApiOperation, ApiTags } from "@nestjs/swagger"; import { CheckValidityDTO } from "./DTO/check-validity.dto"; import { CreateFinancialDto } from "./DTO/create-user-financial.dto"; +import { SearchAdminsDto } from "./DTO/search-admins.dto"; +import { SearchCustomersDto } from "./DTO/search-customers.dto"; import { UpdateProfileDto } from "./DTO/update-profile.dto"; import { CreateUserGroupDto } from "./DTO/user-group.dto"; import { User } from "./entities/user.entity"; @@ -11,6 +13,7 @@ import { UsersService } from "./providers/users.service"; import { AuthGuards } from "../../common/decorators/auth-guard.decorator"; import { Roles } from "../../common/decorators/roles.decorator"; import { UserDec } from "../../common/decorators/user.decorator"; +import { ParamDto } from "../../common/DTO/param.dto"; @Controller("users") @ApiTags("users") @@ -68,12 +71,12 @@ export class UsersController { /************************************************************ */ - @ApiOperation({ summary: "get all users" }) + @ApiOperation({ summary: "get all admins" }) @AuthGuards() @Roles(RoleEnum.ADMIN) - @Get("") - Users() { - return this.usersService.findAllUsers(); + @Get("admins") + Users(@Query() queryDto: SearchAdminsDto) { + return this.usersService.findAllAdmins(queryDto); } /************************************************************ */ @@ -82,8 +85,8 @@ export class UsersController { @AuthGuards() @Roles(RoleEnum.ADMIN) @Get("customers") - customers() { - return this.usersService.findAllCustomers(); + customers(@Query() queryDto: SearchCustomersDto) { + return this.usersService.findAllCustomers(queryDto); } /************************************************************ */ @@ -98,6 +101,16 @@ export class UsersController { /************************************************************ */ + @AuthGuards() + @ApiOperation({ summary: "update user financial" }) + @HttpCode(HttpStatus.CREATED) + @Patch("user-financial/:id/update") + updateUserFinancial(@Body() updateDto: CreateFinancialDto, @UserDec() user: User, @Param() paramDto: ParamDto) { + return this.usersService.updateUserFinancial(updateDto, user, paramDto.id); + } + + /************************************************************ */ + @ApiOperation({ summary: "get all user financials" }) @AuthGuards() @Get("user-financials")