This commit is contained in:
2026-04-16 12:57:03 +03:30
parent 5ae39bd082
commit b369794d42
+191 -119
View File
@@ -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);
}
}