Files
descarga_factura_transporte/docs/pipeline.md
T
georgeandClaude Sonnet 5 15e3b5079c se agrega fallback OWTR y calculo de fecha de vencimiento por condicion de pago
- hana_manager.get_delivery ahora busca en ODLN y, si no encuentra, en OWTR
- sapb1_manager resuelve StockTransfers -> Orders (via U_IDC_NUMOV) para
  validar precios de flete cuando la guia es un traslado de inventario
- se corrige el orden de parametros tipo/serie/numero (sin cruces) en
  get_delivery, get_delivery_notes y _get_delivery_entries
- se agrega sapb1_manager.obtener_condicion_pago (BusinessPartners +
  PaymentTermsTypes) y pipeline._calcular_fecha_vencimiento para que
  DocDueDate del draft sea fecha de contabilizacion + dias de la condicion
  de pago del proveedor
- se agrega variante de regex para "VALOR REFER" truncado en sunat_schema.py
- se actualiza documentacion (docs/) reflejando estos cambios

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 13:21:04 -05:00

7.9 KiB

pipeline.py — run_pipeline

Estructura general

pipeline.py
├── CONFIG: RUTA_RED_FACTURACION, RUC_EMPRESA (desde .env)
├── HELPERS: _ruta_carpeta(), _copiar_a_red(data, _log)
├── PASO 1: step_authenticate() → token | None
├── PASO 2: step_fetch_emails() → list[dict]
├── _procesar_un_correo() → None  (core, funciones anidadas)
└── run_pipeline() → None

Proceso de run_pipeline()

flowchart TD
    A[run_pipeline] --> B[init_db]
    B --> C[Calcular rango fechas: ayer-dias → hoy]
    C --> D[step_authenticate]
    D --> E{Token obtenido?}
    E -- No --> F[❌ Abortar]
    E -- Sí --> G[step_fetch_emails]
    G --> H{Hay correos?}
    H -- No --> I[Fin]
    H -- Sí --> J[Bucle: por cada email]
    J --> K[_procesar_un_correo]:::highlight
    K --> J

    classDef highlight fill:#fff3e0,stroke:#e65100,stroke-width:3px;

Proceso de _procesar_un_correo()

flowchart TD
    A[_procesar_un_correo] --> B[1-2. BD: ¿ya procesado?]
    B -- Sí --> C[⏭️ Saltar]
    B -- No --> D[Guardar en BD]
    D --> E[3. _descargar_adjuntos]
    E --> F{Hay adjuntos?}
    F -- No --> C
    F -- Sí --> G[4. _descomprimir ZIPs]
    G --> H[5. _filtrar_y_limpiar]
    H --> I{Hay PDF/XML?}
    I -- No --> C
    I -- Sí --> J[6. _procesar_pdfs]:::highlight
    J --> K[7. _procesar_xmls]:::highlight
    K --> K2[8. _completar_guias]
    K2 --> K3[9. _validar_guias]
    K3 --> L{Hay documentos?}
    L -- No --> C
    L -- Sí --> M[10. _guardar_resultado]
    M --> N[11. Filtrar facturas tipo 01]
    N --> O{Hay facturas?}
    O -- No --> C
    O -- Sí --> P[Bucle: por cada factura]

    P --> Q[12. _validate_prices]:::highlight
    Q --> Q2[12.5 _obtener_valor_referencial_sap]
    Q2 --> R[13. _build_sap_draft]:::highlight
    R --> S[14. _guardar_draft_json]
    S --> T{Todas coinciden?}
    T -- Sí --> U[15. sapb1_manager.create_draft]
    T -- No --> V[⏭️ Draft NO creado]
    U --> W[16. outlook_utilities.send_email]
    V --> W
    W --> P

    classDef highlight fill:#fff3e0,stroke:#e65100,stroke-width:3px;

_procesar_xmls (paso 7) incluye internamente la copia de PDF/XML a la ruta de red (_copiar_a_red, ver más abajo) por cada comprobante procesado.


Funciones internas de _procesar_un_correo

_ruta_carpeta y _copiar_a_red están definidas a nivel de módulo (no anidadas); el resto son funciones anidadas dentro de _procesar_un_correo.

Función Línea Propósito
_ruta_carpeta() (módulo) 33 Resuelve una ruta relativa al directorio del script
_copiar_a_red(data, _log) (módulo) 37 Copia PDF/XML de un comprobante a la ruta de red configurable
_descargar_adjuntos() 169 Descarga adjuntos vía Microsoft Graph
_descomprimir() 198 Descomprime archivos ZIP
_filtrar_y_limpiar() 213 Filtra solo PDF/XML, elimina el resto
_procesar_pdfs() 255 Extrae datos de PDFs, renombra, genera cache
_procesar_xmls(pdf_cache) 310 Procesa XMLs, cruza con PDF, asigna guías, copia a red
_completar_guias(documentos, pdf_cache) 465 Completa guías faltantes en líneas XML desde el PDF cache
_validar_guias(documentos) 524 Advierte (log) guías con datos incompletos (falta tipo/serie/numero)
_guardar_resultado(documentos, datos_pdf) 534 Guarda JSON con datos extraídos (XML y PDF)
_calcular_fecha_vencimiento(fecha_base, condicion_pago) 665 Calcula DocDueDate = fecha de contabilización + días de PaymentTermsTypes
_build_sap_draft(factura, resultados_val) 694 Construye payload del draft SAP B1 (incluye U_VS_ANEXO_PDF/U_VS_ANEXO_XML, DocDueDate por condición de pago)
_validate_prices(factura) 856 Valida precios contra DeliveryNotes/Orders SAP
_build_validation_html(resultados) 1032 Genera tabla HTML de validación de precios
_obtener_valor_referencial_sap(factura) 1062 Consulta Sp_ObtenerValorReferencial en HANA por cada guía única
_build_valor_referencial_html(factura, guias_valor_ref) 1091 Genera tabla HTML comparando valor referencial XML vs SAP
_build_notification_body(status, factura, validation_table, valor_referencial_table, **kwargs) 1127 Arma cuerpo del email de notificación
_guardar_draft_json(draft, serie, numero) 1169 Guarda draft JSON para depuración

_copiar_a_red(data, _log) — Copia a ruta de red

Copia el PDF y el XML de cada comprobante procesado a una ruta de red UNC configurable, en paralelo al guardado local existente (no lo reemplaza). No falla el pipeline si la ruta no está configurada o no está disponible: solo registra una advertencia.

  • Configuración (.env): RUTA_RED_FACTURACION (ruta base UNC) y RUC_EMPRESA (RUC fijo de Aberio).
  • Estructura de destino:
    {RUTA_RED_FACTURACION}\{año}\{mes}\{día}\{tipo}\{RUC_EMPRESA}\{ruc_emisor}\
        {RUC_EMPRESA}-{tipo}-{serie}-{numero}_FormatoImpreso.pdf
        {RUC_EMPRESA}-{tipo}-{serie}-{numero}.xml
    
    RUC_EMPRESA es el RUC de Aberio (fijo); ruc_emisor es el RUC del proveedor que emitió la factura (varía por comprobante). Año/mes/día salen de fecha_emision, sin ceros a la izquierda en mes/día.
  • Guarda las rutas resultantes en data["ruta_red_pdf"] / data["ruta_red_xml"], que luego usa _build_sap_draft para llenar U_VS_ANEXO_PDF / U_VS_ANEXO_XML en el draft SAP B1.
  • Se invoca desde _procesar_xmls, justo después de resolver data["ruta_pdf"].

Flujo de llamadas

run_pipeline
  ├── database_utilities.init_db
  ├── step_authenticate
  │     └── outlook_utilities.get_access_token
  ├── step_fetch_emails
  │     └── outlook_utilities.get_emails
  └── _procesar_un_correo (por cada email)
        ├── database_utilities.get_existing_ids / save_emails
        ├── _descargar_adjuntos
        │     └── outlook_utilities.download_attachments
        ├── _descomprimir
        │     └── email_utilities.descomprimir_adjuntos
        ├── _filtrar_y_limpiar
        ├── _procesar_pdfs
        │     └── pdf_utilities.extraer_datos_pdf / renombrar_pdf
        ├── _procesar_xmls
        │     ├── sunat_schema.extraer_datos_xml
        │     ├── pdf_utilities.extraer_referencias
        │     └── _copiar_a_red
        ├── _completar_guias
        ├── _validar_guias
        ├── _guardar_resultado
        │     └── sunat_schema.split_serie_numero, pdf_utilities.TIPO_MAP
        ├── (por cada factura tipo 01)
        │     ├── _validate_prices
        │     │     ├── sapb1_manager.get_zonas
        │     │     └── sapb1_manager.get_delivery_notes
        │     │           ├── hana_manager.get_delivery (fallback si SL no responde;
        │     │           │     busca en ODLN y, si no hay, en OWTR)
        │     │           └── si el DocEntry vino de OWTR (StockTransfers), resuelve
        │     │                 además la Orden de Venta asociada (Orders vía
        │     │                 U_IDC_NUMOV) para poder validar las líneas FL*
        │     ├── _build_validation_html
        │     ├── _obtener_valor_referencial_sap
        │     │     └── hana_manager.obtener_valor_referencial
        │     ├── _build_valor_referencial_html
        │     ├── _build_sap_draft
        │     │     └── sapb1_manager.obtener_condicion_pago (BusinessPartners +
        │     │           PaymentTermsTypes, para calcular DocDueDate)
        │     ├── _guardar_draft_json
        │     ├── sapb1_manager.create_draft  (si todas las líneas coinciden)
        │     ├── _build_notification_body
        │     └── outlook_utilities.send_email