fix: handle Firebird Decimal and datetime types in JSON serialization
- PRECIO and VALORTOTAL are Decimal from fdb driver; convert via _safe_num() - HORAINICIORECEPCION is a datetime object; use _fmt_hora_str() for HH:MM:SS - _parse_hora() now handles datetime objects directly (no str() roundtrip) - Same fixes applied to pacientes.py exam builder - sync-now endpoint catches exceptions and returns traceback as JSON Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
fc18acc761
commit
863e012c12
@@ -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)
|
||||
|
||||
+25
-5
@@ -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})
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user