diff --git a/app/routes/automation.py b/app/routes/automation.py index d6fa572..702e0b1 100644 --- a/app/routes/automation.py +++ b/app/routes/automation.py @@ -587,27 +587,22 @@ async def run_automation( f"{TNS_BASE}/v2/tablas/Tercero/Crear", json=tercero_json, headers=headers, ) - body = resp.json() - ok = bool(body.get("status")) or bool((body.get("data") or {}).get("success")) - msg = body.get("message") or (body.get("data") or {}).get("response") or resp.text[:200] - # "ya registrado" no es error real — el tercero existe en TNS - if not ok and msg and any(s in msg.lower() for s in ("ya esta registrado", "ya existe", "already exist")): - ok = True + status, msg = _parse_tns_resp(resp) except Exception as e: - ok = False - msg = str(e) + status, msg = "error", str(e) - if ok: - resultado["paso1_terceros"]["enviados"] += 1 - else: + if status == "error": resultado["paso1_terceros"]["errores"] += 1 terceros_fallidos.add(codigo_pac) + else: + resultado["paso1_terceros"]["enviados"] += 1 resultado["paso1_terceros"]["detalle"].append({ - "codigo": codigo_pac, "doc": doc, "nombre": nombre, "ok": ok, "msg": msg, + "codigo": codigo_pac, "doc": doc, "nombre": nombre, + "ok": status != "error", "status": status, "msg": msg, }) - _guardar_envio(user["user_id"], "terceros", fecha, tercero_json, msg, ok, + _guardar_envio(user["user_id"], "terceros", fecha, tercero_json, msg, status, cedula=str(row.get("DOCIDENT") or "").strip()) # ── Sync paralelo a WhatsApp Lab (silencioso) ───────────────────────────── @@ -656,24 +651,24 @@ async def run_automation( factura = num_override or str(id_recepcion) try: resp = await client.post(endpoint, json=rda_json, headers=headers) - ok, msg = _parse_tns_resp(resp) + status, msg = _parse_tns_resp(resp) except Exception as e: - ok, msg = False, str(e) + status, msg = "error", str(e) - if ok: - resultado["paso2_rda"]["enviados"] += 1 - else: + if status == "error": resultado["paso2_rda"]["errores"] += 1 + else: + resultado["paso2_rda"]["enviados"] += 1 resultado["paso2_rda"]["detalle"].append({ "idrecepcion": id_recepcion, "factura": factura, "paciente": str(grupo_rows[0].get("COD_PACIENTE", "")), "examenes": len(grupo_rows), - "ok": ok, "msg": msg, + "ok": status != "error", "status": status, "msg": msg, }) - _guardar_envio(user["user_id"], "transaccion", factura, rda_json, msg, ok, + _guardar_envio(user["user_id"], "transaccion", factura, rda_json, msg, status, fecha_inicio=fecha, fecha_fin=fecha, servicios=len(grupo_rows), idrecepcion=id_recepcion, contrato=str(grupo_rows[0].get("CODCONTRATO") or "").strip(), cedula=pac_map.get(str(grupo_rows[0].get("COD_PACIENTE", "")), "")) @@ -710,24 +705,24 @@ async def run_automation( factura_ps = f"{ps_prefijo}-{ps_numero.zfill(5)}" try: resp = await client.post(endpoint, json=rda_json, headers=headers) - ok, msg = _parse_tns_resp(resp) + status, msg = _parse_tns_resp(resp) except Exception as e: - ok, msg = False, str(e) + status, msg = "error", str(e) - if ok: - resultado["paso3_preserv"]["enviados"] += 1 - else: + if status == "error": resultado["paso3_preserv"]["errores"] += 1 + else: + resultado["paso3_preserv"]["enviados"] += 1 resultado["paso3_preserv"]["detalle"].append({ "idrecepcion": id_ps, "factura": factura_ps, "paciente": str(grupo_rows[0].get("COD_PACIENTE", "")), "examenes": len(grupo_rows), - "ok": ok, "msg": msg, + "ok": status != "error", "status": status, "msg": msg, }) - _guardar_envio(user["user_id"], "transaccion", factura_ps, rda_json, msg, ok, + _guardar_envio(user["user_id"], "transaccion", factura_ps, rda_json, msg, status, fecha_inicio=fecha, fecha_fin=fecha, servicios=len(grupo_rows), idrecepcion=id_ps, contrato=str(grupo_rows[0].get("CODCONTRATO") or "").strip(), cedula=pac_map.get(str(grupo_rows[0].get("COD_PACIENTE", "")), "")) @@ -752,24 +747,24 @@ async def run_automation( contrato_vta = str(grupo_rows[0].get("CODCONTRATO") or "").strip() try: resp = await client.post(endpoint_venta, json=venta_json, headers=headers) - ok, msg = _parse_tns_resp(resp) + status, msg = _parse_tns_resp(resp) except Exception as e: - ok, msg = False, str(e) + status, msg = "error", str(e) - if ok: - resultado["paso4_ventas"]["enviados"] += 1 - else: + if status == "error": resultado["paso4_ventas"]["errores"] += 1 + else: + resultado["paso4_ventas"]["enviados"] += 1 resultado["paso4_ventas"]["detalle"].append({ "idrecepcion": id_rec, "factura": factura_display, "paciente": str(grupo_rows[0].get("COD_PACIENTE", "")), "examenes": len(grupo_rows), - "ok": ok, "msg": msg, + "ok": status != "error", "status": status, "msg": msg, }) - _guardar_envio(user["user_id"], "ventas", factura_display, venta_json, msg, ok, + _guardar_envio(user["user_id"], "ventas", factura_display, venta_json, msg, status, fecha_inicio=fecha, fecha_fin=fecha, servicios=len(grupo_rows), idrecepcion=id_rec, contrato=contrato_vta, cedula=pac_map.get(str(grupo_rows[0].get("COD_PACIENTE", "")), "")) @@ -787,36 +782,34 @@ async def run_automation( return JSONResponse({"success": True, "resultado": resultado}) -_YA_EXISTE = ("ya esta registrado", "ya existe", "already exist", "ya existe una autorización") +_YA_WARNING = ( + "ya esta registrado", "ya existe", "already exist", + "ya existe una autorización", "ya existe un rda", +) -def _parse_tns_resp(resp) -> tuple[bool, str]: - """Parsea respuesta TNS: retorna (ok, msg). Trata 'ya existe' como OK.""" +def _parse_tns_resp(resp) -> tuple[str, str]: + """Parsea respuesta TNS. Retorna (status, msg) donde status es 'success'|'warning'|'error'.""" + raw_body = resp.text try: body = resp.json() raw_status = body.get("status") - # TNS usa status=true/false (booleano); ASP.NET usa status=400 (entero HTTP) → error if isinstance(raw_status, bool): ok = raw_status elif isinstance(raw_status, int): ok = raw_status < 400 else: ok = bool(raw_status) or bool((body.get("data") or {}).get("success")) - # Mensaje: TNS normal vs error de validación ASP.NET - errors = body.get("errors") - if errors and isinstance(errors, dict): - msgs = [f"{k}: {v[0] if isinstance(v, list) else v}" for k, v in errors.items()] - msg = " | ".join(msgs) - else: - msg = body.get("message") or (body.get("data") or {}).get("response") or body.get("title") or resp.text[:200] + msg = raw_body # siempre guardamos el JSON crudo completo except Exception: ok = resp.is_success - msg = resp.text[:200] - if not ok and msg and any(s in str(msg).lower() for s in _YA_EXISTE): - ok = True - return ok, str(msg or "") + msg = raw_body[:500] + + if not ok and any(s in msg.lower() for s in _YA_WARNING): + return "warning", msg + return ("success" if ok else "error"), msg -def _guardar_envio(user_id, tipo, factura, json_data, respuesta, ok, +def _guardar_envio(user_id, tipo, factura, json_data, respuesta, status, fecha_inicio=None, fecha_fin=None, servicios=0, idrecepcion=None, contrato=None, cedula=None): try: @@ -830,10 +823,10 @@ def _guardar_envio(user_id, tipo, factura, json_data, respuesta, ok, user_id, tipo, factura, idrecepcion, contrato, cedula, fecha_inicio, fecha_fin, 1, servicios, - "success" if ok else "error", + status, json_lib.dumps(json_data, indent=2, ensure_ascii=False)[:10000], - respuesta[:1000] if respuesta else "", - respuesta[:300] if respuesta else "", + respuesta[:5000] if respuesta else "", + respuesta[:500] if respuesta else "", datetime.now().isoformat(), )) conn.commit() diff --git a/app/routes/logs.py b/app/routes/logs.py index eef1ed5..a3cf046 100644 --- a/app/routes/logs.py +++ b/app/routes/logs.py @@ -8,6 +8,7 @@ from app.auth import get_current_user from app.services.json_generator import _clean_times from app.services.api_client import get_tns_token, TNS_BASE from app.utils.activity import log_activity, get_ip +from app.routes.automation import _parse_tns_resp router = APIRouter(prefix="/logs", tags=["logs"]) @@ -144,8 +145,8 @@ async def reenviar_envio(envio_id: int, request: Request, user: dict = Depends(g conn.close() if not row: return JSONResponse({"success": False, "message": "Registro no encontrado"}) - if row["status"] == "success": - return JSONResponse({"success": False, "message": "Ya fue enviado exitosamente"}) + if row["status"] in ("success", "warning"): + return JSONResponse({"success": False, "message": "No aplica reenvío para este registro"}) if not row["json_enviado"]: return JSONResponse({"success": False, "message": "Sin JSON guardado para reenviar"}) @@ -170,42 +171,32 @@ async def reenviar_envio(envio_id: int, request: Request, user: dict = Depends(g endpoint = ( f"{TNS_BASE}/v2/rda/RdaPaciente/Insertar?codigosucursal={api_sucursal}" if tipo == "transaccion" + else f"{TNS_BASE}/v2/facturacion/Ventas/Crear?codigosucursal={api_sucursal}" + if tipo == "ventas" else f"{TNS_BASE}/v2/tablas/Tercero/Crear" ) - ok = False + new_status = "error" msg = "" - raw_resp = "" try: async with httpx.AsyncClient(timeout=30) as client: r = await client.post(endpoint, json=rda_json, headers=headers) - raw_resp = r.text - try: - data = r.json() - if tipo == "transaccion": - ok = bool(data.get("status") or (data.get("data") or {}).get("success", False)) - msg = ((data.get("data") or {}).get("response") or data.get("message") or raw_resp[:300]) - else: - ok = r.is_success - msg = data.get("message", "") or raw_resp[:200] - except Exception: - ok = r.status_code < 300 - msg = raw_resp[:300] + new_status, msg = _parse_tns_resp(r) except Exception as ex: msg = str(ex) - if ok: + if new_status != "error": conn = get_connection() conn.execute( - "UPDATE envios SET status='success', mensaje_tns=?, respuesta_api=?, created_at=? WHERE id=?", - (msg, raw_resp[:2000], datetime.now().isoformat(), envio_id) + "UPDATE envios SET status=?, mensaje_tns=?, respuesta_api=?, created_at=? WHERE id=?", + (new_status, msg[:500], msg[:5000], datetime.now().isoformat(), envio_id) ) conn.commit() conn.close() log_activity(user["user_id"], user["username"], "reenvio_ok", - f"Envío #{envio_id} factura {row['factura']} reenvío exitoso", get_ip(request)) + f"Envío #{envio_id} factura {row['factura']} reenvío {new_status}", get_ip(request)) - return JSONResponse({"success": ok, "message": msg}) + return JSONResponse({"success": new_status != "error", "message": msg}) @router.get("/detalle/{envio_id}") diff --git a/app/templates/logs.html b/app/templates/logs.html index f7f647c..01305d1 100644 --- a/app/templates/logs.html +++ b/app/templates/logs.html @@ -12,10 +12,10 @@ {% if s.tipo == 'transaccion' %}bg-purple-100 text-purple-700 {% elif s.tipo == 'ventas' %}bg-emerald-100 text-emerald-700 {% else %}bg-blue-100 text-blue-700{% endif %}">{{ s.tipo }} - + {{ s.cnt }} - {{ 'exitosos' if s.status == 'success' else 'errores' }} + {{ 'enviados' if s.status == 'success' else ('ya existe' if s.status == 'warning' else 'errores') }} {% endfor %}
@@ -42,8 +42,9 @@
@@ -109,7 +110,7 @@ {% for e in envios %} + {% if e.status == 'success' %}bg-green-50/25{% elif e.status == 'warning' %}bg-yellow-50/40{% elif e.status == 'error' %}bg-red-50/25{% endif %}"> {{ e.id }} {{ e.idrecepcion or '-' }} {{ e.contrato or '-' }} - - {{ '✓ OK' if e.status == 'success' else '✗ Error' }} - + {% if e.status == 'success' %} + ✓ Enviado + {% elif e.status == 'warning' %} + ! Ya existe + {% else %} + ✗ Error + {% endif %} {% if e.mensaje_tns %} @@ -247,9 +250,9 @@ async function verDetalle(id) { const data = await resp.json(); if (data.error) { showToast(data.error, 'error'); return; } - const estadoLabel = data.status === 'success' ? '✓ Exitoso' : '✗ Error'; + const estadoLabel = data.status === 'success' ? '✓ Enviado' : data.status === 'warning' ? '! Ya existe' : '✗ Error'; document.getElementById('modal-titulo').innerHTML = - `Envío #${data.id} — ${data.tipo}${estadoLabel}`; + `Envío #${data.id} — ${data.tipo}${estadoLabel}`; const info = [ data.factura ? `Factura: ${data.factura}` : '',