Existencia a una fecha y movimientos en un periodo

Modelo reusable que calcula la existencia de un producto a una fecha ancla pasada sin depender de un export manual de MPRO cada vez, más los reportes de movimientos que se construyeron encima. Vive en exploracion/existencia_a_una_fecha/ del repo trivasa-bi-dev; surgió como necesidad de Trazabilidad Consumo Interno → Compras (su FIFO “on the fly” necesita una semilla de existencia confiable) pero se separó a propósito porque es reusable para cualquier caso futuro que necesite existencia a una fecha.

Tres métodos comparados, uno elegido

Se compararon contra el ground truth real de MPRO (export “Existencias a una fecha”, columna CANTIDAD CTRL-1):

  • Método A (Existencia.Ex_Cantidad_Control_1 actual − suma de Movimiento posteriores a la fecha): 99.3% de match sobre 5,845 productos. Robusto porque parte de Existencia, mantenida en vivo e íntegra por el sistema transaccional, y solo resta el neto de movimientos posteriores — no depende de re-derivar el emparejamiento completo de anulaciones de toda la historia.
  • Método B (réplica literal del .asp de MPRO que genera ese mismo reporte, sumando desde el origen con filtro Es_Cve_Estado='AC' AND Tm_Anulacion='NO'): 98.9%. Resuelve parte de los fallos de A, pero introduce 33 fallos nuevos en productos de alto volumen (BLOCK/VIGA/BOVEDILLA) — la causa raíz es que asume que cada cancelación empata 1:1 con su reverso, y cuando no empatan exacto (drift por correcciones históricas) el resultado se desvía.
  • Método C (variante de B: suma desde el origen, pero sin el filtro de estado/anulación — se obtuvo la consulta SQL exacta que usa MPRO cuando se activa “considera cancelaciones/anulaciones”): 100.00% de match en las 3 fechas ancla probadas (2025-01-01, 2026-01-01, 2025-03-31). Al no excluir nada por estado, nunca depende de que una cancelación y su reverso empaten 1:1 — es el método vigente que usan los reportes construidos después de este hallazgo (movimientos_periodo, FIFO “on the fly”), aunque formalmente sigue abierto validarlo contra más fechas antes de reemplazar a A como “oficial” en el sentido estricto del ADR original.

Detalle completo, incluida la causa raíz exacta del fallo de B, en docs/decisions/2026-07-existencia-a-fecha-metodo-resta-vs-suma.md (raíz del repo).

Movimientos en un periodo

Reporte que lista los movimientos “efectivos” de inventario en un rango de fechas, reconciliado 100% contra existencia inicial/final (Método C). Clave: se excluyen del listado los tipos de movimiento que son puro traspaso/transferencia interna (sucursal-a-sucursal, entre almacenes, a producción) — se verificó que netean exactamente a 0 por producto en el periodo, no son negocio, son reacomodos. Las cancelaciones/anulaciones se excluyen por default y solo se reactivan automáticamente para los productos donde la reconciliación no cuadre sin ellas — evita repetir el mismo error que rompía al Método B (excluir cancelación+reverso a ciegas asumiendo que siempre empatan 1:1).

Bug encontrado en el camino: comparar movimientos contra existencia requiere usar Mv_Cantidad_Control_1 en ambos lados — mezclar con Mv_Cantidad_1 (que difiere en la inmensa mayoría de los casos, pero no en todos) rompe la reconciliación para productos específicos con un factor de conversión de unidad distinto.

V2 — movimientos con UUID de compra vía FIFO

Extensión que, para cada salida del reporte de movimientos, encuentra el UUID del CFDI de compra que la respalda — reusa íntegra la lógica FIFO “on the fly” de Trazabilidad Consumo Interno (fifo_lib.py) en vez de reescribirla. Cobertura sobre enero 2026: 100% de las salidas asignadas sin descuadre (algunas se parten en 2+ filas al cruzar más de una capa de compra); del volumen total, ~52% tiene respaldo de compra real con UUID resuelto — el resto es dominado por venta de producto fabricado internamente (BLOCK/BOVEDILLA/VIGA), consistente con que Trivasa produce además de revender.

Nota técnica para quien reuse fifo_lib.py: la función que arma la cola FIFO no ordena el DataFrame que recibe por fecha de compra — asume que el caller ya lo entrega ordenado. No se modificó la librería (la usan reportes ya publicados); el caller nuevo ordena explícitamente antes de invocarla.

Véase también