05_movimientos_periodo.py
Ruta original: docs/proyectos/consumo-interno-fifo/scripts/05_movimientos_periodo.py
"""
CLI: genera el CSV de movimientos efectivos en un periodo + reconciliacion contra
existencia a una fecha (inicial/final, Metodo C). Logica compartida con el reporte
interactivo del hub en movimientos_lib.py -- ver ese modulo y
decisions/2026-07-movimientos-periodo-clasificacion-tipos.md para el detalle completo.
Uso: python3 scripts/05_movimientos_periodo.py 2026-01-01 2026-01-31
"""
import sys
import pandas as pd
from movimientos_lib import TOLERANCIA, extraer_movimientos, reconciliar_y_ajustar, verificar_traspasos_netean_cero
def main(fecha_ini: str, fecha_fin: str):
print(f"Periodo: {fecha_ini} a {fecha_fin}")
check = verificar_traspasos_netean_cero(fecha_ini, fecha_fin)
if len(check):
print(f"AVISO: {len(check)} productos NO netean a 0 en traspaso/transferencia para este periodo.")
print(check.sort_values("neto", key=abs, ascending=False).head(5).to_string(index=False))
else:
print("OK: traspaso/transferencia/traspaso-a-produccion netean a 0 por producto -- exclusion segura.")
print("\nExtrayendo movimientos efectivos...")
mov = extraer_movimientos(fecha_ini, fecha_fin, incluir_anulaciones=False)
print(f" {len(mov)} filas de movimiento")
print("\nDistribucion por tipo:")
print(mov.groupby(["entrada_salida", "categoria_movimiento"]).size().sort_values(ascending=False).to_string())
print("\nReconciliando contra existencia a una fecha (Metodo C, inicial y final)...")
mov, resumen, sin_cuadrar = reconciliar_y_ajustar(fecha_ini, fecha_fin, mov)
print(f" {len(resumen)} productos totales | {len(sin_cuadrar)} NO cuadran (tolerancia {TOLERANCIA})")
out_mov = f"output/movimientos_{fecha_ini}_a_{fecha_fin}.csv"
out_recon = f"output/reconciliacion_{fecha_ini}_a_{fecha_fin}.csv"
mov.to_csv(out_mov, index=False)
resumen.sort_values("diferencia", key=abs, ascending=False).to_csv(out_recon, index=False)
print(f"\nGuardado: {out_mov} ({len(mov)} filas)")
print(f"Guardado: {out_recon} ({len(resumen)} filas)")
if len(sin_cuadrar):
print(f"\n{len(sin_cuadrar)} productos siguen sin cuadrar (tolerancia {TOLERANCIA}). Top 10:")
print(sin_cuadrar.sort_values("diferencia", key=abs, ascending=False)
.head(10)[["Pr_Cve_Producto", "existencia_inicial", "neto_movimientos",
"existencia_calculada", "existencia_final", "diferencia"]]
.to_string(index=False))
else:
print("\nTODOS los productos cuadran dentro de la tolerancia.")
if __name__ == "__main__":
fecha_ini = sys.argv[1] if len(sys.argv) > 1 else "2026-01-01"
fecha_fin = sys.argv[2] if len(sys.argv) > 2 else "2026-01-31"
main(fecha_ini, fecha_fin)