Uecko_ERP/modules/customer-invoices/src/api/domain/services/issue-customer-invoice-domain-service.ts
2025-11-17 19:12:52 +01:00

88 lines
3.0 KiB
TypeScript

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<CustomerInvoice, Error> - Nueva factura emitida o error de dominio.
*/
public async issueFromProforma(
proforma: CustomerInvoice,
params: {
issueNumber: CustomerInvoiceNumber;
issueDate: UtcDate;
}
): Promise<Result<CustomerInvoice, Error>> {
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);
}
}