Files
descarga_factura_transporte/hana_manager.py
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

141 lines
5.0 KiB
Python

#!/usr/bin/env python3
import os
import sys
import json
from datetime import datetime, date
from hdbcli import dbapi
from dotenv import load_dotenv
# ============================================================================
# CONFIGURACIÓN
# ============================================================================
# Cargar variables de entorno desde .env (ruta absoluta: no depende del
# directorio de trabajo desde el que se lance el proceso)
load_dotenv(dotenv_path=os.path.join(os.path.dirname(__file__), ".env"))
# Configuración de la API
HANA_HOST = os.getenv("HANA_SERVER", "")
HANA_PORT = int(os.getenv("HANA_PORT", 30015))
HANA_USER = os.getenv("HANA_USER", "")
HANA_PASSWORD = os.getenv("HANA_PASSWORD", "")
HANA_SCHEMA = os.getenv("HANA_SCHEMA", "")
# ============================================================================
def get_connection():
"""Crea y retorna una conexión a SAP HANA."""
try:
conn = dbapi.connect(
address=HANA_HOST,
port=HANA_PORT,
user=HANA_USER,
password=HANA_PASSWORD
)
return conn
except Exception as e:
print(f" ❌ Error conectando a HANA: {e}")
return None
# ============================================================================
def get_delivery(tipo: str, serie: str, numero: str) -> list:
"""
Consulta la tabla ODLN en SAP HANA para obtener DeliveryNotes.
Si no encuentra el tipo, serie y número en ODLN, busca en OWTR
(Traslados de Inventario) como fallback.
Args:
tipo: Código de tipo de documento SUNAT, ej. "09"/"31" (U_BPP_MDTD).
serie: Serie del documento, ej. "T001" (U_BPP_MDSD).
numero: Número del documento (se busca con LIKE).
Returns:
list[dict] — Lista con DocEntry, DocNum, CardCode, CardName, Tabla.
"Tabla" indica en qué tabla se encontró el registro ("ODLN" u
"OWTR"), para que el llamador sepa qué endpoint de Service Layer
usar (DeliveryNotes o StockTransfers).
"""
connection = get_connection()
if not connection:
return []
try:
results = _query_delivery_table(connection, "ODLN", tipo, serie, numero)
if results:
return results
return _query_delivery_table(connection, "OWTR", tipo, serie, numero)
finally:
connection.close()
# ============================================================================
def _query_delivery_table(connection, tabla: str, tipo: str, serie: str, numero: str) -> list:
"""Ejecuta la consulta de tipo/serie/número contra la tabla indicada (ODLN u OWTR)."""
schema = HANA_SCHEMA
query = f'''SELECT "DocEntry", "DocNum", "CardCode", "CardName"
FROM "{schema}"."{tabla}"
WHERE "CANCELED" = 'N'
AND "U_BPP_MDTD" = ?
AND "U_BPP_MDSD" = ?
AND "U_BPP_MDCD" LIKE ?'''
pattern = f"%{numero}%"
cursor = None
try:
cursor = connection.cursor()
cursor.execute(query, (tipo, serie, pattern))
columns = [column[0] for column in cursor.description]
results = []
for row in cursor.fetchall():
registro = dict(zip(columns, row))
registro["Tabla"] = tabla
results.append(registro)
return results
except Exception as e:
print(f" ❌ Error en consulta HANA get_delivery ({tabla}): {e}")
return []
finally:
if cursor:
cursor.close()
# ============================================================================
def obtener_valor_referencial(serie: str, numero: str) -> float:
"""
Ejecuta el stored procedure Sp_ObtenerValorReferencial en SAP HANA para
obtener el valor referencial de una guía de remisión (tipo 09).
Args:
serie: Serie de la guía de remisión (ej. "T001").
numero: Número de la guía de remisión (ej. "132800").
Returns:
float — Columna ``ValorReferencial`` del primer registro devuelto
por el SP, o 0.0 si no hay resultados o si ocurre un error.
"""
schema = HANA_SCHEMA
query = f'CALL "{schema}"."Sp_ObtenerValorReferencial"(?, ?)'
connection = get_connection()
if not connection:
return 0.0
cursor = None
try:
cursor = connection.cursor()
cursor.execute(query, (serie, numero))
columns = [column[0] for column in cursor.description]
row = cursor.fetchone()
if not row:
return 0.0
valor = dict(zip(columns, row)).get("ValorReferencial")
return float(valor) if valor is not None else 0.0
except Exception as e:
print(f" ❌ Error en consulta HANA obtener_valor_referencial: {e}")
return 0.0
finally:
if cursor:
cursor.close()
if connection:
connection.close()
# ============================================================================