Files
descarga_cpe_sovos/hana_manager.py
T
George 277d07dfeb Migra el servicio a arquitectura Docker persistente y mueve credenciales HANA a .env
- Reemplaza config_loader/config_model/hana_client/logger_utilities/outlook_utilities
  por core.py (pipeline funcional), hana_manager.py, logger_manager.py, scheduler.py y
  descargador_sovos.py (Playwright/Firefox) según la plantilla de servicios.
- main.py pasa a loop continuo dirigido por scheduler.json, con manejo de SIGINT/SIGTERM.
- Agrega Dockerfile, docker-compose.yml (restart unless-stopped) y certs/ para despliegue.
- Agrega smb_transfer.py para la transferencia a carpeta compartida.
- Saca las credenciales de HANA de config.json y las mueve a .env (HANA_HOST/PORT/USER/PASSWORD).
2026-08-25 09:29:35 -05:00

163 lines
5.5 KiB
Python

import os
import json
from pathlib import Path
from dotenv import load_dotenv
from logger_manager import get_logger
# ============================================================================
load_dotenv()
logger = get_logger("hana")
CONFIG_FILE = Path(__file__).parent / "config.json"
_conn = None
_cfg_hana = None
# ============================================================================
def _cargar_config():
global _cfg_hana
if _cfg_hana is None:
_cfg_hana = {
"host": os.getenv("HANA_HOST"),
"port": int(os.getenv("HANA_PORT", "30015")),
"user": os.getenv("HANA_USER"),
"password": os.getenv("HANA_PASSWORD"),
}
return _cfg_hana
def obtener_config_hana() -> dict:
return _cargar_config()
# ============================================================================
def get_connection():
global _conn
if _conn is None:
try:
cfg = _cargar_config()
from hdbcli import dbapi
_conn = dbapi.connect(
address=cfg["host"],
port=cfg["port"],
user=cfg["user"],
password=cfg["password"]
)
except Exception as e:
logger.error(f" ❌ Error conectando a HANA: {e}")
return None
return _conn
# ============================================================================
def _nombres_rucs(rucs) -> str:
if not rucs:
return ""
return ",".join(f"'{r}'" for r in rucs)
# ============================================================================
def obtener_pendientes(schema: str, rucs: list[str], days: int = 240) -> list[dict]:
conn = get_connection()
if not conn:
return []
filtro_rucs = _nombres_rucs(rucs)
cond_rucs = f"AND T0.\"LicTradNum\" IN ({filtro_rucs})" if filtro_rucs else ""
try:
query = f"""
SELECT T0."DocNum", T0."DocEntry", TO_VARCHAR(T0."DocDate",'YYYY-MM-DD') "DocDate"
, TO_VARCHAR(T0."TaxDate",'YYYY-MM-DD') "TaxDate", T0."CardName", T0."FolioPref", T0."FolioNum", T0."Indicator", T0."DocCur"
, CASE T0."DocCur" WHEN 'SOL' THEN T0."DocTotal" ELSE T0."DocTotalFC" END "DocTotal"
, T0."U_PGX_REPIMP" "urlPDF"
, T0."U_PGX_PDF" "tienePDF"
, T0."U_PGX_REPIMPXML" "urlXML"
, T0."U_PGX_XML" "tieneXML"
, T0."LicTradNum"
FROM "{schema}".OPCH T0
WHERE T0."CANCELED" = 'N'
{cond_rucs}
AND T0."Indicator" IN ('01', '02','08')
AND (IFNULL(T0."U_PGX_PDF",'N') = 'N' OR IFNULL(T0."U_PGX_XML",'N') = 'N')
AND T0."CreateDate" >= ADD_DAYS(CURRENT_DATE, -{days});
"""
cursor = conn.cursor()
cursor.execute(query)
columns = [desc[0] for desc in cursor.description]
rows = cursor.fetchall()
cursor.close()
result = [dict(zip(columns, row)) for row in rows]
for d in result:
d["schema"] = schema
return result
except Exception as e:
logger.error(f" ❌ Error en query HANA: {e}")
return []
# ============================================================================
def marcar_pdf_descargado(schema: str, docentry: int, url: str, ruta: str):
conn = get_connection()
if not conn:
return False
try:
cursor = conn.cursor()
cursor.execute(f"""
UPDATE "{schema}".OPCH
SET "U_PGX_PDF" = 'Y'
WHERE "DocEntry" = ?
""", (docentry,))
conn.commit()
cursor.close()
return True
except Exception as e:
logger.error(f" ❌ Error actualizando PDF HANA: {e}")
return False
# ============================================================================
def marcar_xml_descargado(schema: str, docentry: int, url: str, ruta: str):
conn = get_connection()
if not conn:
return False
try:
cursor = conn.cursor()
cursor.execute(f"""
UPDATE "{schema}".OPCH
SET "U_PGX_XML" = 'Y'
WHERE "DocEntry" = ?
""", (docentry,))
conn.commit()
cursor.close()
return True
except Exception as e:
logger.error(f" ❌ Error actualizando XML HANA: {e}")
return False
# ============================================================================
def cerrar_conexion():
global _conn
if _conn:
try:
_conn.close()
except:
pass
_conn = None
# ============================================================================
if __name__ == "__main__":
logger.info("=" * 60)
logger.info("📋 HANA MANAGER - Prueba")
logger.info("=" * 60)
with open(CONFIG_FILE, encoding="utf-8") as f:
cfg = json.load(f)
for emp in cfg["empresas"]:
logger.info(f"\n🏢 Empresa: {emp['nombre']} (schema={emp['schema']})")
docs = obtener_pendientes(emp["schema"], cfg.get("rucs", []), cfg.get("consulta", {}).get("dias", 240))
logger.info(f" 📄 Documentos pendientes: {len(docs)}")
for d in docs[:5]:
logger.info(f" DocEntry={d['DocEntry']} DocNum={d['DocNum']} "
f"DocDate={d['DocDate']} Indicator={d['Indicator']} "
f"Folio={d['FolioPref']}-{d['FolioNum']} "
f"PDF={d['tienePDF']} XML={d['tieneXML']}")
if len(docs) > 5:
logger.info(f" ... y {len(docs) - 5} más")
# ============================================================================