import type { TaxCatalogProvider } from "@erp/core"; import { Tax } from "@erp/core/api"; import { ValueObject } from "@repo/rdx-ddd"; import { Maybe, Result } from "@repo/rdx-utils"; export type CustomerTaxesProps = { iva: Maybe; // si existe rec: Maybe; // si existe retention: Maybe; // si existe }; export interface ICustomerItemTaxes { iva: Maybe; // si existe rec: Maybe; // si existe retention: Maybe; // si existe toKey(): string; // Clave para representar un trío. } export class CustomerTaxes extends ValueObject implements ICustomerItemTaxes { static create(props: CustomerTaxesProps) { return Result.ok(new CustomerTaxes(props)); } /** * Reconstruye una instancia de CustomerTaxes a partir de una clave serializada. * Este método es la operación inversa de toKey(). * * @param key Clave en formato "ivaCode;recCode;retentionCode" * Donde cada código puede ser "#" si el impuesto no existe * Ejemplo: "iva_21;rec_02;#" o "#;#;#" * @param provider Proveedor del catálogo de impuestos (ej: SpainTaxCatalogProvider) * @returns Result Éxito con la instancia creada o fallo con mensaje de error * * @example * const key = "iva_21;rec_02;ret_1500"; * const result = CustomerTaxes.fromKey(key, SpainTaxCatalogProvider()); * if (result.isOk()) { * const customerTaxes = result.value; * } */ static fromKey(key: string, provider: TaxCatalogProvider): Result { // Validar que la clave no esté vacía if (!key || typeof key !== "string") { return Result.fail(new Error("La clave debe ser una cadena no vacía.")); } // Dividir la clave por punto y coma para obtener los tres códigos const codes = key.split(";"); // Validar que la clave tiene exactamente 3 partes (IVA, REC, Retención) if (codes.length !== 3) { return Result.fail( new Error( `Formato de clave inválido. Se esperaban 3 códigos separados por ";", se recibieron ${codes.length}.` ) ); } const [ivaCode, recCode, retentionCode] = codes; // Función auxiliar para resolver un código a un impuesto (Maybe) // Si el código es "#", retorna Maybe.none(), si no, busca en el catálogo const resolveTaxFromCode = (code: string): Result> => { const trimmedCode = code.trim(); // Si el código es "#", significa que no existe este tipo de impuesto if (trimmedCode === "#") { return Result.ok(Maybe.none()); } // Si el código no es "#", buscamos en el catálogo usando Tax.createFromCode const taxResult = Tax.createFromCode(trimmedCode, provider); // Si hay un error creando el impuesto, propagamos el error if (taxResult.isFailure) { return Result.fail(taxResult.error); } // Si se creó exitosamente, lo envolvemos en Maybe.some() return Result.ok(Maybe.some(taxResult.data)); }; // Resolver el IVA desde su código const ivaResult = resolveTaxFromCode(ivaCode); if (ivaResult.isFailure) { return Result.fail( new Error(`Error al resolver IVA desde código "${ivaCode}": ${ivaResult.error.message}`) ); } // Resolver el REC desde su código const recResult = resolveTaxFromCode(recCode); if (recResult.isFailure) { return Result.fail( new Error(`Error al resolver REC desde código "${recCode}": ${recResult.error.message}`) ); } // Resolver la Retención desde su código const retentionResult = resolveTaxFromCode(retentionCode); if (retentionResult.isFailure) { return Result.fail( new Error( `Error al resolver Retención desde código "${retentionCode}": ${retentionResult.error.message}` ) ); } // Crear la instancia de CustomerTaxes con los impuestos resueltos return Result.ok( new CustomerTaxes({ iva: ivaResult.data, rec: recResult.data, retention: retentionResult.data, }) ); } /** * Genera una clave única que representa la combinación de impuestos. * Extrae el código de cada impuesto o usa "#" si no existe. * @returns {string} Clave en formato: "ivaCode;recCode;retentionCode" */ toKey(): string { // Extrae el código del IVA, o "#" si no existe const ivaCode = this.props.iva.match( (iva) => iva.code, () => "#" ); // Extrae el código de la retención de cliente (REC), o "#" si no existe const recCode = this.props.rec.match( (rec) => rec.code, () => "#" ); // Extrae el código de la retención, o "#" si no existe const retentionCode = this.props.retention.match( (retention) => retention.code, () => "#" ); // Retorna la clave combinada separada por punto y coma return `${ivaCode};${recCode};${retentionCode}`; } get iva(): Maybe { return this.props.iva; } get rec(): Maybe { return this.props.rec; } get retention(): Maybe { return this.props.retention; } getProps() { return this.props; } toPrimitive() { return this.getProps(); } }