import { ITransactionManager } from "@erp/core/api"; import { UniqueID, UtcDate } from "@repo/rdx-ddd"; import { Maybe, Result } from "@repo/rdx-utils"; import { InvalidProformaStatusError } from "../../domain"; import { StatusInvoiceIsApprovedSpecification } from "../../domain/specs"; import { CustomerInvoiceApplicationService } from "../customer-invoice-application.service"; type IssueCustomerInvoiceUseCaseInput = { companyId: UniqueID; proforma_id: string; }; /** * Caso de uso: Conversión de una proforma a factura definitiva. * * - Recupera la proforma * - Valida su estado ("approved") * - Genera la factura definitiva (nueva entidad) * - Marca la proforma como "issued" * - Persiste ambas dentro de la misma transacción */ export class IssueCustomerInvoiceUseCase { constructor( private readonly service: CustomerInvoiceApplicationService, private readonly transactionManager: ITransactionManager ) {} public execute(params: IssueCustomerInvoiceUseCaseInput) { const { proforma_id, companyId } = params; const idOrError = UniqueID.create(proforma_id); if (idOrError.isFailure) { return Result.fail(idOrError.error); } const proformaId = idOrError.data; return this.transactionManager.complete(async (transaction) => { try { /** 1. Recuperamos la proforma */ const proformaResult = await this.service.getInvoiceByIdInCompany( companyId, proformaId, transaction ); if (proformaResult.isFailure) { return Result.fail(proformaResult.error); } const proforma = proformaResult.data; /** 2. Comprobamos que la proforma origen está aprovada para generar la factura */ const isApprovedSpec = new StatusInvoiceIsApprovedSpecification(); if (!(await isApprovedSpec.isSatisfiedBy(proforma))) { return Result.fail(new InvalidProformaStatusError(proformaId.toString())); } /** 3. Generar nueva factura */ const nextNumberResult = await this.service.getNextIssueInvoiceNumber( companyId, proforma.series, transaction ); if (nextNumberResult.isFailure) { return Result.fail(nextNumberResult.error); } const newIssueNumber = nextNumberResult.data; // props base obtenidas del agregado proforma const issuedInvoiceOrError = this.service.buildIssueInvoiceInCompany(companyId, proforma, { invoiceNumber: Maybe.some(newIssueNumber), invoiceDate: UtcDate.today(), }); if (issuedInvoiceOrError.isFailure) { return Result.fail(issuedInvoiceOrError.error); } const issuedInvoice = issuedInvoiceOrError.data; /** 4. Persistencia */ await this.service.createInvoiceInCompany(companyId, issuedInvoice, transaction); // actualizamos la proforma const updatedProformaResult = proforma.asIssued(); if (updatedProformaResult.isFailure) { return Result.fail(updatedProformaResult.error); } await this.service.updateInvoiceInCompany( companyId, updatedProformaResult.data, transaction ); /** 5. Resultado */ return Result.ok(issuedInvoice); } catch (error: unknown) { return Result.fail(error as Error); } }); } }