diff --git a/src/modules/danak-services/providers/danak-services.service.ts b/src/modules/danak-services/providers/danak-services.service.ts index e805378..d7c18f7 100644 --- a/src/modules/danak-services/providers/danak-services.service.ts +++ b/src/modules/danak-services/providers/danak-services.service.ts @@ -1,8 +1,10 @@ import { BadRequestException, Injectable } from "@nestjs/common"; +import Decimal from "decimal.js"; import { FindOptionsWhere, In, IsNull } from "typeorm"; import { ParamDto } from "../../../common/DTO/param.dto"; import { CategoryMessage, CommonMessage, ServiceMessage } from "../../../common/enums/message.enum"; +import { DiscountCalculationType } from "../../discounts/enums/discount-type.enum"; import { PaginationUtils } from "../../utils/providers/pagination.utils"; import { AddReviewDto } from "../DTO/add-review.dto"; import { CategoryListSearchQueryDto, CategorySearchQueryDto } from "../DTO/category-search-query.dto"; @@ -179,10 +181,34 @@ export class DanakServicesService { async getDanakServiceByIdWithSubs(serviceId: string, isAdmin: boolean) { const danakService = await this.danakServicesRepository.findOne({ where: { id: serviceId, ...(isAdmin && { isActive: true, subscriptionPlans: { isActive: true } }) }, - relations: { images: true, subscriptionPlans: true }, + relations: { images: true, subscriptionPlans: { discounts: true } }, }); if (!danakService) throw new BadRequestException(ServiceMessage.SERVICE_NOT_FOUND_BY_ID); + const transformedSubscriptions = danakService.subscriptionPlans.map((subscription) => { + let price = new Decimal(subscription.price); + + if (subscription.discounts && subscription.discounts.length > 0) { + subscription.discounts.forEach((discount) => { + if (discount.isActive && new Date() >= discount.startDate && new Date() <= discount.endDate) { + if (discount.calculationType === DiscountCalculationType.PERCENTAGE) { + const discountAmount = price.mul(discount.amount).div(100); + price = price.sub(discountAmount); + } else if (discount.calculationType === DiscountCalculationType.FIXED) { + price = price.sub(discount.amount); + } + } + }); + } + + return { + ...subscription, + finalPrice: price.toNumber(), + }; + }); + + danakService.subscriptionPlans = transformedSubscriptions; + const reviews = await this.danakServiceReviewRepository.find({ where: { service: { id: serviceId }, status: ReviewStatus.APPROVED }, relations: { user: true }, diff --git a/src/modules/users/DTO/create-user.dto.ts b/src/modules/users/DTO/create-user.dto.ts index 12f0171..892f59e 100644 --- a/src/modules/users/DTO/create-user.dto.ts +++ b/src/modules/users/DTO/create-user.dto.ts @@ -1,10 +1,10 @@ -import { ApiProperty, PartialType } from "@nestjs/swagger"; -import { IsEmail, IsMobilePhone, IsNotEmpty, IsNumberString, IsString, Length, MinLength } from "class-validator"; +import { ApiProperty, ApiPropertyOptional, PartialType } from "@nestjs/swagger"; +import { IsEmail, IsMobilePhone, IsNotEmpty, IsNumberString, IsOptional, IsString, Length, MinLength } from "class-validator"; import { CreateLegalUserDto } from "./create-legal-user.dto"; -import { AuthMessage } from "../../../common/enums/message.enum"; +import { AuthMessage, FinancialMessage } from "../../../common/enums/message.enum"; -export class CreateUserDto extends PartialType(CreateLegalUserDto) { +export class CreateCustomerDto extends PartialType(CreateLegalUserDto) { @IsNotEmpty({ message: AuthMessage.PHONE_NOT_EMPTY }) @IsMobilePhone("fa-IR", {}, { message: AuthMessage.INVALID_PHONE_FORMAT }) @Length(11, 11, { message: AuthMessage.PHONE_SHOULD_BE_11_DIGIT }) @@ -44,4 +44,44 @@ export class CreateUserDto extends PartialType(CreateLegalUserDto) { @ApiProperty({ description: "password", example: "12S345SS678" }) @MinLength(8, { message: AuthMessage.PASSWORD_LENGTH }) password: string; + + @ApiPropertyOptional({ description: "Economic code" }) + @IsOptional() + @IsString({ message: FinancialMessage.ECONOMIC_CODE_INVALID }) + economicCode?: string; + + @ApiPropertyOptional({ description: "Company registered name" }) + @IsOptional() + @IsString({ message: FinancialMessage.ECONOMIC_CODE_INVALID }) + companyRegisteredName?: string; + + @ApiPropertyOptional({ description: "Registration ID" }) + @IsOptional() + @IsString({ message: FinancialMessage.REGISTRATION_ID_INVALID }) + registrationId?: string; + + @ApiPropertyOptional({ description: "national Id" }) + @IsOptional() + @IsString({ message: FinancialMessage.NATIONAL_ID_INVALID }) + nationalId?: string; + + @ApiPropertyOptional({ description: "number" }) + @IsOptional() + @IsString({ message: FinancialMessage.FIXED_PHONE_INVALID }) + number?: string; + + @ApiPropertyOptional({ description: "postalCode" }) + @IsOptional() + @IsString({ message: FinancialMessage.NATIONAL_ID_INVALID }) + postalCode?: string; + + @ApiPropertyOptional({ description: "address" }) + @IsOptional() + @IsString({ message: FinancialMessage.NATIONAL_ID_INVALID }) + address?: string; + + @ApiPropertyOptional({ description: "City id", example: "1" }) + @IsOptional() + @IsNotEmpty({ message: FinancialMessage.CITY_ID_REQUIRED }) + cityId: string; } diff --git a/src/modules/users/providers/users.service.ts b/src/modules/users/providers/users.service.ts index d895656..1cc6c73 100644 --- a/src/modules/users/providers/users.service.ts +++ b/src/modules/users/providers/users.service.ts @@ -26,6 +26,7 @@ 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 { CreateCustomerDto } from "../DTO/create-user.dto"; import { SearchAdminQueryDto } from "../DTO/search-admins-query.dto"; import { SearchCustomersDto } from "../DTO/search-customers.dto"; import { SearchRolesQueryDto } from "../DTO/search-roles.dto"; @@ -350,6 +351,69 @@ export class UsersService { /************************************************************ */ + async createCustomer(createDto: CreateCustomerDto) { + const { cityId, address, postalCode, economicCode, companyRegisteredName, registrationId, nationalId, number, ...userData } = createDto; + + const queryRunner = this.userRepository.manager.connection.createQueryRunner(); + await queryRunner.connect(); + await queryRunner.startTransaction(); + + try { + const existUser = await queryRunner.manager.findOneBy(User, { email: createDto.email }); + if (existUser) throw new BadRequestException(UserMessage.EMAIL_EXIST); + + const role = await queryRunner.manager.findOneBy(Role, { name: RoleEnum.USER }); + if (!role) throw new BadRequestException(UserMessage.ROLE_NOT_FOUND); + + const hashedPassword = await this.passwordService.hashPassword(createDto.password); + + const legalUserData = economicCode + ? { + economicCode, + companyRegisteredName, + registrationId, + nationalId, + number, + address: { + address, + city: cityId + ? { + id: +cityId, + } + : undefined, + postalCode, + }, + } + : undefined; + + const user = queryRunner.manager.create(User, { + ...userData, + password: hashedPassword, + roles: [role], + legalUser: legalUserData, + }); + await queryRunner.manager.save(user); + + await this.userSettingsService.createUserSettings(user.id, queryRunner); + + await this.walletsService.createUserWallet(user.id, queryRunner); + + await queryRunner.commitTransaction(); + + return { + message: CommonMessage.CREATED, + user, + }; + } catch (error) { + await queryRunner.rollbackTransaction(); + throw error; + } finally { + await queryRunner.release(); + } + } + + /************************************************************ */ + async findOneCustomer(userId: string) { const customer = await this.userRepository.findOne({ where: { diff --git a/src/modules/users/users.controller.ts b/src/modules/users/users.controller.ts index 67edb4c..cff1ba8 100644 --- a/src/modules/users/users.controller.ts +++ b/src/modules/users/users.controller.ts @@ -7,6 +7,7 @@ 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 { CreateCustomerDto } from "./DTO/create-user.dto"; import { SearchAdminQueryDto } from "./DTO/search-admins-query.dto"; import { SearchCustomersDto } from "./DTO/search-customers.dto"; import { SearchRolesQueryDto } from "./DTO/search-roles.dto"; @@ -174,4 +175,14 @@ export class UsersController { } /************************************************************ */ + + @AuthGuards() + @ApiOperation({ summary: "Create customer" }) + @HttpCode(HttpStatus.CREATED) + @Post("customer") + createCustomer(@Body() createDto: CreateCustomerDto) { + return this.usersService.createCustomer(createDto); + } + + /************************************************************ */ }