Saltar a contenido

Autor: Iker Acevedo

Fecha creación: 2026-09-16
Estado: producción (Motor Legacy) + preprod (Motor Dinámico)

Liquidaciones — Visión general

Liquidar una guía es calcularle a la tienda cuánto le queda de esa venta después de descontar flete, servicio, seguro, comisión de recaudo, impuesto de gobierno, y —si hay un proveedor dropshipper detrás— su parte también. Es uno de los procesos más sensibles de LogiGho porque mueve dinero real: si el cálculo está mal, alguien cobra de más o de menos, y eso se nota de inmediato.

Hoy conviven dos motores dentro de la misma Lambda, ApiLambdaLiquidacionesLogighoAOT:

  • Motor Legacy — calcula liquidaciones desde siempre. Un único método de ~350 líneas (DepurarLiquidacionUseCase.DepuraLiquidacionInter) con toda la lógica hardcodeada: switches por nombre de trayecto, fallbacks en cascada, reglas de tarifa entremezcladas con aritmética. Funciona y está en producción. Liquida hoy Interrapidísimo, Envía y Servientrega.
  • Motor Dinámico — arquitectura nueva, dominio limpio desde cero (modelos, calculadoras, políticas, validadores — cada uno con su propio test). En Legacy, las reglas de una transportadora viven en código; aquí viven en Mongo: tarifas, trayectos y política de peso son configuración, editable desde el módulo Costos Transportadora del front. Hoy liquida TCC. El diseño permite migrar cualquier transportadora solo con crear su configuración, sin tocar código.

Hoy coexisten. La Lambda decide, pedido por pedido, cuál motor usa cada uno.


Por qué dos motores en vez de reescribir Legacy

Reescribir un proceso que mueve dinero real de una día para otro es arriesgado: si falla, falla para todos a la vez. Se aplicó el patrón Strangler Fig: motor nuevo crece junto al viejo, transportadora por transportadora. El viejo se desactiva solo cuando el nuevo demuestra que calcula igual (o mejor, documentando cada diferencia). Ver ADR-001.

Consecuencia clave: - Legacy nunca se modificó. DepurarLiquidacionUseCase.cs es el archivo original. - Código nuevo vive en carpeta aparte (LiquidacionDinamica/). - Ambos motores escriben en la misma colección Mongo (LiquidacionesLogigho), mismo formato. - Para contabilidad y reportes: no hay diferencia visible entre liquidación Legacy o Motor Dinámico.


Cómo se decide qué motor usa cada pedido

Colección ConfiguracionLiquidacionTransportadora (un documento/transportadora) define qué motor usa. Campo MotorLiquidacion:

Valor Comportamiento
Dinamica Motor Dinámico calcula y persiste resultado.
Sombra Motor Dinámico calcula pero no guarda — valida configuración sin arriesgar dinero real.
Legacy Motor Legacy procesa como siempre (Motor Dinámico ignorado).
Deshabilitado Transportadora no liquida — ambos motores la saltan.
(sin config) Mismo que Legacy — comportamiento seguro por defecto. Sin configuración explícita, transportadora sigue ruta original.

LiquidarPedidosDinamicoUseCase.Particionar() hace la separación: 1. Agrupa configuraciones por NombreEnPedidos (normalizado). 2. Compara campo Transportadora de cada pedido contra ese diccionario. 3. Genera dos lotes: Dinamicos y Legacy. 4. Ambos se procesan en misma ejecución Lambda — no son mutuamente excluyentes.

Detalles en Orquestador.


Diagrama de flujo (estilo BPMN)

Vista de punto a punto: desde que llega la orden de liquidar hasta que el documento queda en Mongo, con las dos "carriles" (pools) — Motor Legacy y Motor Dinámico — corriendo en la misma invocación.

flowchart TB
    Inicio((("Inicio
Pedidos a liquidar"))):::evento --> ObtenerDatos["Obtener de Mongo:
ConfiguracionLiquidacionTransportadora,
TarifasTransportadoraTienda, TrayectosTransportadora"]:::tarea ObtenerDatos --> Particionar{"¿MotorLiquidacion de
la transportadora del pedido?"}:::gateway Particionar -- "Legacy /
sin configuración" --> PoolLegacy Particionar -- "Dinamica o Sombra" --> PoolDinamico Particionar -- "Deshabilitado" --> Descartado["Guía no se procesa"]:::tareaDescartada subgraph PoolLegacy ["MOTOR LEGACY — DepurarLiquidacionUseCase"] direction TB L1["Clasificar Entrega / Devolución
(if/else inline)"]:::tarea --> L2["Buscar tarifa por tienda + transportadora
(fallbacks en cascada)"]:::tarea L2 --> L3["Calcular flete por switch de nombre
de trayecto (Urbano/Nacional/...)"]:::tarea L3 --> L4["Armar Peticion(es):
Entrega / Devolución / Proveedor / Impuesto Gobierno"]:::tarea end subgraph PoolDinamico ["MOTOR DINÁMICO — LiquidarPedidosDinamicoUseCase"] direction TB D1["ClasificadorTipoLiquidacion"]:::tarea --> D2["ValidadorTrayecto
(lista blanca + default)"]:::tarea D2 --> D3["ResolvedorTarifa
(por IdTienda, o Genérica)"]:::tarea D3 --> D4["Calculadoras:
BaseRecaudo → PolíticaPeso → FleteTienda →
Entrega/Devolución → Dropshipper/Marketing/Referido"]:::tarea D4 --> D5["ConstructorPeticion"]:::tarea D2 -. "trayecto inválido
y sin default" .-> DAudit D3 -. "sin tarifa ni genérica" .-> DAudit DAudit["Auditoría:
GuiaNoLiquidada + MotivoNoLiquidacion"]:::tareaDescartada end L4 --> GatewayPersistLegacy{{"Insertar en Mongo"}}:::gateway D5 --> GatewaySombra{"¿Motor = Sombra?"}:::gateway GatewaySombra -- "Sí — no persiste" --> FinSombra["Solo queda el cálculo,
para comparar"]:::tareaDescartada GatewaySombra -- "No (Dinamica)" --> GatewayPersistDinamico{{"Insertar en Mongo"}}:::gateway GatewayPersistLegacy --> Coleccion[("LiquidacionesLogigho
(misma colección, mismo formato)")]:::datastore GatewayPersistDinamico --> Coleccion Coleccion --> Fin((("Fin
Liquidaciones disponibles
para contabilidad/reportes"))):::evento classDef evento fill:#fce4ec,stroke:#ad1457,stroke-width:2px,color:#000 classDef tarea fill:#e3f2fd,stroke:#1565c0,stroke-width:1.5px,color:#000 classDef tareaDescartada fill:#fff3e0,stroke:#ef6c00,stroke-width:1.5px,color:#000 classDef gateway fill:#fff9c4,stroke:#f9a825,stroke-width:2px,color:#000 classDef datastore fill:#ede7f6,stroke:#5e35b1,stroke-width:2px,color:#000

Fuente y demás diagramas de este módulo: carpeta de diagramas.


Mapa de la documentación

Página Qué encontrás ahí
Lambda (contrato técnico) Trigger, request/response, variables de entorno
Motor Legacy Cómo liquida hoy DepurarLiquidacionUseCase, sus reglas y sus puntos frágiles
Motor Dinámico → Visión general Filosofía de diseño, flujo del dominio guía por guía
Motor Dinámico → Orquestador Partición Legacy/Dinámica/Sombra, Function.cs, fases del proyecto
Motor Dinámico → Modelos de dominio Configuración, Pedido, Resultados de cálculo
Motor Dinámico → Calculadoras Cada fórmula, una por una
Motor Dinámico → Políticas de peso Tabla de incrementos (TCC) vs. porcentaje sobre flete (resto)
Motor Dinámico → Validación Clasificador de tipo, resolución de tarifa, validación de trayecto
Motor Dinámico → Infraestructura Mapeo Mongo ↔ dominio, por qué escribe en la misma colección
Despliegue en PreProd (Financiero) Step Function Financiero-PreProd, cómo se desplegó
Operación Cómo correr una liquidación de prueba a mano, guía por guía
Frontend — Costos Transportadora Pantalla donde se configura el motor Dinámico

Historial de cambios

Fecha Autor Cambio
2026-09-16 Iker Acevedo Documentación inicial completa: motor Legacy, motor Dinámico, despliegue en PreProd y frontend de configuración.