107 lines
3.3 KiB
TypeScript
107 lines
3.3 KiB
TypeScript
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);
|
|
}
|
|
});
|
|
}
|
|
}
|