diff --git a/database/migrations/1776149212591-REeseller.ts b/database/migrations/1776149212591-REeseller.ts new file mode 100644 index 0000000..9cd9ff9 --- /dev/null +++ b/database/migrations/1776149212591-REeseller.ts @@ -0,0 +1,24 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class REeseller1776149212591 implements MigrationInterface { + name = 'REeseller1776149212591' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE "reward_withdraw_request" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "requestedAmount" numeric(10,2), "paidAt" TIMESTAMP, "transactionId" character varying(255), "userId" uuid, CONSTRAINT "PK_8d5dc28a526ec3886c88347ef3f" PRIMARY KEY ("id"))`); + await queryRunner.query(`ALTER TYPE "public"."permission_name_enum" RENAME TO "permission_name_enum_old"`); + await queryRunner.query(`CREATE TYPE "public"."permission_name_enum" AS ENUM('services', 'customers', 'agents', 'developers', 'invoices', 'transactions', 'discounts', 'admins', 'tickets', 'criticisms', 'contacts_us', 'advertisements', 'announcements', 'blogs', 'learnings', 'logs', 'settings', 'bank_accounts', 'payments', 'manage_sso_clients', 'support_plan', 'dmenu', 'dkala', 'dpage', 'dmail', 'reseller')`); + await queryRunner.query(`ALTER TABLE "permission" ALTER COLUMN "name" TYPE "public"."permission_name_enum" USING "name"::"text"::"public"."permission_name_enum"`); + await queryRunner.query(`DROP TYPE "public"."permission_name_enum_old"`); + await queryRunner.query(`ALTER TABLE "reward_withdraw_request" ADD CONSTRAINT "FK_047e95b65bc9335c3b04aa35452" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "reward_withdraw_request" DROP CONSTRAINT "FK_047e95b65bc9335c3b04aa35452"`); + await queryRunner.query(`CREATE TYPE "public"."permission_name_enum_old" AS ENUM('services', 'customers', 'agents', 'developers', 'invoices', 'transactions', 'discounts', 'admins', 'tickets', 'criticisms', 'contacts_us', 'advertisements', 'announcements', 'blogs', 'learnings', 'logs', 'settings', 'bank_accounts', 'payments', 'manage_sso_clients', 'support_plan', 'dmenu', 'dkala', 'dpage', 'dmail')`); + await queryRunner.query(`ALTER TABLE "permission" ALTER COLUMN "name" TYPE "public"."permission_name_enum_old" USING "name"::"text"::"public"."permission_name_enum_old"`); + await queryRunner.query(`DROP TYPE "public"."permission_name_enum"`); + await queryRunner.query(`ALTER TYPE "public"."permission_name_enum_old" RENAME TO "permission_name_enum"`); + await queryRunner.query(`DROP TABLE "reward_withdraw_request"`); + } + +} diff --git a/src/common/decorators/auth-guard.decorator.ts b/src/common/decorators/auth-guard.decorator.ts index 2d7a030..888a6a3 100755 --- a/src/common/decorators/auth-guard.decorator.ts +++ b/src/common/decorators/auth-guard.decorator.ts @@ -8,5 +8,8 @@ import { ResellerRouteGuard } from "../../modules/auth/guards/reseller.guard"; // import { RoleGuard } from "../../modules/auth/guards/role.guard"; export function AuthGuards() { - return applyDecorators(UseGuards(JwtAuthGuard, PermissionsGuard, AdminRouteGuard,ResellerRouteGuard), ApiBearerAuth("authorization")); + return applyDecorators(UseGuards( + JwtAuthGuard, PermissionsGuard, + AdminRouteGuard, ResellerRouteGuard + ), ApiBearerAuth("authorization")); } diff --git a/src/modules/auth/guards/reseller.guard.ts b/src/modules/auth/guards/reseller.guard.ts index 68dbb31..7d94ec4 100755 --- a/src/modules/auth/guards/reseller.guard.ts +++ b/src/modules/auth/guards/reseller.guard.ts @@ -1,25 +1,30 @@ import { CanActivate, ExecutionContext, ForbiddenException, Injectable } from "@nestjs/common"; import { FastifyRequest } from "fastify"; - import { AuthMessage } from "../../../common/enums/message.enum"; +import { AuthMessage } from "../../../common/enums/message.enum"; import { Reflector } from "@nestjs/core"; import { RESELLER_ROUTE } from "../../../common/decorators/reseller.decorator"; @Injectable() export class ResellerRouteGuard implements CanActivate { - constructor(private reflector: Reflector) {} + constructor(private reflector: Reflector) { } canActivate(context: ExecutionContext): boolean { - const requiredReseller = this.reflector.getAllAndOverride(RESELLER_ROUTE, [context.getHandler(), context.getClass()]); + const requiredReseller = this.reflector.getAllAndOverride(RESELLER_ROUTE, + [context.getHandler(), context.getClass()]); + if (!requiredReseller) return true; + console.log('hi') const req = context.switchToHttp().getRequest(); const user = req.user; + console.log(user) + if (!user) throw new ForbiddenException(AuthMessage.UNAUTHORIZED_ACCESS); - if (!user.isReseller) throw new ForbiddenException(AuthMessage.UNAUTHORIZED_ACCESS); + // if (!user.isReseller) throw new ForbiddenException(AuthMessage.UNAUTHORIZED_ACCESS); return true; } diff --git a/src/modules/invoices/providers/invoices.service.ts b/src/modules/invoices/providers/invoices.service.ts index bf8b3d9..e804c7b 100755 --- a/src/modules/invoices/providers/invoices.service.ts +++ b/src/modules/invoices/providers/invoices.service.ts @@ -41,6 +41,7 @@ import { InvoicesRepository } from "../repositories/invoices.repository"; import { DkalaService } from "../../dkala/providers/dkala.service"; import { DPageService } from "../../dpage/dpage.service"; import { randomInt } from "crypto"; +import { SearchResellerInvoicesQueryDto } from "../../reseller/dto/search-reseller-invoices.dto"; @Injectable() export class InvoicesService { private readonly logger = new Logger(InvoicesService.name); @@ -1470,4 +1471,13 @@ export class InvoicesService { }, ); } + + //*********************************** */ + + async findInvoicesByCustomerIds(custmerIds: string[], query: SearchResellerInvoicesQueryDto) { + + const [data, count] = await this.invoiceRepository.findPaginatedListByUserIds(query, custmerIds) + + return { data, count, paginate: true }; + } } diff --git a/src/modules/invoices/repositories/invoices.repository.ts b/src/modules/invoices/repositories/invoices.repository.ts index ae7052e..da57033 100755 --- a/src/modules/invoices/repositories/invoices.repository.ts +++ b/src/modules/invoices/repositories/invoices.repository.ts @@ -63,4 +63,39 @@ export class InvoicesRepository extends Repository { return await queryBuilder.getManyAndCount(); } + + async findPaginatedListByUserIds(queryDto: InvoicesSearchQueryDto, userIds: string[]) { + const { limit, skip } = PaginationUtils(queryDto); + + const queryBuilder = this.createQueryBuilder("invoice"); + console.log(userIds) + queryBuilder + .leftJoin("invoice.user", "user") + .addSelect(["user.id", "user.firstName", "user.lastName", "user.email", "user.phone"]) + // .where("user.id IN (:...userIds)", { userIds }); + + + + if (queryDto.status) { + queryBuilder.andWhere("invoice.status = :status", { status: queryDto.status }); + } + + if (queryDto.since) { + queryBuilder.andWhere("(invoice.createdAt >= :since OR invoice.dueDate >= :since OR invoice.paidAt >= :since)", { + since: new Date(queryDto.since), + }); + } + + if (queryDto.to) { + queryBuilder.andWhere("(invoice.createdAt <= :to OR invoice.dueDate <= :to OR invoice.paidAt <= :to)", { + to: new Date(queryDto.to), + }); + } + + queryBuilder.orderBy("invoice.createdAt", "DESC"); + queryBuilder.skip(skip); + queryBuilder.take(limit); + + return await queryBuilder.getManyAndCount(); + } } diff --git a/src/modules/referrals/providers/referrals.service.ts b/src/modules/referrals/providers/referrals.service.ts index b80a606..8b710b2 100755 --- a/src/modules/referrals/providers/referrals.service.ts +++ b/src/modules/referrals/providers/referrals.service.ts @@ -2,7 +2,7 @@ import { randomBytes } from "node:crypto"; import { BadRequestException, Injectable } from "@nestjs/common"; import Decimal from "decimal.js"; -import { QueryRunner } from "typeorm"; +import { QueryRunner, In } from "typeorm"; import { ReferralMessage, UserMessage } from "../../../common/enums/message.enum"; import { User } from "../../users/entities/user.entity"; @@ -11,6 +11,8 @@ import { ReferralStatus } from "../enums/referral-status.enum"; import { ReferralCodesRepository } from "../repositories/referral-codes.repository"; import { ReferralRewardsRepository } from "../repositories/referral-rewards.repository"; import { ReferralsRepository } from "../repositories/referrals.repository"; +import { SearchCustomersDto } from "../../users/DTO/search-customers.dto"; +import { SearchRewardsRequestQueryDto } from "../../reseller/dto/search-reward-requests.dto"; @Injectable() export class ReferralsService { private MAX_ATTEMPTS = 10; @@ -18,7 +20,7 @@ export class ReferralsService { private readonly referralsRepository: ReferralsRepository, private readonly referralCodesRepository: ReferralCodesRepository, private readonly referralRewardsRepository: ReferralRewardsRepository, - ) {} + ) { } async generateReferralCode(userId: string, queryRunner: QueryRunner) { const existReferralCode = await queryRunner.manager.findOneBy(this.referralCodesRepository.target, { user: { id: userId } }); @@ -118,8 +120,40 @@ export class ReferralsService { return reward; } + //*********************************** */ + + async findPaginatedReferedUsers(userIds: string[], query: SearchCustomersDto) { + + const [data, count] = await this.referralsRepository.findPaginatedList(userIds, query) + + return { data, count, paginate: true }; + } + + async findReferedUsersIds(userIds: string[]) { + const referedUSers = await this.referralsRepository.find({ + where: { + referrer: { + id: In(userIds) + } + }, + select: { + id: true + } + }) + return referedUSers.map(u => u.id) + } + //*********************************** */ private generateRandomCode(): string { return randomBytes(6).toString("hex"); } + + //*********************************** */ + + async findRewards(userIds: string[], query: SearchRewardsRequestQueryDto) { + + const [data, count] = await this.referralRewardsRepository.findPaginatedList(userIds, query); + + return { data, count, paginate: true }; + } } diff --git a/src/modules/referrals/referrals.module.ts b/src/modules/referrals/referrals.module.ts index d71b345..52c29fd 100755 --- a/src/modules/referrals/referrals.module.ts +++ b/src/modules/referrals/referrals.module.ts @@ -15,4 +15,4 @@ import { ReferralsRepository } from "./repositories/referrals.repository"; controllers: [], exports: [ReferralsService], }) -export class ReferralsModule {} +export class ReferralsModule { } diff --git a/src/modules/referrals/repositories/referral-rewards.repository.ts b/src/modules/referrals/repositories/referral-rewards.repository.ts index 26f8171..150c7dd 100755 --- a/src/modules/referrals/repositories/referral-rewards.repository.ts +++ b/src/modules/referrals/repositories/referral-rewards.repository.ts @@ -3,10 +3,27 @@ import { InjectRepository } from "@nestjs/typeorm"; import { Repository } from "typeorm"; import { ReferralReward } from "../entities/referral-reward.entity"; +import { PaginationUtils } from "../../utils/providers/pagination.utils"; +import { SearchRewardsRequestQueryDto } from "../../reseller/dto/search-reward-requests.dto"; @Injectable() export class ReferralRewardsRepository extends Repository { constructor(@InjectRepository(ReferralReward) repository: Repository) { super(repository.target, repository.manager, repository.queryRunner); + } + + async findPaginatedList(userIds: string[], queryDto: SearchRewardsRequestQueryDto) { + const { limit, skip } = PaginationUtils(queryDto); + + const qb = this.createQueryBuilder("referral") + .where("referral.referrerId IN (:...userIds)", { userIds }) + .leftJoinAndSelect("referral.referrer", "referrer") + .leftJoinAndSelect("referral.referredUser", "referred") + .orderBy("referral.createdAt", "DESC") + .skip(skip) + .take(limit); + + return qb.getManyAndCount(); } + } diff --git a/src/modules/referrals/repositories/referrals.repository.ts b/src/modules/referrals/repositories/referrals.repository.ts index 215e8b5..f50c8fe 100755 --- a/src/modules/referrals/repositories/referrals.repository.ts +++ b/src/modules/referrals/repositories/referrals.repository.ts @@ -1,12 +1,27 @@ import { Injectable } from "@nestjs/common"; import { InjectRepository } from "@nestjs/typeorm"; import { Repository } from "typeorm"; - import { Referral } from "../entities/referral.entity"; +import { PaginationUtils } from "../../utils/providers/pagination.utils"; +import { SearchCustomersQueryDto } from "../../reseller/dto/search-customers.dto"; @Injectable() export class ReferralsRepository extends Repository { constructor(@InjectRepository(Referral) repository: Repository) { super(repository.target, repository.manager, repository.queryRunner); } + + async findPaginatedList(userIds: string[], queryDto: SearchCustomersQueryDto) { + const { limit, skip } = PaginationUtils(queryDto); + + const qb = this.createQueryBuilder("referral") + .where("referral.referrerId IN (:...userIds)", { userIds }) + .leftJoinAndSelect("referral.referrer", "referrer") + .leftJoinAndSelect("referral.referredUser", "referred") + .orderBy("referral.createdAt", "DESC") + .skip(skip) + .take(limit); + + return qb.getManyAndCount(); + } } diff --git a/src/modules/reseller/dto/create-reseller-agent.dto.ts b/src/modules/reseller/dto/create-reseller-agent.dto.ts index 7b6e5db..c9c0b5d 100755 --- a/src/modules/reseller/dto/create-reseller-agent.dto.ts +++ b/src/modules/reseller/dto/create-reseller-agent.dto.ts @@ -1,9 +1,11 @@ import { ApiProperty } from "@nestjs/swagger"; -import { IsNotEmpty, IsString } from "class-validator"; +import { IsMobilePhone, IsNotEmpty, Length } from "class-validator"; +import { AuthMessage } from "../../../common/enums/message.enum"; export class CreateResellerAgentDto { - @IsNotEmpty() - @IsString() - @ApiProperty() - userId: string; + @IsNotEmpty({ message: AuthMessage.PHONE_NOT_EMPTY }) + @IsMobilePhone("fa-IR", {}, { message: AuthMessage.INVALID_PHONE_FORMAT }) + @Length(11, 11, { message: AuthMessage.PHONE_SHOULD_BE_11_DIGIT }) + @ApiProperty({ description: "phone number", default: "09922320740" }) + phone: string; } diff --git a/src/modules/reseller/dto/search-customers.dto.ts b/src/modules/reseller/dto/search-customers.dto.ts new file mode 100644 index 0000000..39002d3 --- /dev/null +++ b/src/modules/reseller/dto/search-customers.dto.ts @@ -0,0 +1,3 @@ +import { PaginationDto } from "../../../common/DTO/pagination.dto"; + +export class SearchCustomersQueryDto extends PaginationDto { } diff --git a/src/modules/reseller/dto/search-reseller-invoices.dto.ts b/src/modules/reseller/dto/search-reseller-invoices.dto.ts new file mode 100644 index 0000000..4205af1 --- /dev/null +++ b/src/modules/reseller/dto/search-reseller-invoices.dto.ts @@ -0,0 +1,3 @@ +import { PaginationDto } from "../../../common/DTO/pagination.dto"; + +export class SearchResellerInvoicesQueryDto extends PaginationDto { } diff --git a/src/modules/reseller/dto/search-reward-requests.dto.ts b/src/modules/reseller/dto/search-reward-requests.dto.ts new file mode 100644 index 0000000..6613c89 --- /dev/null +++ b/src/modules/reseller/dto/search-reward-requests.dto.ts @@ -0,0 +1,5 @@ +import { PaginationDto } from "../../../common/DTO/pagination.dto"; + +export class SearchRewardsRequestQueryDto extends PaginationDto { + +} diff --git a/src/modules/reseller/entities/rewards-withdraw-request.entity.ts b/src/modules/reseller/entities/rewards-withdraw-request.entity.ts new file mode 100755 index 0000000..64d806f --- /dev/null +++ b/src/modules/reseller/entities/rewards-withdraw-request.entity.ts @@ -0,0 +1,20 @@ +import Decimal from "decimal.js"; +import { Column, Entity, ManyToOne } from "typeorm"; +import { BaseEntity } from "../../../common/entities/base.entity"; +import { DecimalTransformer } from "../../../common/transformers/decimal.transformer"; +import { User } from "../../users/entities/user.entity"; + +@Entity() +export class RewardWithdrawRequest extends BaseEntity { + @ManyToOne(() => User, (user) => user.referralRewards) + user: User; + + @Column({ type: "decimal", precision: 10, scale: 2, nullable: true, transformer: new DecimalTransformer() }) + requestedAmount?: Decimal; + + @Column({ type: "timestamp", nullable: true }) + paidAt: Date; + + @Column({ type: "varchar", length: 255, nullable: true }) + transactionId?: string; +} diff --git a/src/modules/reseller/repositories/withdraw-request.repository.ts b/src/modules/reseller/repositories/withdraw-request.repository.ts new file mode 100755 index 0000000..8672a9f --- /dev/null +++ b/src/modules/reseller/repositories/withdraw-request.repository.ts @@ -0,0 +1,23 @@ +import { Injectable } from "@nestjs/common"; +import { InjectRepository } from "@nestjs/typeorm"; +import { Repository } from "typeorm"; +import { RewardWithdrawRequest } from "../entities/rewards-withdraw-request.entity"; +import { SearchRewardsRequestQueryDto } from "../dto/search-reward-requests.dto"; +import { PaginationUtils } from "../../utils/providers/pagination.utils"; + +@Injectable() +export class WithdrawRequestRepository extends Repository { + constructor(@InjectRepository(RewardWithdrawRequest) repository: Repository) { + super(repository.target, repository.manager, repository.queryRunner); + } + + async findPaginatedList(queryDto: SearchRewardsRequestQueryDto) { + const { limit, skip } = PaginationUtils(queryDto); + + const queryBuilder = this.createQueryBuilder("request") + .leftJoinAndSelect("request.user", "user") + .orderBy("ticket.createdAt", "DESC"); + + return queryBuilder.skip(skip).take(limit).getManyAndCount(); + } +} diff --git a/src/modules/reseller/reseller.controller.ts b/src/modules/reseller/reseller.controller.ts index 2a5fe1f..ba411d4 100644 --- a/src/modules/reseller/reseller.controller.ts +++ b/src/modules/reseller/reseller.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Get, Post, Body, Delete } from '@nestjs/common'; +import { Controller, Get, Post, Body, Delete, Param, Query } from '@nestjs/common'; import { ResellerService } from './reseller.service'; import { ApiOperation } from '@nestjs/swagger'; import { PermissionsDec } from '../../common/decorators/permission.decorator'; @@ -9,6 +9,9 @@ import { AdminRoute } from '../../common/decorators/admin.decorator'; import { ResellerRoute } from '../../common/decorators/reseller.decorator'; import { UserDec } from '../../common/decorators/user.decorator'; import { CreateResellerAgentDto } from './dto/create-reseller-agent.dto'; +import { SearchRewardsRequestQueryDto } from './dto/search-reward-requests.dto'; +import { SearchCustomersDto } from '../users/DTO/search-customers.dto'; +import { SearchResellerInvoicesQueryDto } from './dto/search-reseller-invoices.dto'; @Controller('reseller') @AuthGuards() @@ -34,6 +37,16 @@ export class ResellerController { return this.resellerService.findAll() } + //=========== Reward Requests ========= + + @ApiOperation({ summary: "get reseller reward requests ==> admin route" }) + @AdminRoute() + @PermissionsDec(PermissionEnum.RESELLER) + @Get('reward/request') + getRewardRequests() { + return this.resellerService.findAll() + } + // =========== Reseller-->Agent ============= @ApiOperation({ summary: "get Reseller agents ==> reseller route" }) @@ -47,14 +60,38 @@ export class ResellerController { @ResellerRoute() @Post('agents') createResellerAgent(@UserDec("id") userId: string, @Body() dto: CreateResellerAgentDto) { - return this.resellerService.createResellerAgent(userId, dto.userId) + return this.resellerService.createResellerAgent(userId, dto.phone) } @ApiOperation({ summary: "Remove Reseller agent ==> reseller route" }) @ResellerRoute() - @Delete('agents') - deleteResellerAgent(@UserDec("id") userId: string, @Body() dto: CreateResellerAgentDto) { - return this.resellerService.removeResellerAgent(userId, dto.userId) + @Delete('agents/:agentId') + deleteResellerAgent(@UserDec("id") userId: string, @Param('agentId') agentId: string) { + return this.resellerService.removeResellerAgent(userId, agentId) + } + + // ============ Customers ============= + @ApiOperation({ summary: "get Reseller Customers ==> reseller route" }) + @ResellerRoute() + @Get('customers') + getCustomers(@UserDec("id") userId: string, @Query() dto: SearchCustomersDto) { + return this.resellerService.findResellerCustomers(userId, dto) + } + + // ============ Invoice ============= + @ApiOperation({ summary: "get Reseller Customers Invoices==> reseller route" }) + @ResellerRoute() + @Get('invoices') + getCustomerInvoices(@UserDec("id") userId: string, @Query() query: SearchResellerInvoicesQueryDto) { + return this.resellerService.findResellerCustomersInvoices(userId, query) + } + + // ============ Rewards ============= + @ApiOperation({ summary: "get Reseller Agents Rewards==> reseller route" }) + @ResellerRoute() + @Get('rewards') + getResellerAgentsRewards(@UserDec("id") userId: string, @Query() query: SearchRewardsRequestQueryDto) { + return this.resellerService.findResellerAgentsRewards(userId, query) } } diff --git a/src/modules/reseller/reseller.module.ts b/src/modules/reseller/reseller.module.ts index ba6e902..ef041ce 100644 --- a/src/modules/reseller/reseller.module.ts +++ b/src/modules/reseller/reseller.module.ts @@ -5,10 +5,17 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { Reseller } from './entities/reseller.entity'; import { ResellerRepository } from './repositories/reseller.repository'; import { UsersModule } from '../users/users.module'; +import { ReferralsModule } from '../referrals/referrals.module'; +import { InvoicesModule } from '../invoices/invoices.module'; +import { RewardWithdrawRequest } from "../reseller/entities/rewards-withdraw-request.entity"; +import { WithdrawRequestRepository } from './repositories/withdraw-request.repository'; @Module({ - imports: [TypeOrmModule.forFeature([Reseller]), UsersModule], + imports: [TypeOrmModule.forFeature([Reseller, RewardWithdrawRequest]), + UsersModule, + ReferralsModule, + InvoicesModule], controllers: [ResellerController], - providers: [ResellerService, ResellerRepository], + providers: [ResellerService, ResellerRepository, WithdrawRequestRepository], }) export class ResellerModule { } diff --git a/src/modules/reseller/reseller.service.ts b/src/modules/reseller/reseller.service.ts index fd779ef..11821a2 100644 --- a/src/modules/reseller/reseller.service.ts +++ b/src/modules/reseller/reseller.service.ts @@ -3,12 +3,21 @@ import { UsersService } from '../users/providers/users.service'; import { CreateResellerDto } from './dto/create-reseller.dto'; import { ResellerRepository } from './repositories/reseller.repository'; import { AuthMessage } from '../../common/enums/message.enum'; +import { ReferralsService } from '../referrals/providers/referrals.service'; +import { InvoicesService } from '../invoices/providers/invoices.service'; +import { SearchRewardsRequestQueryDto } from './dto/search-reward-requests.dto'; +import { WithdrawRequestRepository } from './repositories/withdraw-request.repository'; +import { SearchCustomersQueryDto } from './dto/search-customers.dto'; +import { SearchResellerInvoicesQueryDto } from './dto/search-reseller-invoices.dto'; @Injectable() export class ResellerService { constructor( private readonly usersService: UsersService, + private readonly referralService: ReferralsService, private readonly resellerRepository: ResellerRepository, + private readonly invoicesService: InvoicesService, + private readonly withdrawRequestRepository: WithdrawRequestRepository, ) { } async create(dto: CreateResellerDto) { @@ -44,6 +53,7 @@ export class ResellerService { return reseller.agents } + async FindOneOrFailByUserId(userId: string) { const reseller = await this.resellerRepository.findOne({ where: { owner: { id: userId } }, @@ -57,10 +67,14 @@ export class ResellerService { return reseller } - async createResellerAgent(userId: string, userTobeAgent: string) { + async createResellerAgent(userId: string, userTobeAgentPhone: string) { const reseller = await this.FindOneOrFailByUserId(userId) - const { user } = await this.usersService.findOneById(userTobeAgent) + const user = await this.usersService.findOneWithPhone(userTobeAgentPhone) + + if (!user) { + throw new NotFoundException(AuthMessage.USER_NOT_FOUND) + } user.reseller = reseller @@ -80,4 +94,37 @@ export class ResellerService { return user } + + async findResellerCustomers(userId: string, query: SearchCustomersQueryDto) { + const reselller = await this.FindOneOrFailByUserId(userId) + + const agentIds = reselller.agents.map(ag => ag.id) + + return this.referralService.findPaginatedReferedUsers(agentIds, query) + } + + async findResellerCustomersInvoices(userId: string, query: SearchResellerInvoicesQueryDto) { + const reselller = await this.FindOneOrFailByUserId(userId) + + const agentIds = reselller.agents.map(ag => ag.id) + + const customerIds = await this.referralService.findReferedUsersIds(agentIds) + + return this.invoicesService.findInvoicesByCustomerIds(customerIds, query) + } + + async findResellerAgentsRewards(userId: string, query: SearchRewardsRequestQueryDto) { + const reselller = await this.FindOneOrFailByUserId(userId) + + const agentIds = reselller.agents.map(ag => ag.id) + + return this.referralService.findRewards(agentIds, query) + } + + async getRewrdsRequests(queryDto: SearchRewardsRequestQueryDto) { + + const [requests, count] = await this.withdrawRequestRepository.findPaginatedList(queryDto); + + return { requests, count, paginate: true }; + } } diff --git a/src/modules/users/providers/users.service.ts b/src/modules/users/providers/users.service.ts index c55d86b..251c387 100755 --- a/src/modules/users/providers/users.service.ts +++ b/src/modules/users/providers/users.service.ts @@ -419,4 +419,8 @@ export class UsersService { async save(user: User) { return this.userRepository.save(user) } + + /**************************** */ + + }