diff --git a/app/routes/envios.py b/app/routes/envios.py index 899c79d..c7bdbf1 100644 --- a/app/routes/envios.py +++ b/app/routes/envios.py @@ -33,5 +33,9 @@ async def erp_sync_now( ventana_min: int = Query(default=30, ge=1, le=1440), user: dict = Depends(get_current_user), ): - resultado = await sync_recientes(ventana_min=ventana_min) - return JSONResponse(resultado) + import traceback as tb + try: + resultado = await sync_recientes(ventana_min=ventana_min) + return JSONResponse(resultado) + except Exception as e: + return JSONResponse({"ok": False, "error": str(e), "traceback": tb.format_exc()}, status_code=200) diff --git a/app/routes/pacientes.py b/app/routes/pacientes.py index 2a30eb6..a2e6136 100644 --- a/app/routes/pacientes.py +++ b/app/routes/pacientes.py @@ -113,10 +113,12 @@ async def examenes_paciente(request: Request, cedula: str = ""): def parse_hora(raw) -> datetime | None: if not raw: return None + if isinstance(raw, datetime): + return ahora.replace(hour=raw.hour, minute=raw.minute, second=raw.second, microsecond=0) s = str(raw) try: - if "T" in s: - dt = datetime.fromisoformat(s) + if len(s) > 10 and (s[10] == "T" or s[10] == " "): + dt = datetime.fromisoformat(s.replace(" ", "T")) return ahora.replace(hour=dt.hour, minute=dt.minute, second=dt.second, microsecond=0) partes = s.split(":") return ahora.replace(hour=int(partes[0]), minute=int(partes[1]), @@ -124,6 +126,24 @@ async def examenes_paciente(request: Request, cedula: str = ""): except Exception: return None + def safe_num(val): + if val is None: + return None + try: + return float(val) + except Exception: + return None + + def fmt_hora_str(val) -> str: + if not val: + return "" + if isinstance(val, datetime): + return val.strftime("%H:%M:%S") + s = str(val) + if len(s) > 10 and (s[10] == "T" or s[10] == " "): + return s[11:19] + return s[:8] + examenes = [] for row in rows: hora_dt = parse_hora(row.get("HORAINICIORECEPCION")) @@ -131,16 +151,16 @@ async def examenes_paciente(request: Request, cedula: str = ""): continue examenes.append({ "recepcion_id": row.get("IDRECEPCION"), - "hora": row.get("HORAINICIORECEPCION"), + "hora": fmt_hora_str(row.get("HORAINICIORECEPCION")), "cod_examen": (row.get("COD_EXAMEN") or "").strip(), "nombre": (row.get("NOM_EXAMEN") or "").strip(), "cups": (row.get("CUPS") or "").strip(), - "precio": row.get("PRECIO"), + "precio": safe_num(row.get("PRECIO")), "diagnostico_cod": (row.get("DIAG_PPAL") or "").strip(), "diagnostico_nombre": (row.get("DIAG_CONCEPTO") or "").strip(), "medico_docidmedico": (row.get("DOCIDMEDICO") or "").strip(), "nit_empresa": (row.get("NIT_EMPRESA") or "").strip(), - "valor_total": row.get("VALORTOTAL"), + "valor_total": safe_num(row.get("VALORTOTAL")), }) return JSONResponse({"ok": True, "examenes": examenes}) diff --git a/app/services/scheduler.py b/app/services/scheduler.py index 0d53f15..54213f0 100644 --- a/app/services/scheduler.py +++ b/app/services/scheduler.py @@ -62,12 +62,18 @@ ORDER BY r.IDRECEPCION def _parse_hora(val) -> datetime | None: if not val: return None + # Si ya es datetime (fdb devuelve objetos datetime) + if isinstance(val, datetime): + ahora = datetime.now() + return ahora.replace(hour=val.hour, minute=val.minute, second=val.second, microsecond=0) s = str(val) try: - if "T" in s: - dt = datetime.fromisoformat(s) + # "2026-07-16T15:20:12" o "2026-07-16 15:20:12" + if len(s) > 10 and (s[10] == "T" or s[10] == " "): + dt = datetime.fromisoformat(s.replace(" ", "T")) ahora = datetime.now() return ahora.replace(hour=dt.hour, minute=dt.minute, second=dt.second, microsecond=0) + # "15:20:12" partes = s.split(":") ahora = datetime.now() return ahora.replace(hour=int(partes[0]), minute=int(partes[1]), @@ -76,6 +82,29 @@ def _parse_hora(val) -> datetime | None: return None +def _fmt_hora_str(val) -> str: + """Extrae HH:MM:SS de un datetime o string timestamp de Firebird.""" + if not val: + return "" + if isinstance(val, datetime): + return val.strftime("%H:%M:%S") + s = str(val) + # "2026-07-16T15:20:12" o "2026-07-16 15:20:12" + if len(s) > 10 and (s[10] == "T" or s[10] == " "): + return s[11:19] + return s[:8] + + +def _safe_num(val): + """Convierte Decimal/int/float a float, o None.""" + if val is None: + return None + try: + return float(val) + except Exception: + return None + + 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() @@ -123,14 +152,14 @@ async def sync_recientes(ventana_min: int = 2) -> dict: "cod_examen": (ex.get("COD_EXAMEN") or "").strip(), "nombre": (ex.get("NOM_EXAMEN") or "").strip(), "cups": (ex.get("CUPS") or "").strip(), - "precio": ex.get("PRECIO"), + "precio": _safe_num(ex.get("PRECIO")), "recepcion_id": ex.get("IDRECEPCION"), - "hora": str(ex.get("HORAINICIORECEPCION") or "")[:8], + "hora": _fmt_hora_str(ex.get("HORAINICIORECEPCION")), "diagnostico_cod": (ex.get("DIAG_PPAL") or "").strip(), "diagnostico_nombre": (ex.get("DIAG_CONCEPTO") or "").strip(), "medico_docidmedico": (ex.get("DOCIDMEDICO") or "").strip(), "nit_empresa": (ex.get("NIT_EMPRESA") or "").strip(), - "valor_total": ex.get("VALORTOTAL"), + "valor_total": _safe_num(ex.get("VALORTOTAL")), }) ingest_url = f"{wa_url}/api/lab/ingest_paciente.php"