import { Inject, Injectable, Logger, NotFoundException } from '@nestjs/common'; import { CreateDesignRequestDto } from './dto/create-design-request.dto'; import { UpdateDesignRequestDto } from './dto/update-design-request.dto'; import { FinishDesignRequestDto } from './dto/finish-design-request.dto'; import { MarkDesignRequestPaidDto } from './dto/mark-design-request-paid.dto'; import { ConfigService } from '@nestjs/config'; import { HttpService } from '@nestjs/axios'; import { AxiosError } from 'axios'; import { catchError, firstValueFrom, throwError } from 'rxjs'; import { EntityManager } from '@mikro-orm/postgresql'; import { BusinessService } from '../business/business.service'; import { CreateExternalInvoiceDto } from '../catalogue/dto/create-invoice.dto'; import { DesignRequest } from './entities/design-request.entity'; import { DesignRequestRepository } from './repositories/design-request.repository'; import { FindDesignRequestsDto } from './dto/find-design-requests.dto'; @Injectable() export class DesignRequestService { private readonly logger = new Logger(DesignRequestService.name); constructor( private readonly em: EntityManager, private readonly designRequestRepository: DesignRequestRepository, private readonly businessService: BusinessService, private readonly httpService: HttpService, @Inject(ConfigService) private readonly configService: ConfigService, ) { } private getDesignUnitPrice(count: number): number { if (count < 10) { return +this.configService.getOrThrow('DESIGN_PRICE_UNDER_10'); } if (count <= 30) { return +this.configService.getOrThrow('DESIGN_PRICE_10_TO_30'); } if (count <= 70) { return +this.configService.getOrThrow('DESIGN_PRICE_30_TO_70'); } return +this.configService.getOrThrow('DESIGN_PRICE_ABOVE_70'); } async createDesignRequest(dto: CreateDesignRequestDto, businessId: string) { const { title, count, desc, expectedDate, attachments } = dto; const business = await this.businessService.findByIdOrFail(businessId); const danakApiUrl = this.configService.getOrThrow('DANAK_API_URL'); const xApiKey = this.configService.getOrThrow('EXTERNAL_API_KEYS'); const unitPrice = this.getDesignUnitPrice(count); const params: CreateExternalInvoiceDto = { danakSubscriptionId: business.danakSubscriptionId, businessId, items: [ { name: 'purchase_design', count, unitPrice, discount: 0, }, ], }; let invoiceData: { id: string } = { id: '' }; try { const { data } = await firstValueFrom( this.httpService .post(`${danakApiUrl}/invoices/external/create`, params, { headers: { 'Content-Type': 'application/json', 'X-API-Key': xApiKey, }, }) .pipe( catchError((err: AxiosError) => { this.logger.error(`Failed to create invoice: ${err.message}`, err.stack); return throwError(() => err); }), ), ); invoiceData = data.data.invoice } catch (error: unknown) { this.logger.error( `Error create invoice: ${error instanceof Error ? error.message : 'Unknown error'}`, ); throw error; } const designRequest = this.em.create(DesignRequest, { title, count, desc, expectedDate: expectedDate ? new Date(expectedDate) : undefined, attachments: attachments ?? [], invoiceId: invoiceData.id, business, }); await this.em.persistAndFlush(designRequest); return designRequest; } async finishDesignRequest(dto: FinishDesignRequestDto) { const { businessId, invoiceId } = dto; await this.businessService.findByIdOrFail(businessId); const designRequest = await this.em.findOne(DesignRequest, { invoiceId }); if (!designRequest) { throw new NotFoundException('design request not found'); } designRequest.paidAt = new Date(); await this.em.flush(); return designRequest; } async markAsPaid(id: string, dto: MarkDesignRequestPaidDto = {}) { const designRequest = await this.em.findOne(DesignRequest, { id }); if (!designRequest) { throw new NotFoundException('design request not found'); } designRequest.paidAt = dto.paidAt ? new Date(dto.paidAt) : new Date(); await this.em.flush(); return designRequest; } async findAll(businessId: string, dto: FindDesignRequestsDto) { await this.businessService.findByIdOrFail(businessId); return this.designRequestRepository.findAllPaginated(dto, businessId); } findAllAsSuperAdmin(dto: FindDesignRequestsDto) { return this.designRequestRepository.findAllPaginated(dto); } async findOne(id: string, businessId: string) { await this.businessService.findByIdOrFail(businessId); const designRequest = await this.em.findOne(DesignRequest, { id, business: { id: businessId }, }); if (!designRequest) { throw new NotFoundException('design request not found'); } return designRequest; } }