Uecko_ERP/modules/customer-invoices/src/api/domain/services/issue-customer-invoice-domain-service.ts

88 lines
3.0 KiB
TypeScript
Raw Normal View History

2025-11-17 18:12:52 +00:00
import { UniqueID, type UtcDate } from "@repo/rdx-ddd";
2025-11-07 17:51:18 +00:00
import { Maybe, Result } from "@repo/rdx-utils";
2025-11-12 14:48:57 +00:00
2025-11-07 17:51:18 +00:00
import { CustomerInvoice } from "../aggregates";
2025-11-17 18:12:52 +00:00
import { VerifactuRecord } from "../entities";
2025-11-07 17:51:18 +00:00
import { EntityIsNotProformaError, ProformaCannotBeConvertedToInvoiceError } from "../errors";
import {
CustomerInvoiceIsProformaSpecification,
ProformaCanTranstionToIssuedSpecification,
} from "../specs";
2025-11-17 18:12:52 +00:00
import {
type CustomerInvoiceNumber,
CustomerInvoiceStatus,
VerifactuRecordEstado,
} from "../value-objects";
2025-11-07 17:51:18 +00:00
/**
* 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()));
}
2025-11-17 18:12:52 +00:00
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;
2025-11-07 17:51:18 +00:00
/** 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,
2025-11-17 18:12:52 +00:00
verifactu: Maybe.some(verifactuRecord),
2025-11-07 17:51:18 +00:00
});
if (newInvoiceOrError.isFailure) {
return Result.fail(newInvoiceOrError.error);
}
return Result.ok(newInvoiceOrError.data);
}
}