Skip to content

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)