- Nueva página docs/sapb1_manager.md: sesión, funciones, clasificación de errores (SapNoDisponible frente a rechazo de SAP), búsqueda de facturas existentes y cómo se ubica una guía. - procesar_pdfs.md y procesar_xmls.md usaban los nombres anteriores a la división de pipeline.py (_procesar_pdfs, _ruta_carpeta...) y números de línea que envejecen; ahora apuntan a documentos.py. - inventario.md: árbol de dependencias con los módulos nuevos. - pipeline.md: _procesar_factura, importes_coinciden, hana_disponible y buscar_factura_existente; validar_precios.md, hana_manager.md, main.md y procesar_xmls.md con las notas que faltaban; README sin la cifra de pruebas vieja. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
4.6 KiB
sapb1_manager.py — SAP Business One Service Layer
Cliente del Service Layer de SAP B1: crea los drafts de factura de compra, ubica las guías de remisión para validar precios, busca si una factura ya existe y obtiene datos maestros. Si el Service Layer no encuentra una guía, recurre a HANA (hana_manager.py).
Configuración (.env)
| Variable | Para qué | Por defecto |
|---|---|---|
SAP_B1_URL |
URL del Service Layer (por ejemplo https://servidor:50000/b1s/v1) |
— |
SAP_B1_COMPANY, SAP_B1_USER, SAP_B1_PASSWORD |
Base de datos y credenciales | — |
SAP_B1_TIMEOUT |
Segundos de espera por petición | 15 |
SAP_B1_VERIFY_SSL |
Verificar el certificado (true) |
false |
Sesión
La sesión se abre sola en la primera petición (login) y se guarda en un diccionario del módulo. Ante un HTTP 401 se vuelve a iniciar sesión una vez y se repite la petición. No hay cierre automático: logout() existe pero el demonio no lo llama. Al ser un estado global del módulo, no está pensado para varios hilos.
Funciones
| Función | Qué hace |
|---|---|
forward_to_sapb1(method, endpoint_path, json_data, params, extra_headers) |
Punto único de salida hacia SAP. Registra cada petición y clasifica los errores (ver abajo). |
create_draft(payload) |
POST /Drafts. Devuelve el draft creado (con DocEntry). |
buscar_factura_existente(ruc_emisor, tipo, serie, numero) |
¿La factura ya está en SAP? Ver más abajo. |
get_delivery_notes(tipo, serie, numero) |
Documento completo de una guía, con las líneas FL* de las que se validan los precios. |
obtener_condicion_pago(card_code) |
Condición de pago del proveedor (para el vencimiento). |
get_zonas() |
Maestro U_IDC_ZONAS (activas), para resolver el nombre de la zona de destino. |
get_purchase_invoice(doc_entry) |
GET /PurchaseInvoices(DocEntry). |
login() / logout() |
Inicio y cierre de sesión. |
Errores: caída o rechazo
Todo error del Service Layer se lanza como ConnectionError, pero se distinguen dos casos:
| Excepción | Cuándo | Qué hace el pipeline |
|---|---|---|
SapNoDisponible (subclase de ConnectionError) |
Timeout, sin conexión, HTTP 5xx, o login imposible por esos motivos | Es una caída: aviso "En cola" al proveedor y reintento del correo |
ConnectionError |
HTTP 4xx (SAP rechazó la petición), credenciales inválidas | Es un error nuestro: se avisa solo al equipo |
Así, una caída nunca se confunde con "la guía no existe" ni se le comunica al proveedor como rechazo. Detalle del flujo en respuesta_proveedor.md.
¿La factura ya existe en SAP?
buscar_factura_existente busca por proveedor (CardCode = 'PL' + RUC), tipo, serie y número (U_BPP_MDTD, U_BPP_MDSD, U_BPP_MDCD, los mismos campos que llena el draft) en dos entidades, en este orden:
PurchaseInvoices, solo las no anuladas (Cancelled eq 'tNO').Draftsde factura de compra (DocObjectCode eq 'oPurchaseInvoices'): una factura cuyo draft ya se creó aunque nadie lo haya contabilizado.
Devuelve {"entidad", "DocEntry", "DocNum"} del primer documento hallado, o None. Los valores se escapan para el filtro OData (las comillas simples se duplican). Si SAP no responde lanza SapNoDisponible: no se puede afirmar que la factura no exista.
Cómo se ubica una guía (get_delivery_notes)
- Service Layer:
GET /DeliveryNotesconCancelled eq 'tNO', el tipo, la serie ycontains(U_BPP_MDCD, número)(coincidencia parcial del número). - HANA, si Service Layer no devolvió nada o falló: consulta
ODLNy, si no hay resultados,OWTR. - Por cada documento hallado se hace
GETa su entidad:DeliveryNotesya trae las líneasFL*; enStockTransfers(traslados,OWTR) se leeU_IDC_NUMOVy se obtiene la orden de venta (Orders) asociada, que es la que lleva precios.
Si Service Layer cayó y HANA no aporta nada, se lanza SapNoDisponible en vez de devolver una lista vacía; igual si la caída ocurre al leer el documento. Una lista vacía significa solo "la guía no existe".
Notas
obtener_condicion_pagodevuelveNoneante cualquier error (incluida una caída): en ese caso el vencimiento del draft queda igual a la fecha de contabilización.get_zonasno captura errores: si Service Layer no responde, lanza la excepción (yvalidate_pricesla deja pasar comoSapNoDisponible).SAP_B1_VERIFY_SSLesfalsepor defecto; en producción conviene activarlo con el certificado de la CA.