Uecko_ERP/modules/customers/src/api/domain/value-objects/customer-taxes.vo.ts

168 lines
5.1 KiB
TypeScript
Raw Normal View History

2026-03-25 09:34:17 +00:00
import type { TaxCatalogProvider } from "@erp/core";
import { Tax } from "@erp/core/api";
2026-03-16 17:45:45 +00:00
import { ValueObject } from "@repo/rdx-ddd";
2026-03-25 09:34:17 +00:00
import { Maybe, Result } from "@repo/rdx-utils";
2026-03-16 17:45:45 +00:00
export type CustomerTaxesProps = {
iva: Maybe<Tax>; // si existe
rec: Maybe<Tax>; // si existe
retention: Maybe<Tax>; // si existe
};
export interface ICustomerItemTaxes {
iva: Maybe<Tax>; // si existe
rec: Maybe<Tax>; // si existe
retention: Maybe<Tax>; // si existe
toKey(): string; // Clave para representar un trío.
}
2026-03-25 09:34:17 +00:00
export class CustomerTaxes extends ValueObject<CustomerTaxesProps> implements ICustomerItemTaxes {
2026-03-16 17:45:45 +00:00
static create(props: CustomerTaxesProps) {
return Result.ok(new CustomerTaxes(props));
}
2026-03-25 09:34:17 +00:00
/**
* 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<CustomerTaxes> É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<CustomerTaxes, Error> {
// 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<Tax>)
// Si el código es "#", retorna Maybe.none(), si no, busca en el catálogo
const resolveTaxFromCode = (code: string): Result<Maybe<Tax>> => {
const trimmedCode = code.trim();
// Si el código es "#", significa que no existe este tipo de impuesto
if (trimmedCode === "#") {
return Result.ok(Maybe.none<Tax>());
}
// 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"
*/
2026-03-16 17:45:45 +00:00
toKey(): string {
2026-03-25 09:34:17 +00:00
// Extrae el código del IVA, o "#" si no existe
2026-03-16 17:45:45 +00:00
const ivaCode = this.props.iva.match(
(iva) => iva.code,
() => "#"
);
2026-03-25 09:34:17 +00:00
// Extrae el código de la retención de cliente (REC), o "#" si no existe
2026-03-16 17:45:45 +00:00
const recCode = this.props.rec.match(
(rec) => rec.code,
() => "#"
);
2026-03-25 09:34:17 +00:00
// Extrae el código de la retención, o "#" si no existe
2026-03-16 17:45:45 +00:00
const retentionCode = this.props.retention.match(
(retention) => retention.code,
() => "#"
);
2026-03-25 09:34:17 +00:00
// Retorna la clave combinada separada por punto y coma
2026-03-16 17:45:45 +00:00
return `${ivaCode};${recCode};${retentionCode}`;
}
get iva(): Maybe<Tax> {
return this.props.iva;
}
get rec(): Maybe<Tax> {
return this.props.rec;
}
get retention(): Maybe<Tax> {
return this.props.retention;
}
getProps() {
return this.props;
}
toPrimitive() {
return this.getProps();
}
}