import { BadRequestException, Injectable, Logger } from "@nestjs/common"; import { DataSource, Not } from "typeorm"; import { CommonMessage, UserMessage } from "../../../common/enums/message.enum"; import { Address } from "../../address/entities/address.entity"; import { AddressService } from "../../address/providers/address.service"; import { UserSettingsService } from "../../settings/providers/user-settings.service"; import { PasswordService } from "../../utils/providers/password.service"; import { WalletsService } from "../../wallets/providers/wallets.service"; import { CreateCustomerDto } from "../DTO/create-customer.dto"; import { CreateLegalUserDto } from "../DTO/create-legal-user.dto"; import { CreateRealUserDto } from "../DTO/create-real-user.dto"; import { SearchCustomersDto } from "../DTO/search-customers.dto"; import { UpdateCustomerDto } from "../DTO/update-customer.dto"; import { FinancialType } from "../enums/financial-type.enum"; import { RoleEnum } from "../enums/role.enum"; import { LegalUserRepository } from "../repositories/legal-user.repository"; import { RealUserRepository } from "../repositories/real-user.repository"; import { RoleRepository } from "../repositories/roles.repository"; import { UserRepository } from "../repositories/users.repository"; @Injectable() export class CustomersService { private readonly logger = new Logger(CustomersService.name); constructor( private readonly addressService: AddressService, private readonly dataSource: DataSource, private readonly userRepository: UserRepository, private readonly legalUserRepository: LegalUserRepository, private readonly realUserRepository: RealUserRepository, private readonly passwordService: PasswordService, private readonly userSettingsService: UserSettingsService, private readonly rolesRepository: RoleRepository, private readonly walletsService: WalletsService, ) {} async createCustomer(createDto: CreateCustomerDto) { const queryRunner = this.dataSource.createQueryRunner(); try { await queryRunner.connect(); await queryRunner.startTransaction(); const [existingEmail, existingNationalCode, existingPhone, userRole] = await Promise.all([ queryRunner.manager.findOne(this.userRepository.target, { where: { email: createDto.email } }), queryRunner.manager.findOne(this.userRepository.target, { where: { nationalCode: createDto.nationalCode } }), queryRunner.manager.findOne(this.userRepository.target, { where: { phone: createDto.phone } }), queryRunner.manager.findOne(this.rolesRepository.target, { where: { name: RoleEnum.USER } }), ]); if (existingEmail) throw new BadRequestException(UserMessage.EMAIL_EXIST); if (existingNationalCode) throw new BadRequestException(UserMessage.NATIONAL_CODE_EXIST); if (existingPhone) throw new BadRequestException(UserMessage.PHONE_EXIST); if (!userRole) throw new BadRequestException(UserMessage.ROLE_NOT_FOUND); const user = queryRunner.manager.create(this.userRepository.target, { ...createDto, password: await this.passwordService.hashPassword(createDto.password), roles: [userRole], }); await queryRunner.manager.save(user); await Promise.all([ this.userSettingsService.createUserSettings(user.id, queryRunner), this.walletsService.createUserWallet(user.id, queryRunner), ]); await this.createLegalUserData(createDto, user.id, queryRunner); await queryRunner.commitTransaction(); return { message: CommonMessage.CREATED, user }; } catch (error) { await queryRunner.rollbackTransaction(); this.logger.error("Error in create customer", error); throw error; } finally { await queryRunner.release(); } } //************************************************ */ async updateCustomer(customerId: string, updateDto: UpdateCustomerDto) { const queryRunner = this.dataSource.createQueryRunner(); try { await queryRunner.connect(); await queryRunner.startTransaction(); const { registrationName, economicCode, nationalIdentity, registrationCode, postalCode, nationalCode, address, cityId, birthDate, email, firstName, lastName, password, phone, } = updateDto; const legalUserUpdateData = { registrationName, economicCode, nationalIdentity, registrationCode, phone, }; const userUpdateData = { email, firstName, lastName, password, phone, birthDate, }; // const addressUpdateData = { // postalCode, // address, // cityId, // }; const user = await queryRunner.manager.findOne(this.userRepository.target, { where: { id: customerId } }); if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND); if (email) { const existingEmail = await queryRunner.manager.findOne(this.userRepository.target, { where: { email, id: Not(user.id) }, }); if (existingEmail) throw new BadRequestException(UserMessage.EMAIL_EXIST); } if (nationalCode) { const existingNationalCode = await queryRunner.manager.findOne(this.userRepository.target, { where: { nationalCode: nationalCode, id: Not(user.id) }, }); if (existingNationalCode) throw new BadRequestException(UserMessage.NATIONAL_CODE_EXIST); } if (phone) { const existingPhone = await queryRunner.manager.findOne(this.userRepository.target, { where: { phone: phone, id: Not(user.id) }, }); if (existingPhone) throw new BadRequestException(UserMessage.PHONE_EXIST); } if (nationalCode) { const existingNationalCode = await queryRunner.manager.findOne(this.realUserRepository.target, { where: { nationalCode: nationalCode, id: Not(user.id) }, }); if (existingNationalCode) throw new BadRequestException(UserMessage.NATIONAL_CODE_EXIST); } await queryRunner.manager.update(this.userRepository.target, { id: customerId }, userUpdateData); if (password) { const hashPassword = await this.passwordService.hashPassword(password); await queryRunner.manager.update(this.userRepository.target, { id: customerId }, { password: hashPassword }); } const legalUser = await queryRunner.manager.findOne(this.legalUserRepository.target, { where: { user: { id: customerId } }, relations: { address: true }, }); if (!legalUser) throw new BadRequestException(UserMessage.LEGAL_DATA_NOT_FOUND); if (economicCode) { const existingEconomicCode = await queryRunner.manager.findOne(this.legalUserRepository.target, { where: { economicCode, id: Not(legalUser.id) }, }); if (existingEconomicCode) throw new BadRequestException(UserMessage.ECONOMIC_CODE_EXIST); } if (registrationCode) { const existingRegistrationCode = await queryRunner.manager.findOne(this.legalUserRepository.target, { where: { registrationCode, id: Not(legalUser.id) }, }); if (existingRegistrationCode) throw new BadRequestException(UserMessage.REGISTRATION_CODE_EXIST); } if (nationalIdentity) { const existingNationalIdentity = await queryRunner.manager.findOne(this.legalUserRepository.target, { where: { nationalIdentity, id: Not(legalUser.id) }, }); if (existingNationalIdentity) throw new BadRequestException(UserMessage.NATIONAL_IDENTITY_EXIST); } if (registrationName) { const existingRegistrationName = await queryRunner.manager.findOne(this.legalUserRepository.target, { where: { registrationName, id: Not(legalUser.id) }, }); if (existingRegistrationName) throw new BadRequestException(UserMessage.REGISTRATION_NAME_EXIST); } await queryRunner.manager.update(this.legalUserRepository.target, { id: legalUser.id }, legalUserUpdateData); // Update address if needed if (cityId || address || postalCode) { // Get the address entity const addressEntity = legalUser.address; if (!addressEntity) throw new BadRequestException(UserMessage.ADDRESS_NOT_FOUND); // Prepare address updates const addressUpdates: Record = {}; if (cityId) { const { city } = await this.addressService.getCity(+cityId); addressUpdates.city = city; } if (address) { addressUpdates.fullAddress = address; } if (postalCode) { addressUpdates.postalCode = postalCode; } await queryRunner.manager.update(Address, { id: addressEntity.id }, addressUpdates); } await queryRunner.commitTransaction(); return { message: CommonMessage.UPDATE_SUCCESS, }; } catch (error) { await queryRunner.rollbackTransaction(); this.logger.error("Error in update customer", error); throw error; } finally { await queryRunner.release(); } } /************************************************************ */ async getCustomersCount() { const count = await this.userRepository.count({ where: { roles: { name: RoleEnum.USER } } }); return count; } /************************************************************ */ async createRealUserData(createDto: CreateRealUserDto, userId: string, queryRunner = this.dataSource.createQueryRunner()) { let transactionStarted = false; try { if (!queryRunner.isTransactionActive) { await queryRunner.connect(); await queryRunner.startTransaction(); transactionStarted = true; } const user = await queryRunner.manager.findOne(this.userRepository.target, { where: { id: userId } }); if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND); if (user.financialType && user.financialType === FinancialType.LEGAL) throw new BadRequestException(UserMessage.FINANCIAL_TYPE_IS_LEGAL); const existingRealUser = await queryRunner.manager.findOne(this.realUserRepository.target, { where: { user: { id: userId } } }); if (existingRealUser) throw new BadRequestException(UserMessage.REAL_DATA_EXIST); const duplicateChecks = await Promise.all([ queryRunner.manager.findOne(this.realUserRepository.target, { where: { nationalCode: createDto.nationalCode } }), queryRunner.manager.findOne(this.realUserRepository.target, { where: { phone: createDto.phone } }), ]); if (duplicateChecks[0]) throw new BadRequestException(UserMessage.NATIONAL_CODE_EXIST); if (duplicateChecks[1]) throw new BadRequestException(UserMessage.PHONE_EXIST); const { city } = await this.addressService.getCity(+createDto.cityId); const realUser = queryRunner.manager.create(this.realUserRepository.target, { ...createDto, user, address: { fullAddress: createDto.address, city, postalCode: createDto.postalCode, }, }); await queryRunner.manager.save(realUser); user.financialType = FinancialType.REAL; await queryRunner.manager.save(user); if (transactionStarted) await queryRunner.commitTransaction(); return { message: CommonMessage.CREATED, realUser, }; } catch (error) { if (transactionStarted) await queryRunner.rollbackTransaction(); throw error; } finally { if (transactionStarted) await queryRunner.release(); } } // /************************************************************ */ async createLegalUserData(createDto: CreateLegalUserDto, userId: string, queryRunner = this.dataSource.createQueryRunner()) { let transactionStarted = false; try { if (!queryRunner.isTransactionActive) { await queryRunner.connect(); await queryRunner.startTransaction(); transactionStarted = true; } const user = await queryRunner.manager.findOne(this.userRepository.target, { where: { id: userId } }); if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND); if (user.financialType && user.financialType === FinancialType.REAL) throw new BadRequestException(UserMessage.FINANCIAL_TYPE_IS_REAL); const existingLegalUser = await queryRunner.manager.findOne(this.legalUserRepository.target, { where: { user: { id: user.id } } }); if (existingLegalUser) throw new BadRequestException(UserMessage.LEGAL_DATA_EXIST); const duplicateChecks = await Promise.all([ queryRunner.manager.findOne(this.legalUserRepository.target, { where: { economicCode: createDto.economicCode } }), queryRunner.manager.findOne(this.legalUserRepository.target, { where: { phone: createDto.phone } }), queryRunner.manager.findOne(this.legalUserRepository.target, { where: { registrationCode: createDto.registrationCode } }), queryRunner.manager.findOne(this.legalUserRepository.target, { where: { nationalIdentity: createDto.nationalIdentity } }), ]); if (duplicateChecks[0]) throw new BadRequestException(UserMessage.ECONOMIC_CODE_EXIST); if (duplicateChecks[1]) throw new BadRequestException(UserMessage.PHONE_EXIST); if (duplicateChecks[2]) throw new BadRequestException(UserMessage.REGISTRATION_CODE_EXIST); if (duplicateChecks[3]) throw new BadRequestException(UserMessage.NATIONAL_IDENTITY_EXIST); const { city } = await this.addressService.getCity(+createDto.cityId); const legalUser = queryRunner.manager.create(this.legalUserRepository.target, { ...createDto, user, address: { fullAddress: createDto.address, city, postalCode: createDto.postalCode, }, }); await queryRunner.manager.save(legalUser); user.financialType = FinancialType.LEGAL; await queryRunner.manager.save(user); if (transactionStarted) await queryRunner.commitTransaction(); return { message: CommonMessage.CREATED, legalUser, }; } catch (error) { if (transactionStarted) await queryRunner.rollbackTransaction(); throw error; } finally { if (transactionStarted) await queryRunner.release(); } } /************************************************************ */ async getCustomers(queryDto: SearchCustomersDto) { const [customers, count] = await this.userRepository.getCustomer(queryDto); return { customers, count }; } /************************************************************ */ async getCustomerById(userId: string) { const customer = await this.userRepository.findOne({ where: { id: userId, roles: { name: RoleEnum.USER } }, relations: { legalUser: { address: { city: { province: true } } }, realUser: { address: { city: { province: true } } } }, }); return { customer }; } }