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>
This commit is contained in:
georgeandClaude Sonnet 5 committed 2026-09-10 13:21:04 -05:00
1 parent 6b0ed6f519
commit 15e3b5079c
231 files changed
+21421 -475

No files matched your search

+40 -21
View File
@@ -10,8 +10,9 @@ from dotenv import load_dotenv
# CONFIGURACIÓN
# ============================================================================
# Cargar variables de entorno desde .env
load_dotenv(dotenv_path=".env")
# 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", "")
@@ -39,45 +40,62 @@ def get_connection():
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 serie/documento (U_BPP_MDSD).
serie: Serie del documento (U_BPP_MDTD).
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.
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).
"""
schema = HANA_SCHEMA
query = f'''SELECT "DocEntry", "DocNum", "CardCode", "CardName"
FROM "{schema}"."ODLN"
WHERE "CANCELED" = 'N'
AND "U_BPP_MDSD" = ?
AND "U_BPP_MDTD" = ?
AND "U_BPP_MDCD" LIKE ?'''
pattern = f"%{numero}%"
connection = get_connection()
if not connection:
return []
cursor = connection.cursor()
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():
results.append(dict(zip(columns, row)))
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: {e}")
print(f" ❌ Error en consulta HANA get_delivery ({tabla}): {e}")
return []
finally:
if cursor:
cursor.close()
if connection:
connection.close()
# ============================================================================
def obtener_valor_referencial(serie: str, numero: str) -> float:
@@ -100,8 +118,9 @@ def obtener_valor_referencial(serie: str, numero: str) -> float:
if not connection:
return 0.0
cursor = connection.cursor()
cursor = None
try:
cursor = connection.cursor()
cursor.execute(query, (serie, numero))
columns = [column[0] for column in cursor.description]
row = cursor.fetchone()