diff --git a/hana_manager.py b/hana_manager.py index 0032eb0..c977d51 100644 --- a/hana_manager.py +++ b/hana_manager.py @@ -38,7 +38,7 @@ def get_connection(): return None # ============================================================================ -def obtener_documentos_creados_actualizados_hace_x_minutos(schema, minutes=60): +def obtener_documentos_creados_actualizados_hace_60_minutos(schema, minutes=60): """ Se conecta a la base de datos SAP HANA, realiza una consulta a la tabla OPCH, y formatea el resultado para que coincida con el esquema de documentos.json. diff --git a/main.py b/main.py index 2179b0e..0709fcf 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,6 @@ import json import time -from datetime import datetime +from datetime import datetime, timedelta from dotenv import load_dotenv import logger_utilities @@ -11,9 +11,35 @@ from constants import ESTADOS_COMPROBANTE from hana_manager import actualizar_documento_sap # ============================================================================ -def es_hora_pico(dt: datetime) -> bool: + +HORARIOS = [ + (6, 30, 13, 15, 10, "rapido"), # 06:30 - 13:15 -> cada 10 segundos + (13, 15, 14, 0, 1800, "full"), # 13:15 - 14:00 -> cada 30 minutos + (14, 0, 17, 30, 10, "rapido"), # 14:00 - 17:30 -> cada 10 segundos + (17, 30, 21, 30, 1800, "full"), # 17:30 - 21:30 -> cada 30 minutos +] + +# ============================================================================ +def obtener_intervalo(dt: datetime) -> tuple[int, str] | None: minutos = dt.hour * 60 + dt.minute - return (360 <= minutos < 795) or (840 <= minutos < 1050) + + for hi, mi, hf, mf, intervalo, modo in HORARIOS: + inicio = hi * 60 + mi + fin = hf * 60 + mf + + if inicio <= minutos < fin: + return (intervalo, modo) + + return None + +# ============================================================================ +def segundos_hasta_proximo_inicio(dt: datetime) -> int: + inicio = dt.replace(hour=6, minute=30, second=0, microsecond=0) + + if dt >= inicio: + inicio += timedelta(days=1) + + return int((inicio - dt).total_seconds()) # ============================================================================ def cargar_empresas(): @@ -98,11 +124,11 @@ def enviar_mensaje_sl(usercode, doc, resultado, schema): # ============================================================================ def procesar_empresa(schema, datos_empresa, modo): print(f"\n{'='*60}") - print(f"🏢 Procesando empresa: {schema}") + print(f"🏢 Procesando empresa: {schema} [{modo}]") print(f"{'='*60}") if modo == "rapido": - documentos = hana_manager.obtener_documentos_creados_actualizados_hace_x_minutos(schema, 60) + documentos = hana_manager.obtener_documentos_creados_actualizados_hace_60_minutos(schema, 60) else: documentos = hana_manager.obtener_documentos_fecha_emision_ultimos_8_dias(schema) if not documentos: @@ -186,69 +212,96 @@ def procesar_empresa(schema, datos_empresa, modo): return procesados, errores +# ============================================================================ +def procesar_todas_empresas(inicio_ciclo, modo): + + empresas = cargar_empresas() + print(f"📋 Empresas cargadas: {len(empresas)}") + + total_procesados = 0 + total_errores = 0 + + for empresa in empresas: + schema = empresa["CompanyDB"] + + if empresa.get("Locked") == "Y": + print(f"\n⏭️ Empresa {schema} está bloqueada (LOCKED=Y), saltando...") + continue + + sapb1_manager._sap_credentials = { + "company": empresa["CompanyDB"], + "username": empresa["UserName"], + "password": empresa["Password"], + } + + try: + p, e = procesar_empresa(schema, empresa, modo) + total_procesados += p + total_errores += e + except Exception as ex: + print(f"❌ Error procesando empresa {schema}: {ex}") + total_errores += 1 + + duracion_ciclo = datetime.now() - inicio_ciclo + print(f"\n📊 CICLO COMPLETADO en {str(duracion_ciclo).split('.')[0]}") + print(f" ✅ {total_procesados} procesados | ❌ {total_errores} errores") + return total_procesados, total_errores + # ============================================================================ def main(): + log_path = logger_utilities.setup_logger() logger_utilities.limpiar_logs_antiguos(dias=7) load_dotenv(dotenv_path=".env.dev") - empresas = cargar_empresas() - print(f"📋 Empresas cargadas: {len(empresas)}") - print("=" * 60) print("🚀 Demonio iniciado") print(f"📄 Log guardado en: {log_path}") - print(" Horario pico (cada 10 seg): 06:00 - 13:15 | 14:00 - 17:30") - print(" Horario valle (cada 30 min): resto del día") + print("Servicio iniciado") print("=" * 60) while True: + inicio_ciclo = datetime.now() - logger_utilities.rotar_log() - logger_utilities.limpiar_logs_antiguos(dias=7) + resultado = obtener_intervalo(inicio_ciclo) - modo = "rapido" if es_hora_pico(inicio_ciclo) else "lento" + if resultado is not None: - print(f"\n{'='*60}") - print(f"🔄 Ciclo [{modo.upper()}] {inicio_ciclo.strftime('%Y-%m-%d %H:%M:%S')}") - print(f"{'='*60}") + intervalo, modo = resultado - total_procesados = 0 - total_errores = 0 + print( + f"\n[{inicio_ciclo:%Y-%m-%d %H:%M:%S}] " + f"Iniciando ciclo [{modo}] (cada {intervalo}s)" + ) - for empresa in empresas: - schema = empresa["CompanyDB"] + procesar_todas_empresas(inicio_ciclo, modo) - if empresa.get("Locked") == "Y": - print(f"\n⏭️ Empresa {schema} está bloqueada (LOCKED=Y), saltando...") - continue + duracion = (datetime.now() - inicio_ciclo).total_seconds() - sapb1_manager._sap_credentials = { - "company": empresa["CompanyDB"], - "username": empresa["UserName"], - "password": empresa["Password"], - } + if duracion > intervalo: + print( + f"⚠️ El ciclo demoró {duracion:.1f}s " + f"(intervalo configurado {intervalo}s)" + ) - try: - p, e = procesar_empresa(schema, empresa, modo) - total_procesados += p - total_errores += e - except Exception as ex: - print(f"❌ Error procesando empresa {schema}: {ex}") - total_errores += 1 + dormir = max(0, intervalo - duracion) - duracion_ciclo = datetime.now() - inicio_ciclo - print(f"\n📊 CICLO COMPLETADO en {str(duracion_ciclo).split('.')[0]}") - print(f" ✅ {total_procesados} procesados | ❌ {total_errores} errores") + print(f"💤 Próximo ciclo [{modo}] en {dormir:.0f}s...") - transcurrido = duracion_ciclo.total_seconds() - modo_ahora = "rapido" if es_hora_pico(datetime.now()) else "lento" - intervalo = 10 if modo_ahora == "rapido" else 1800 - dormir = max(0, intervalo - transcurrido) + else: + + dormir = segundos_hasta_proximo_inicio(inicio_ciclo) + + despertar = inicio_ciclo + timedelta(seconds=dormir) + + print( + f"\n[{inicio_ciclo:%Y-%m-%d %H:%M:%S}] " + f"Fuera del horario.\n" + f"Próxima ejecución: {despertar:%Y-%m-%d %H:%M:%S}" + ) - print(f"💤 Próximo ciclo [{modo_ahora}] en {dormir:.0f}s...") time.sleep(dormir) # ============================================================================