import { BadRequestException, Injectable, Logger } from '@nestjs/common'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { Transactional } from 'typeorm-transactional'; import { Roles } from '~/auth/enums'; import { NOTIFICATION_EVENTS } from '~/common/modules/notification/constants/event-names.constant'; import { IMoneyRequestApprovedEvent, IMoneyRequestCreatedEvent, IMoneyRequestDeclinedEvent, } from '~/common/modules/notification/interfaces/notification-events.interface'; import { OciService } from '~/document/services'; import { Junior } from '~/junior/entities/junior.entity'; import { JuniorService } from '~/junior/services'; import { CreateMoneyRequestDto, MoneyRequestsFiltersRequestDto, RejectionMoneyRequestDto } from '../dtos/request'; import { MoneyRequest } from '../entities/money-request.entity'; import { MoneyRequestStatus } from '../enums'; import { MoneyRequestsRepository } from '../repositories'; @Injectable() export class MoneyRequestsService { private readonly logger = new Logger(MoneyRequestsService.name); constructor( private readonly moneyRequestsRepository: MoneyRequestsRepository, private readonly juniorService: JuniorService, private readonly ociService: OciService, private readonly eventEmitter: EventEmitter2, ) {} async createMoneyRequest(juniorId: string, body: CreateMoneyRequestDto) { const junior = await this.juniorService.findJuniorById(juniorId); const moneyRequest = await this.moneyRequestsRepository.createMoneyRequest(junior.id, junior.guardianId, body); const moneyRequestWithRelations = await this.moneyRequestsRepository.findByIdWithAllRelations(moneyRequest.id); const event: IMoneyRequestCreatedEvent = { moneyRequest: moneyRequestWithRelations!, timestamp: new Date(), }; this.eventEmitter.emit(NOTIFICATION_EVENTS.MONEY_REQUEST_CREATED, event); return this.findById(moneyRequest.id); } async findById(id: string, userId?: string, role?: Roles): Promise { const moneyRequest = await this.moneyRequestsRepository.findById(id, userId, role); if (!moneyRequest) { throw new BadRequestException('MONEY_REQUEST.NOT_FOUND'); } await this.prepareJuniorImages([moneyRequest.junior]); return moneyRequest; } async findMoneyRequests( userId: string, role: Roles, filters: MoneyRequestsFiltersRequestDto, ): Promise<[MoneyRequest[], number]> { const [moneyRequests, count] = await this.moneyRequestsRepository.findAll(userId, role, filters); const juniors = moneyRequests.map((moneyRequest) => moneyRequest.junior); await this.prepareJuniorImages(juniors); return [moneyRequests, count]; } @Transactional() async approveMoneyRequest(id: string, guardianId: string): Promise { const moneyRequest = await this.moneyRequestsRepository.findById(id, guardianId, Roles.GUARDIAN); if (!moneyRequest) { throw new BadRequestException('MONEY_REQUEST.NOT_FOUND'); } if (moneyRequest.status == MoneyRequestStatus.APPROVED) { throw new BadRequestException('MONEY_REQUEST.ALREADY_APPROVED'); } await Promise.all([ this.moneyRequestsRepository.approveMoneyRequest(id), this.juniorService.transferToJunior( moneyRequest.juniorId, { amount: moneyRequest.amount }, moneyRequest.guardianId, ), ]); const updatedMoneyRequest = await this.moneyRequestsRepository.findByIdWithAllRelations(id, guardianId, Roles.GUARDIAN); const event: IMoneyRequestApprovedEvent = { moneyRequest: updatedMoneyRequest!, timestamp: new Date(), }; this.eventEmitter.emit(NOTIFICATION_EVENTS.MONEY_REQUEST_APPROVED, event); } async rejectMoneyRequest( id: string, guardianId: string, rejectionReasondto: RejectionMoneyRequestDto, ): Promise { const moneyRequest = await this.moneyRequestsRepository.findById(id, guardianId, Roles.GUARDIAN); if (!moneyRequest) { throw new BadRequestException('MONEY_REQUEST.NOT_FOUND'); } if (moneyRequest.status == MoneyRequestStatus.APPROVED) { throw new BadRequestException('MONEY_REQUEST.ALREADY_APPROVED'); } if (moneyRequest.status == MoneyRequestStatus.REJECTED) { throw new BadRequestException('MONEY_REQUEST.ALREADY_REJECTED'); } await this.moneyRequestsRepository.rejectMoneyRequest(id, rejectionReasondto?.rejectionReason); const updatedMoneyRequest = await this.moneyRequestsRepository.findByIdWithAllRelations(id, guardianId, Roles.GUARDIAN); const event: IMoneyRequestDeclinedEvent = { moneyRequest: updatedMoneyRequest!, rejectionReason: rejectionReasondto?.rejectionReason, timestamp: new Date(), }; this.eventEmitter.emit(NOTIFICATION_EVENTS.MONEY_REQUEST_DECLINED, event); } private async prepareJuniorImages(juniors: Junior[]) { this.logger.log(`Preparing junior images`); await Promise.all( juniors.map(async (junior) => { const profilePicture = junior.customer.user.profilePicture; if (profilePicture) { profilePicture.url = await this.ociService.generatePreSignedUrl(profilePicture); } }), ); } }