From b369794d421fe4339e6e0aa76d6e2a3b2467eb6c Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Thu, 16 Apr 2026 12:57:03 +0330 Subject: [PATCH] clean --- src/modules/reseller/reseller.service.ts | 310 ++++++++++++++--------- 1 file changed, 191 insertions(+), 119 deletions(-) diff --git a/src/modules/reseller/reseller.service.ts b/src/modules/reseller/reseller.service.ts index 68e0a1a..b10c91e 100644 --- a/src/modules/reseller/reseller.service.ts +++ b/src/modules/reseller/reseller.service.ts @@ -30,238 +30,293 @@ export class ResellerService { private readonly userBankAccountRepository: UserBankAccountRepository, private readonly walletsService: WalletsService, private readonly dataSource: DataSource, - ) { } + ) {} + // --------------------------------------------------------------------------- + // Create a reseller record for an existing user. + // --------------------------------------------------------------------------- async create(dto: CreateResellerDto) { - const { name, phone } = dto + const { name, phone } = dto; - const user = await this.usersService.findOneWithPhone(phone) + const user = await this.usersService.findOneWithPhone(phone); if (!user) { - throw new BadRequestException(AuthMessage.USER_NOT_FOUND) + throw new BadRequestException(AuthMessage.USER_NOT_FOUND); } const reseller = this.resellerRepository.create({ name, - owner: user - }) + owner: user, + }); - await this.resellerRepository.save(reseller) + await this.resellerRepository.save(reseller); - return reseller + return reseller; } + // --------------------------------------------------------------------------- + // Return all resellers with their owners. + // --------------------------------------------------------------------------- async findAll() { return this.resellerRepository.find({ relations: { - owner: true - } - }) + owner: true, + }, + }); } + // --------------------------------------------------------------------------- + // Return agents assigned to the reseller owner. + // --------------------------------------------------------------------------- async finResellerAgents(userId: string) { - const reseller = await this.FindOneOrFailByUserId(userId) + const reseller = await this.FindOneOrFailByUserId(userId); - return reseller.agents + return reseller.agents; } - + // --------------------------------------------------------------------------- + // Find a reseller by owner user id and fail if it does not exist. + // --------------------------------------------------------------------------- async FindOneOrFailByUserId(userId: string) { const reseller = await this.resellerRepository.findOne({ where: { owner: { id: userId } }, - relations: { agents: true } - }) + relations: { agents: true }, + }); if (!reseller) { - throw new NotFoundException() + throw new NotFoundException(); } - return reseller + return reseller; } + // --------------------------------------------------------------------------- + // Attach an existing user to the reseller as an agent. + // --------------------------------------------------------------------------- async createResellerAgent(userId: string, userTobeAgentPhone: string) { - const reseller = await this.FindOneOrFailByUserId(userId) + const reseller = await this.FindOneOrFailByUserId(userId); - const user = await this.usersService.findOneWithPhone(userTobeAgentPhone) + const user = await this.usersService.findOneWithPhone(userTobeAgentPhone); if (!user) { - throw new NotFoundException(AuthMessage.USER_NOT_FOUND) + throw new NotFoundException(AuthMessage.USER_NOT_FOUND); } - user.reseller = reseller + user.reseller = reseller; - await this.usersService.save(user) + await this.usersService.save(user); - return user + return user; } + // --------------------------------------------------------------------------- + // Remove an agent from the reseller if it belongs to them. + // --------------------------------------------------------------------------- async removeResellerAgent(userId: string, agentId: string) { - const reseller = await this.FindOneOrFailByUserId(userId) - - const { user } = await this.usersService.findOneById(agentId) + const reseller = await this.FindOneOrFailByUserId(userId); + + const { user } = await this.usersService.findOneById(agentId); if (!user) { - throw new NotFoundException(AuthMessage.USER_NOT_FOUND) + throw new NotFoundException(AuthMessage.USER_NOT_FOUND); } if (user.reseller?.id !== reseller.id) { - throw new BadRequestException("بازاریاب به این نمایندگی تعلق ندارد") + throw new BadRequestException('بازاریاب به این نمایندگی تعلق ندارد'); } - user.reseller = undefined + user.reseller = undefined; - await this.usersService.save(user) + await this.usersService.save(user); - return user + return user; } + // --------------------------------------------------------------------------- + // Return customers referred by all agents of the reseller. + // --------------------------------------------------------------------------- async findResellerCustomers(userId: string, query: SearchCustomersQueryDto) { - const reselller = await this.FindOneOrFailByUserId(userId) + const reseller = await this.FindOneOrFailByUserId(userId); - const agentIds = reselller.agents.map(ag => ag.id) + const agentIds = reseller.agents.map((agent) => agent.id); if (!agentIds.length) { return { customers: [], count: 0, paginate: true }; } - return this.referralService.findPaginatedReferedUsers(agentIds, query) + return this.referralService.findPaginatedReferedUsers(agentIds, query); } - // *************** Invoice *************** + // --------------------------------------------------------------------------- + // Resolve invoice lookup based on reseller or agent role. + // --------------------------------------------------------------------------- async findInvoices(userId: string, type: ResellerType, query: SearchResellerInvoicesQueryDto) { - if (type == ResellerType.agent) { - return this.findAgentCustomersInvoices(userId, query) - } else if (type == ResellerType.reseller) { - return this.findResellerCustomersInvoices(userId, query) + if (type === ResellerType.agent) { + return this.findAgentCustomersInvoices(userId, query); + } + + if (type === ResellerType.reseller) { + return this.findResellerCustomersInvoices(userId, query); } } + // --------------------------------------------------------------------------- + // Return invoices for customers referred by the reseller's agents. + // --------------------------------------------------------------------------- private async findResellerCustomersInvoices(userId: string, query: SearchResellerInvoicesQueryDto) { - const reselller = await this.FindOneOrFailByUserId(userId) + const reseller = await this.FindOneOrFailByUserId(userId); - const agentIds = reselller.agents.map(ag => ag.id) + const agentIds = reseller.agents.map((agent) => agent.id); if (!agentIds.length) { return { invoices: [], count: 0, paginate: true }; } - const customerIds = await this.referralService.findReferedUsersIds(agentIds) + const customerIds = await this.referralService.findReferedUsersIds(agentIds); - const [invoices, count] = await this.invoicesService.findInvoicesByCustomerIds(customerIds, query) + const [invoices, count] = await this.invoicesService.findInvoicesByCustomerIds(customerIds, query); return { invoices, count, paginate: true }; - } + // --------------------------------------------------------------------------- + // Return invoices for customers referred by a single agent. + // --------------------------------------------------------------------------- private async findAgentCustomersInvoices(userId: string, query: SearchResellerInvoicesQueryDto) { + const agentIds = [userId]; - const agentIds = [userId] + const customerIds = await this.referralService.findReferedUsersIds(agentIds); - const customerIds = await this.referralService.findReferedUsersIds(agentIds) - - const [invoices, count] = await this.invoicesService.findInvoicesByCustomerIds(customerIds, query) + const [invoices, count] = await this.invoicesService.findInvoicesByCustomerIds(customerIds, query); return { invoices, count, paginate: true }; - } - // *********** Rewards ********************* + // --------------------------------------------------------------------------- + // Resolve reward lookup based on reseller or agent role. + // --------------------------------------------------------------------------- async findRewards(userId: string, type: ResellerType, query: SearchWithdrawRequestQueryDto) { - if (type == ResellerType.agent) { - return this.findAgentRewards(userId, query) - } else if (type == ResellerType.reseller) { - return this.findResellerAgentsRewards(userId, query) + if (type === ResellerType.agent) { + return this.findAgentRewards(userId, query); + } + + if (type === ResellerType.reseller) { + return this.findResellerAgentsRewards(userId, query); } } + // --------------------------------------------------------------------------- + // Return rewards earned by all agents under a reseller. + // --------------------------------------------------------------------------- async findResellerAgentsRewards(userId: string, query: SearchWithdrawRequestQueryDto) { - const reselller = await this.FindOneOrFailByUserId(userId) + const reseller = await this.FindOneOrFailByUserId(userId); - const agentIds = reselller.agents.map(ag => ag.id) + const agentIds = reseller.agents.map((agent) => agent.id); if (!agentIds.length) { return { rewards: [], count: 0, paginate: true }; } - return this.referralService.findRewards(agentIds, query) + return this.referralService.findRewards(agentIds, query); } + // --------------------------------------------------------------------------- + // Return rewards earned by a single agent. + // --------------------------------------------------------------------------- async findAgentRewards(userId: string, query: SearchWithdrawRequestQueryDto) { - return this.referralService.findRewards([userId], query) + return this.referralService.findRewards([userId], query); } - //************* withdraw request ***********/ + // --------------------------------------------------------------------------- + // Return paginated withdraw requests for admin review. + // --------------------------------------------------------------------------- async getwithdrawRequestsForAdmin(queryDto: SearchWithdrawRequestQueryDto) { - const [requests, count] = await this.withdrawRequestRepository.findPaginatedListForAdmin(queryDto); return { requests, count, paginate: true }; } + // --------------------------------------------------------------------------- + // Return paginated withdraw requests for a specific user. + // --------------------------------------------------------------------------- async getWithdrawRequests(userId: string, queryDto: SearchWithdrawRequestQueryDto) { - const [requests, count] = await this.withdrawRequestRepository.findPaginatedList(userId, queryDto); return { requests, count, paginate: true }; } + // --------------------------------------------------------------------------- + // Create a withdraw request after validating balance and pending requests. + // --------------------------------------------------------------------------- async createWithdrawRequest(userId: string, requestedAmount: number) { - const { user } = await this.usersService.findOneById(userId) + const { user } = await this.usersService.findOneById(userId); if (!user) { - throw new NotFoundException(AuthMessage.USER_NOT_FOUND) + throw new NotFoundException(AuthMessage.USER_NOT_FOUND); } const pendingWithdrawRequest = await this.withdrawRequestRepository.findOne({ where: { user: { id: userId }, - paidAt: IsNull() - } - }) + paidAt: IsNull(), + }, + }); + if (pendingWithdrawRequest) { - throw new BadRequestException("امکان ایجاد درخواست برداشت جدید نیست") + throw new BadRequestException('امکان ایجاد درخواست برداشت جدید نیست'); } - const { balance } = await this.walletsService.getBalance(userId) + const { balance } = await this.walletsService.getBalance(userId); if (new Decimal(balance).lessThan(requestedAmount)) { - throw new BadRequestException("برداشت بیشتر از موجودی ممکن نیست") + throw new BadRequestException('برداشت بیشتر از موجودی ممکن نیست'); } + const request = this.withdrawRequestRepository.create({ requestedAmount, - user - }) + user, + }); - return this.withdrawRequestRepository.save(request) + return this.withdrawRequestRepository.save(request); } + // --------------------------------------------------------------------------- + // Delete a pending withdraw request owned by the user. + // --------------------------------------------------------------------------- async removeWithdrawRequest(userId: string, withdrawRequestId: string) { - const request = await this.withdrawRequestRepository.findOne({ where: { id: withdrawRequestId, - user: { id: userId } - } - }) + user: { id: userId }, + }, + }); if (!request) { - throw new NotFoundException() - } - if (request.paidAt) { - throw new BadRequestException("درخواست پرداخت شده قابل حذف نیست") + throw new NotFoundException(); } - return this.withdrawRequestRepository.remove(request) + if (request.paidAt) { + throw new BadRequestException('درخواست پرداخت شده قابل حذف نیست'); + } + + return this.withdrawRequestRepository.remove(request); } + // --------------------------------------------------------------------------- + // Confirm a withdraw request and create its wallet transaction atomically. + // --------------------------------------------------------------------------- async confirmWithdrawRequest(requestId: string, transactionId: string) { - const request = await this.withdrawRequestRepository.findOne({ where: { id: requestId }, relations: ['user'] }) + const request = await this.withdrawRequestRepository.findOne({ + where: { id: requestId }, + relations: ['user'], + }); + if (!request) { - throw new BadRequestException("درخواست برداشت یافت نشد .") + throw new BadRequestException('درخواست برداشت یافت نشد .'); } if (request.paidAt) { - throw new BadRequestException("قبلا تایید شده است") + throw new BadRequestException('قبلا تایید شده است'); } this.logger.log(`confirm withdraw request for request id ${requestId} `); @@ -272,14 +327,14 @@ export class ResellerService { await queryRunner.connect(); await queryRunner.startTransaction(); - const amount = new Decimal(request.requestedAmount) + const amount = new Decimal(request.requestedAmount); - await this.walletsService.createWithdrawTransaction(request.user.id, amount, request, queryRunner) + await this.walletsService.createWithdrawTransaction(request.user.id, amount, request, queryRunner); - request.paidAt = new Date() - request.transactionId = transactionId + request.paidAt = new Date(); + request.transactionId = transactionId; - await queryRunner.manager.save(request) + await queryRunner.manager.save(request); await queryRunner.commitTransaction(); return true; @@ -292,69 +347,86 @@ export class ResellerService { } } - //********* Bank Account ***********/ + // --------------------------------------------------------------------------- + // Return all bank accounts registered for a user. + // --------------------------------------------------------------------------- async getUserBankAccount(userId: string) { - return this.userBankAccountRepository.find({ - where: { user: { id: userId } } - }) + where: { user: { id: userId } }, + }); } - //********* Bank Account ***********/ + // --------------------------------------------------------------------------- + // Create a bank account if the iban and user record are not duplicated. + // --------------------------------------------------------------------------- async createUserBankAccount(userId: string, dto: CreateUserBankAccountDto) { - const { user } = await this.usersService.findOneById(userId) + const { user } = await this.usersService.findOneById(userId); const exitingBankAccount = await this.userBankAccountRepository.findOne({ - where: { - IBan: dto.IBan - } - }) + where: [ + { + IBan: dto.IBan, + }, + { + user: { id: userId }, + }, + ], + }); if (exitingBankAccount) { - throw new BadRequestException("شماره شبا موجود می باشد .") + throw new BadRequestException('شماره شبا موجود می باشد .'); } const account = this.userBankAccountRepository.create({ user, - ...dto - }) + ...dto, + }); - return this.userBankAccountRepository.save(account) + return this.userBankAccountRepository.save(account); } - //********* Remove Bank Account ***********/ + // --------------------------------------------------------------------------- + // Delete a bank account that belongs to the user. + // --------------------------------------------------------------------------- async deleteUserBankAccount(userId: string, bankAccountId: string) { - return this.userBankAccountRepository.delete({ user: { id: userId }, - id: bankAccountId - }) - + id: bankAccountId, + }); } - //*********** Referral Code ************/ + // --------------------------------------------------------------------------- + // Resolve referral code lookup based on reseller or agent role. + // --------------------------------------------------------------------------- async getReferralCodes(userId: string, type: ResellerType, query: PaginationDto) { - if (type == ResellerType.agent) { - return this.findAgentReferralCodes(userId, query) - } else if (type == ResellerType.reseller) { - return this.findResellerAgentsReferralCodes(userId, query) + if (type === ResellerType.agent) { + return this.findAgentReferralCodes(userId, query); + } + + if (type === ResellerType.reseller) { + return this.findResellerAgentsReferralCodes(userId, query); } } + // --------------------------------------------------------------------------- + // Return referral codes created by all agents under a reseller. + // --------------------------------------------------------------------------- private async findResellerAgentsReferralCodes(userId: string, query: SearchWithdrawRequestQueryDto) { - const reselller = await this.FindOneOrFailByUserId(userId) + const reseller = await this.FindOneOrFailByUserId(userId); - const agentIds = reselller.agents.map(ag => ag.id) + const agentIds = reseller.agents.map((agent) => agent.id); if (!agentIds.length) { return { codes: [], count: 0, paginate: true }; } - return this.referralService.getReferralCodesByUserIds(agentIds, query) + return this.referralService.getReferralCodesByUserIds(agentIds, query); } + // --------------------------------------------------------------------------- + // Return referral codes created by a single agent. + // --------------------------------------------------------------------------- private async findAgentReferralCodes(userId: string, query: SearchWithdrawRequestQueryDto) { - return this.referralService.getReferralCodesByUserIds([userId], query) + return this.referralService.getReferralCodesByUserIds([userId], query); } - }