import type { Criteria } from "@repo/rdx-criteria/server"; import type { UniqueID } from "@repo/rdx-ddd"; import { type Collection, Maybe, Result } from "@repo/rdx-utils"; import type { Transaction } from "sequelize"; import { CustomerInvoiceIsProformaSpecification, type CustomerInvoiceNumber, type CustomerInvoiceSerie, type CustomerInvoiceStatus, type ICustomerInvoiceNumberGenerator, ProformaCannotBeDeletedError, StatusInvoiceIsDraftSpecification, } from "../../domain"; import { CustomerInvoice, type CustomerInvoicePatchProps, type CustomerInvoiceProps, } from "../../domain/aggregates"; import type { ICustomerInvoiceRepository } from "../../domain/repositories"; import type { CustomerInvoiceListDTO } from "../../infrastructure"; export class CustomerInvoiceApplicationService { constructor( private readonly repository: ICustomerInvoiceRepository, private readonly numberGenerator: ICustomerInvoiceNumberGenerator ) {} /** * Devuelve el siguiente nº para proformas * * @param companyId - Identificador de la empresa a la que pertenece la proforma. * @param transaction - Transacción activa para la operación. * @returns Result - El agregado construido o un error si falla la creación. */ async getNextProformaNumber( companyId: UniqueID, transaction: Transaction ): Promise> { return await this.numberGenerator.nextForCompany(companyId, Maybe.none(), transaction); } /** * Devuelve el siguiente nº para facturas (issue) * * @param companyId - Identificador de la empresa a la que pertenece la factura. * @param series - Serie por la que buscar la última factura * @param transaction - Transacción activa para la operación. * @returns Result - El agregado construido o un error si falla la creación. */ async getNextIssueInvoiceNumber( companyId: UniqueID, series: Maybe, transaction: Transaction ): Promise> { return await this.numberGenerator.nextForCompany(companyId, series, transaction); } /** * Construye una proforma a partir de props validadas. * * @param companyId - Identificador de la empresa a la que pertenece la proforma. * @param props - Las propiedades ya validadas para crear la proforma. * @param proformaId - Identificador UUID de la proforma (opcional). * @returns Result - El agregado construido o un error si falla la creación. */ buildProformaInCompany( companyId: UniqueID, props: Omit, proformaId?: UniqueID ): Result { return CustomerInvoice.create({ ...props, companyId }, proformaId); } /** * Guarda una nueva factura y devuelve la factura guardada. * * @param companyId - Identificador de la empresa a la que pertenece la factura. * @param proforma - La factura a guardar. * @param transaction - Transacción activa para la operación. * @returns Result - La factura guardada o un error si falla la operación. */ async createIssueInvoiceInCompany( companyId: UniqueID, invoice: CustomerInvoice, transaction: Transaction ): Promise> { const result = await this.repository.create(invoice, transaction); if (result.isFailure) { return Result.fail(result.error); } return this.getIssueInvoiceByIdInCompany(companyId, invoice.id, transaction); } /** * Guarda una nueva proforma y devuelve la proforma guardada. * * @param companyId - Identificador de la empresa a la que pertenece la proforma. * @param proforma - La proforma a guardar. * @param transaction - Transacción activa para la operación. * @returns Result - La proforma guardada o un error si falla la operación. */ async createProformaInCompany( companyId: UniqueID, proforma: CustomerInvoice, transaction: Transaction ): Promise> { const result = await this.repository.create(proforma, transaction); if (result.isFailure) { return Result.fail(result.error); } return this.getProformaByIdInCompany(companyId, proforma.id, transaction); } /** * Actualiza una proforma existente y devuelve la proforma actualizada. * * @param companyId - Identificador de la empresa a la que pertenece la factura. * @param proforma - La proforma a guardar. * @param transaction - Transacción activa para la operación. * @returns Result - La proforma guardada o un error si falla la operación. */ async updateProformaInCompany( companyId: UniqueID, proforma: CustomerInvoice, transaction: Transaction ): Promise> { const result = await this.repository.update(proforma, transaction); if (result.isFailure) { return Result.fail(result.error); } return this.getProformaByIdInCompany(companyId, proforma.id, transaction); } /** * * Comprueba si existe o no en persistencia una proforma con el ID proporcionado * * @param companyId - Identificador de la empresa a la que pertenece la factura. * @param proformaId - Identificador UUID de la factura. * @param transaction - Transacción activa para la operación. * @returns Result - Existe la factura o no. */ async existsProformaByIdInCompany( companyId: UniqueID, proformaId: UniqueID, transaction?: Transaction ): Promise> { return this.repository.existsByIdInCompany(companyId, proformaId, transaction, { is_proforma: true, }); } /** * * Comprueba si existe o no en persistencia una proforma con el ID proporcionado * * @param companyId - Identificador de la empresa a la que pertenece la factura. * @param invoiceId - Identificador UUID de la factura. * @param transaction - Transacción activa para la operación. * @returns Result - Existe la factura o no. */ async existsIssueInvoiceByIdInCompany( companyId: UniqueID, invoiceId: UniqueID, transaction?: Transaction ): Promise> { return this.repository.existsByIdInCompany(companyId, invoiceId, transaction, { is_proforma: false, }); } /** * Obtiene una colección de proformas que cumplen con los filtros definidos en un objeto Criteria. * * @param companyId - Identificador de la empresa a la que pertenece la factura. * @param criteria - Objeto con condiciones de filtro, paginación y orden. * @param transaction - Transacción activa para la operación. * @returns Result, Error> - Colección de proformas o error. */ async findProformasByCriteriaInCompany( companyId: UniqueID, criteria: Criteria, transaction?: Transaction ): Promise, Error>> { return this.repository.findByCriteriaInCompany(companyId, criteria, transaction, { where: { is_proforma: true, }, }); } /** * Obtiene una colección de facturas que cumplen con los filtros definidos en un objeto Criteria. * * @param companyId - Identificador de la empresa a la que pertenece la factura. * @param criteria - Objeto con condiciones de filtro, paginación y orden. * @param transaction - Transacción activa para la operación. * @returns Result, Error> - Colección de facturas o error. */ async findIssueInvoiceByCriteriaInCompany( companyId: UniqueID, criteria: Criteria, transaction?: Transaction ): Promise, Error>> { return this.repository.findByCriteriaInCompany(companyId, criteria, transaction, { where: { is_proforma: false, }, }); } /** * Recupera una factura por su identificador único. * * @param invoiceId - Identificador UUID de la factura. * @param transaction - Transacción activa para la operación. * @returns Result - Factura encontrada o error. */ async getIssueInvoiceByIdInCompany( companyId: UniqueID, invoiceId: UniqueID, transaction?: Transaction ): Promise> { return await this.repository.getByIdInCompany(companyId, invoiceId, transaction, { where: { is_proforma: false, }, }); } /** * Recupera una proforma por su identificador único. * * @param proformaId - Identificador UUID de la proforma. * @param transaction - Transacción activa para la operación. * @returns Result - proforma encontrada o error. */ async getProformaByIdInCompany( companyId: UniqueID, proformaId: UniqueID, transaction?: Transaction ): Promise> { return await this.repository.getByIdInCompany(companyId, proformaId, transaction, { where: { is_proforma: true, }, }); } /** * Actualiza parcialmente una factura existente con nuevos datos. * No lo guarda en el repositorio. * * @param companyId - Identificador de la empresa a la que pertenece la factura. * @param proformaId - Identificador de la factura a actualizar. * @param changes - Subconjunto de props válidas para aplicar. * @param transaction - Transacción activa para la operación. * @returns Result - Factura actualizada o error. */ async patchProformaByIdInCompany( companyId: UniqueID, proformaId: UniqueID, changes: CustomerInvoicePatchProps, transaction?: Transaction ): Promise> { const proformaResult = await this.getProformaByIdInCompany(companyId, proformaId, transaction); if (proformaResult.isFailure) { return Result.fail(proformaResult.error); } const updated = proformaResult.data.update(changes); if (updated.isFailure) { return Result.fail(updated.error); } return Result.ok(updated.data); } /** * Elimina (o marca como eliminada) una proforma según su ID. * * @param companyId - Identificador de la empresa a la que pertenece la proforma. * @param proformaId - Identificador UUID de la proforma. * @param transaction - Transacción activa para la operación. * @returns Result - Resultado de la operación. */ async deleteProformaByIdInCompany( companyId: UniqueID, proformaId: UniqueID, transaction?: Transaction ): Promise> { // 1) Buscar la proforma const proformaResult = await this.getProformaByIdInCompany(companyId, proformaId, transaction); if (proformaResult.isFailure) return Result.fail(proformaResult.error); const proforma = proformaResult.data; // 2) Validar: es proforma const isProforma = new CustomerInvoiceIsProformaSpecification(); if (!(await isProforma.isSatisfiedBy(proforma))) { return Result.fail(new ProformaCannotBeDeletedError(proformaId.toString(), "not a proforma")); } // 3) Validar: estado draft const isDraft = new StatusInvoiceIsDraftSpecification(); if (!(await isDraft.isSatisfiedBy(proforma))) { return Result.fail( new ProformaCannotBeDeletedError(proformaId.toString(), "status is not 'draft'") ); } // 4) Borrar la proforma (baja lógica) return this.repository.deleteProformaByIdInCompany(companyId, proformaId, transaction); } /** * * Actualiza el "status" de una proforma * * @param companyId - Identificador UUID de la empresa a la que pertenece la proforma. * @param proformaId - UUID de la proforma a actualizar. * @param newStatus - nuevo estado * @param transaction - Transacción activa para la operación. * @returns Result */ async updateProformaStatusByIdInCompany( companyId: UniqueID, proformaId: UniqueID, newStatus: CustomerInvoiceStatus, transaction?: Transaction ): Promise> { return this.repository.updateProformaStatusByIdInCompany( companyId, proformaId, newStatus, transaction ); } }