import { UniqueID, type UtcDate } from "@repo/rdx-ddd"; import { Maybe, Result } from "@repo/rdx-utils"; import { CustomerInvoice } from "../aggregates"; import { VerifactuRecord } from "../entities"; import { EntityIsNotProformaError, ProformaCannotBeConvertedToInvoiceError } from "../errors"; import { CustomerInvoiceIsProformaSpecification, ProformaCanTranstionToIssuedSpecification, } from "../specs"; import { type CustomerInvoiceNumber, CustomerInvoiceStatus, VerifactuRecordEstado, } from "../value-objects"; /** * Servicio de dominio que encapsula la lógica de emisión de factura definitiva desde una proforma. */ export class IssueCustomerInvoiceDomainService { private readonly isProformaSpec = new CustomerInvoiceIsProformaSpecification(); private readonly isApprovedSpec = new ProformaCanTranstionToIssuedSpecification(); /** * Convierte una proforma en factura definitiva. * * @param proforma - Entidad CustomerInvoice en estado proforma aprobada. * @param params.issueNumber - Número de la nueva factura. * @param params.issueDate - Fecha de emisión. * @returns Result - Nueva factura emitida o error de dominio. */ public async issueFromProforma( proforma: CustomerInvoice, params: { issueNumber: CustomerInvoiceNumber; issueDate: UtcDate; } ): Promise> { const { issueDate, issueNumber } = params; /** 1. Validar que la entidad origen es una proforma */ if (!(await this.isProformaSpec.isSatisfiedBy(proforma))) { return Result.fail(new EntityIsNotProformaError(proforma.id.toString())); } /** 2. Validar que la proforma puede emitirse */ if (!(await this.isApprovedSpec.isSatisfiedBy(proforma))) { return Result.fail(new ProformaCannotBeConvertedToInvoiceError(proforma.id.toString())); } const verifactuRecordOrError = VerifactuRecord.create( { estado: VerifactuRecordEstado.createPendiente(), qrCode: Maybe.none(), url: Maybe.none(), uuid: Maybe.none(), operacion: Maybe.none(), }, UniqueID.generateNewID() ); if (verifactuRecordOrError.isFailure) { return Result.fail(new ProformaCannotBeConvertedToInvoiceError(proforma.id.toString())); } const verifactuRecord = verifactuRecordOrError.data; /** 3. Generar la nueva factura definitiva (inmutable) */ const proformaProps = proforma.getProps(); const newInvoiceOrError = CustomerInvoice.create({ ...proformaProps, isProforma: false, proformaId: Maybe.some(proforma.id), status: CustomerInvoiceStatus.createIssued(), invoiceNumber: issueNumber, invoiceDate: issueDate, description: proformaProps.description.isNone() ? Maybe.some(".") : proformaProps.description, verifactu: Maybe.some(verifactuRecord), }); if (newInvoiceOrError.isFailure) { return Result.fail(newInvoiceOrError.error); } return Result.ok(newInvoiceOrError.data); } }