feat(envios/erp): add manual sync trigger endpoint and test UI

- POST /envios/erp/sync-now?ventana_min=N calls sync_recientes with configurable window
- sync_recientes() now returns a result dict (ok, total, created, updated, skipped, errores, detalle)
- ERP tab shows a "Prueba manual" card with ventana selector (2m / 30m / 2h / hoy) and result breakdown

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-07-16 21:40:54 -05:00
co-authored by Claude Sonnet 4.6
parent 8e27225326
commit dc18d128fb
3 changed files with 124 additions and 14 deletions
+27 -13
View File
@@ -76,8 +76,8 @@ def _parse_hora(val) -> datetime | None:
return None
async def sync_recientes():
"""Job que corre cada minuto: sincroniza pacientes con recepción en los últimos 2 min."""
async def sync_recientes(ventana_min: int = 2) -> dict:
"""Job que corre cada minuto: sincroniza pacientes con recepción en los últimos `ventana_min` min."""
conn = get_connection()
configs = {row["key"]: row["value"] for row in conn.execute("SELECT * FROM config").fetchall()}
conn.close()
@@ -85,29 +85,31 @@ async def sync_recientes():
wa_url = configs.get("whatsapp_url", "").rstrip("/")
wa_key = configs.get("whatsapp_api_key", "")
if not wa_url or not wa_key:
return
return {"ok": False, "error": "whatsapp_url o whatsapp_api_key no configurados"}
fb, ok, _ = get_firebird_from_config(configs)
fb, ok, msg = get_firebird_from_config(configs)
if not ok:
return
return {"ok": False, "error": f"Firebird: {msg}"}
ok_pac, _, rows_pac = fb.execute_query(_SQL_HOY, None)
ok_ex, _, rows_ex = fb.execute_query(_SQL_EXAMENES_HOY, None)
ok_pac, err_pac, rows_pac = fb.execute_query(_SQL_HOY, None)
ok_ex, _, rows_ex = fb.execute_query(_SQL_EXAMENES_HOY, None)
fb.disconnect()
if not ok_pac or not rows_pac:
return
if not ok_pac:
return {"ok": False, "error": f"Query pacientes: {err_pac}"}
limite = datetime.now() - timedelta(minutes=2)
total_hoy = len(rows_pac) if rows_pac else 0
limite = datetime.now() - timedelta(minutes=ventana_min)
recientes = [
row for row in rows_pac
row for row in (rows_pac or [])
if (h := _parse_hora(row.get("HORAINICIORECEPCION"))) is None or h >= limite
]
if not recientes:
return
return {"ok": True, "total": 0, "created": 0, "updated": 0,
"skipped": 0, "errores": 0, "total_hoy": total_hoy,
"ventana_min": ventana_min, "mensaje": "Sin recepciones en la ventana de tiempo"}
# Construir mapa DOCIDENT → lista de exámenes filtrados por la misma ventana de 2 min
examenes_map: dict[str, list] = {}
if ok_ex and rows_ex:
for ex in rows_ex:
@@ -138,3 +140,15 @@ async def sync_recientes():
if resultado["total"] > 0:
guardar_sync_log(resultado, 0, origen="scheduler", modo="upsert")
return {
"ok": True,
"total": resultado["total"],
"created": resultado["created"],
"updated": resultado["updated"],
"skipped": resultado["skipped"],
"errores": resultado["errores"],
"total_hoy": total_hoy,
"ventana_min": ventana_min,
"detalle": resultado.get("detalle", []),
}