Add test-rda page, fix prefijo/remisionante/especialidad for TNS RDA sends
- New /test-rda route: list Firebird candidates with valid contract/factura, send individual RDA with auto tercero creation and full result display - json_generator: use prefijo_tns_default config instead of Firebird PREFIJO, add profesionalRemisionante from DOCIDMEDICO or remisionante_default config - All queries: add DOCIDMEDICO column from MEDICO JOIN - automation/transaccion: add remisionante_default and prefijo_tns_default params - config: add remisionante_default (00) and prefijo_tns_default (00) keys - automation SQL: filter NUM_FACTURA > 0 to skip unbilled records - config.html: add UI fields for new config keys Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
3017c7956e
commit
c64a68ec6f
@@ -60,12 +60,14 @@ SELECT
|
||||
rel.FECHA_REPORTADO,
|
||||
m.COD_ESPECIALIDAD,
|
||||
COALESCE(NULLIF(TRIM(m.CODIGO), ''), NULLIF(TRIM(r.USUARIO), ''), '') AS profesional,
|
||||
e.CODCONTRATO
|
||||
e.CODCONTRATO,
|
||||
TRIM(m.DOCIDMEDICO) AS DOCIDMEDICO
|
||||
FROM RECEPCION r
|
||||
JOIN RELACION rel ON rel.IDRECEPCION = r.IDRECEPCION
|
||||
LEFT JOIN MEDICO m ON m.CODIGO = r.COD_MEDICO
|
||||
LEFT JOIN EMPRESA e ON e.NIT = r.NIT_EMPRESA
|
||||
WHERE r.FECHA_RECEPCION BETWEEN :fecha_ini AND :fecha_fin
|
||||
AND r.NUM_FACTURA > 0
|
||||
ORDER BY r.IDRECEPCION
|
||||
"""
|
||||
|
||||
@@ -231,10 +233,12 @@ async def run_automation(
|
||||
endpoint = f"{TNS_BASE}/v2/rda/RdaPaciente/Insertar?codigosucursal={api_sucursal or '00'}"
|
||||
prof_def = configs.get("profesional_default", "")
|
||||
esp_def = configs.get("especialidad_default", "")
|
||||
remis_def = configs.get("remisionante_default", "00")
|
||||
prefijo_def = configs.get("prefijo_tns_default", "00")
|
||||
|
||||
async with httpx.AsyncClient(timeout=timeout) as client:
|
||||
for id_recepcion, grupo_rows in grupos.items():
|
||||
rda_json = generar_rda_paciente(grupo_rows, prof_def, esp_def)
|
||||
rda_json = generar_rda_paciente(grupo_rows, prof_def, esp_def, remis_def, prefijo_def)
|
||||
factura = str(grupo_rows[0].get("NUM_FACTURA", id_recepcion))
|
||||
try:
|
||||
resp = await client.post(endpoint, json=rda_json, headers=headers)
|
||||
|
||||
@@ -25,6 +25,8 @@ DEFAULT_KEYS = [
|
||||
("cod_prestador", ""),
|
||||
("profesional_default", ""),
|
||||
("especialidad_default", ""),
|
||||
("remisionante_default", "00"),
|
||||
("prefijo_tns_default", "00"),
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -52,7 +52,8 @@ WHERE p.DOCIDENT = :doc_num""",
|
||||
rel.FECHA_REPORTADO,
|
||||
m.COD_ESPECIALIDAD,
|
||||
COALESCE(NULLIF(TRIM(m.CODIGO), ''), NULLIF(TRIM(r.USUARIO), ''), '') AS profesional,
|
||||
e.CODCONTRATO
|
||||
e.CODCONTRATO,
|
||||
TRIM(m.DOCIDMEDICO) AS DOCIDMEDICO
|
||||
FROM RECEPCION r
|
||||
JOIN RELACION rel ON rel.IDRECEPCION = r.IDRECEPCION
|
||||
LEFT JOIN MEDICO m ON m.CODIGO = r.COD_MEDICO
|
||||
@@ -82,7 +83,8 @@ WHERE r.NUM_FACTURA = :num_factura""",
|
||||
rel.FECHA_REPORTADO,
|
||||
m.COD_ESPECIALIDAD,
|
||||
COALESCE(NULLIF(TRIM(m.CODIGO), ''), NULLIF(TRIM(r.USUARIO), ''), '') AS profesional,
|
||||
e.CODCONTRATO
|
||||
e.CODCONTRATO,
|
||||
TRIM(m.DOCIDMEDICO) AS DOCIDMEDICO
|
||||
FROM RECEPCION r
|
||||
JOIN RELACION rel ON rel.IDRECEPCION = r.IDRECEPCION
|
||||
LEFT JOIN MEDICO m ON m.CODIGO = r.COD_MEDICO
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
import json as json_lib
|
||||
from fastapi import APIRouter, Request, Depends
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
import httpx
|
||||
|
||||
from app.database import get_connection
|
||||
from app.auth import get_current_user
|
||||
from app.services.firebird_service import get_firebird_from_config
|
||||
from app.services.json_generator import (
|
||||
generar_tercero_api,
|
||||
generar_rda_paciente,
|
||||
agrupar_por_recepcion,
|
||||
)
|
||||
from app.services.api_client import get_tns_token, TNS_BASE
|
||||
|
||||
router = APIRouter(prefix="/test-rda", tags=["test-rda"])
|
||||
|
||||
_SQL_RDA_BY_WHERE = """
|
||||
SELECT
|
||||
r.IDRECEPCION, r.PREFIJO, r.NUM_FACTURA, r.FECHA_RECEPCION, r.COD_PACIENTE,
|
||||
r.NIT_EMPRESA, r.DIAG_PPAL, r.TIPOUSU, r.TIPOUSUSISPRO, r.AUTORIZACION, r.CLASEPROC,
|
||||
r.HORAINICIORECEPCION, r.VALORTOTAL, rel.COD_EXAMEN, rel.PRECIO, rel.FECHA_REPORTADO,
|
||||
m.COD_ESPECIALIDAD,
|
||||
COALESCE(NULLIF(TRIM(m.CODIGO), ''), NULLIF(TRIM(r.USUARIO), ''), '') AS profesional,
|
||||
e.CODCONTRATO,
|
||||
TRIM(m.DOCIDMEDICO) AS DOCIDMEDICO
|
||||
FROM RECEPCION r
|
||||
JOIN RELACION rel ON rel.IDRECEPCION = r.IDRECEPCION
|
||||
LEFT JOIN MEDICO m ON m.CODIGO = r.COD_MEDICO
|
||||
LEFT JOIN EMPRESA e ON e.NIT = r.NIT_EMPRESA
|
||||
WHERE {where}
|
||||
AND r.NUM_FACTURA > 0
|
||||
AND e.CODCONTRATO IS NOT NULL
|
||||
AND TRIM(e.CODCONTRATO) <> ''
|
||||
ORDER BY r.IDRECEPCION
|
||||
"""
|
||||
|
||||
_SQL_CANDIDATOS = """
|
||||
SELECT FIRST 100
|
||||
r.IDRECEPCION,
|
||||
r.NUM_FACTURA,
|
||||
r.FECHA_RECEPCION,
|
||||
r.COD_PACIENTE,
|
||||
r.NIT_EMPRESA,
|
||||
e.CODCONTRATO,
|
||||
LIST(DISTINCT rel.COD_EXAMEN, ', ') AS examenes,
|
||||
COUNT(DISTINCT rel.COD_EXAMEN) AS total_examenes
|
||||
FROM RECEPCION r
|
||||
JOIN RELACION rel ON rel.IDRECEPCION = r.IDRECEPCION
|
||||
LEFT JOIN EMPRESA e ON e.NIT = r.NIT_EMPRESA
|
||||
WHERE r.NUM_FACTURA > 0
|
||||
AND e.CODCONTRATO IS NOT NULL
|
||||
AND TRIM(e.CODCONTRATO) <> ''
|
||||
GROUP BY r.IDRECEPCION, r.NUM_FACTURA, r.FECHA_RECEPCION, r.COD_PACIENTE, r.NIT_EMPRESA, e.CODCONTRATO
|
||||
ORDER BY r.IDRECEPCION DESC
|
||||
"""
|
||||
|
||||
_SQL_PACIENTE = """
|
||||
SELECT p.CODIGO, p.NOMBRES, p.APELLIDOS, p.DOCIDENT, p.TIPOIDENT,
|
||||
p.F_NACIMIENTO, p.SEXO, p.DIRECCION, p.TELEFONOS, p.EMAIL
|
||||
FROM PACIENTE p WHERE p.CODIGO = :cod
|
||||
"""
|
||||
|
||||
|
||||
@router.get("")
|
||||
async def test_page(request: Request, user: dict = Depends(get_current_user)):
|
||||
return request.app.state.templates.TemplateResponse(
|
||||
"test_rda.html", {"request": request, "user": user}
|
||||
)
|
||||
|
||||
|
||||
@router.get("/candidatos")
|
||||
async def get_candidatos(request: Request, user: dict = Depends(get_current_user),
|
||||
contrato: str = "", factura: str = ""):
|
||||
db = get_connection()
|
||||
cfg = {r[0]: r[1] for r in db.execute("SELECT key, value FROM config").fetchall()}
|
||||
db.close()
|
||||
|
||||
fb = get_firebird_from_config(cfg)
|
||||
ok, err = fb.connect()
|
||||
if not ok:
|
||||
return JSONResponse({"error": f"Error Firebird: {err}"})
|
||||
|
||||
sql = _SQL_CANDIDATOS
|
||||
params = {}
|
||||
filtros = ["r.NUM_FACTURA > 0", "e.CODCONTRATO IS NOT NULL", "TRIM(e.CODCONTRATO) <> ''"]
|
||||
if contrato:
|
||||
filtros.append("e.CODCONTRATO = :contrato")
|
||||
params["contrato"] = contrato
|
||||
if factura:
|
||||
filtros.append("r.NUM_FACTURA = :factura")
|
||||
params["factura"] = int(factura)
|
||||
|
||||
sql_final = """
|
||||
SELECT FIRST 100
|
||||
r.IDRECEPCION, r.NUM_FACTURA, r.FECHA_RECEPCION, r.COD_PACIENTE, r.NIT_EMPRESA,
|
||||
e.CODCONTRATO, LIST(DISTINCT rel.COD_EXAMEN, ', ') AS examenes,
|
||||
COUNT(DISTINCT rel.COD_EXAMEN) AS total_examenes
|
||||
FROM RECEPCION r
|
||||
JOIN RELACION rel ON rel.IDRECEPCION = r.IDRECEPCION
|
||||
LEFT JOIN EMPRESA e ON e.NIT = r.NIT_EMPRESA
|
||||
WHERE """ + " AND ".join(filtros) + """
|
||||
GROUP BY r.IDRECEPCION, r.NUM_FACTURA, r.FECHA_RECEPCION, r.COD_PACIENTE, r.NIT_EMPRESA, e.CODCONTRATO
|
||||
ORDER BY r.IDRECEPCION DESC
|
||||
"""
|
||||
|
||||
ok2, err2, rows = fb.execute_query(sql_final, params)
|
||||
fb.disconnect()
|
||||
|
||||
if not ok2:
|
||||
return JSONResponse({"error": err2})
|
||||
|
||||
candidatos = []
|
||||
for row in rows:
|
||||
candidatos.append({
|
||||
"idrecepcion": row.get("IDRECEPCION"),
|
||||
"factura": row.get("NUM_FACTURA"),
|
||||
"fecha": str(row.get("FECHA_RECEPCION", ""))[:10],
|
||||
"paciente": row.get("COD_PACIENTE", ""),
|
||||
"empresa": row.get("NIT_EMPRESA", ""),
|
||||
"contrato": row.get("CODCONTRATO", ""),
|
||||
"examenes": row.get("examenes", ""),
|
||||
"total": row.get("total_examenes", 0),
|
||||
})
|
||||
|
||||
return JSONResponse({"candidatos": candidatos})
|
||||
|
||||
|
||||
@router.post("/enviar")
|
||||
async def test_enviar(request: Request, user: dict = Depends(get_current_user)):
|
||||
form = await request.form()
|
||||
modo = form.get("modo", "idrecepcion")
|
||||
valor = str(form.get("valor", "")).strip()
|
||||
|
||||
if not valor:
|
||||
return JSONResponse({"error": "Ingresa un IDRECEPCION o número de factura"})
|
||||
|
||||
db = get_connection()
|
||||
cfg = {r[0]: r[1] for r in db.execute("SELECT key, value FROM config").fetchall()}
|
||||
db.close()
|
||||
|
||||
prof_def = cfg.get("profesional_default", "XIMENA")
|
||||
esp_def = cfg.get("especialidad_default", "02")
|
||||
remis_def = cfg.get("remisionante_default", "00")
|
||||
prefijo_def = cfg.get("prefijo_tns_default", "00")
|
||||
api_sucursal = cfg.get("api_sucursal", "00") or "00"
|
||||
|
||||
fb = get_firebird_from_config(cfg)
|
||||
ok, err = fb.connect()
|
||||
if not ok:
|
||||
return JSONResponse({"error": f"Error Firebird: {err}"})
|
||||
|
||||
if modo == "factura":
|
||||
where = "r.NUM_FACTURA = :val"
|
||||
params = {"val": int(valor)}
|
||||
else:
|
||||
where = "r.IDRECEPCION = :val"
|
||||
params = {"val": int(valor)}
|
||||
|
||||
ok2, err2, rows_rda = fb.execute_query(
|
||||
_SQL_RDA_BY_WHERE.format(where=where), params
|
||||
)
|
||||
if not ok2:
|
||||
fb.disconnect()
|
||||
return JSONResponse({"error": f"Error consulta: {err2}"})
|
||||
|
||||
if not rows_rda:
|
||||
fb.disconnect()
|
||||
return JSONResponse({"error": "Sin registros (¿NUM_FACTURA = 0 o contrato vacío?)"})
|
||||
|
||||
# Pacientes únicos
|
||||
pacientes_unicos = {}
|
||||
for row in rows_rda:
|
||||
cod = str(row.get("COD_PACIENTE", "")).strip()
|
||||
if cod and cod not in pacientes_unicos:
|
||||
ok3, err3, prows = fb.execute_query(_SQL_PACIENTE, {"cod": cod})
|
||||
if ok3 and prows:
|
||||
pacientes_unicos[cod] = prows[0]
|
||||
|
||||
fb.disconnect()
|
||||
|
||||
token, err_tns = await get_tns_token(
|
||||
cfg.get("tns_empresa", ""), cfg.get("tns_usuario", ""), cfg.get("tns_password", "")
|
||||
)
|
||||
if not token:
|
||||
return JSONResponse({"error": f"Error login TNS: {err_tns}"})
|
||||
|
||||
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
|
||||
endpoint_rda = f"{TNS_BASE}/v2/rda/RdaPaciente/Insertar?codigosucursal={api_sucursal}"
|
||||
|
||||
pasos = []
|
||||
|
||||
# Paso 1: crear terceros
|
||||
async with httpx.AsyncClient(timeout=20) as client:
|
||||
for cod, pac_row in pacientes_unicos.items():
|
||||
tercero_json = generar_tercero_api(pac_row)
|
||||
try:
|
||||
r = await client.post(
|
||||
f"{TNS_BASE}/v2/tablas/Tercero/Crear",
|
||||
json=tercero_json, headers=headers,
|
||||
)
|
||||
try:
|
||||
data = r.json()
|
||||
msg = data.get("message") or r.text[:200]
|
||||
except Exception:
|
||||
msg = r.text[:200]
|
||||
pasos.append({
|
||||
"tipo": "tercero",
|
||||
"codigo": cod,
|
||||
"nombre": tercero_json.get("nombre", ""),
|
||||
"status": r.status_code,
|
||||
"ok": r.status_code in (200, 201),
|
||||
"mensaje": msg,
|
||||
})
|
||||
except Exception as ex:
|
||||
pasos.append({"tipo": "tercero", "codigo": cod, "ok": False, "mensaje": str(ex)})
|
||||
|
||||
# Paso 2: enviar RDA
|
||||
grupos = agrupar_por_recepcion(rows_rda)
|
||||
async with httpx.AsyncClient(timeout=30) as client:
|
||||
for id_rec, grupo_rows in grupos.items():
|
||||
rda_json = generar_rda_paciente(grupo_rows, prof_def, esp_def, remis_def, prefijo_def)
|
||||
examenes = [r.get("COD_EXAMEN", "") for r in grupo_rows]
|
||||
try:
|
||||
r = await client.post(endpoint_rda, json=rda_json, headers=headers)
|
||||
try:
|
||||
data = r.json()
|
||||
ok_rda = (data.get("status") or (data.get("data") or {}).get("success", False)) if isinstance(data, dict) else False
|
||||
msg_rda = ((data.get("data") or {}).get("response") or data.get("message") or r.text[:200])
|
||||
except Exception:
|
||||
ok_rda = r.status_code < 300
|
||||
msg_rda = r.text[:200]
|
||||
pasos.append({
|
||||
"tipo": "rda",
|
||||
"idrecepcion": id_rec,
|
||||
"factura": rda_json.get("numero", ""),
|
||||
"paciente": rda_json.get("codTercero", ""),
|
||||
"contrato": rda_json.get("numeroContrato", ""),
|
||||
"examenes": examenes,
|
||||
"status": r.status_code,
|
||||
"ok": ok_rda,
|
||||
"mensaje": msg_rda,
|
||||
"json_enviado": rda_json,
|
||||
})
|
||||
except Exception as ex:
|
||||
pasos.append({
|
||||
"tipo": "rda", "idrecepcion": id_rec, "ok": False,
|
||||
"examenes": examenes, "mensaje": str(ex), "json_enviado": {},
|
||||
})
|
||||
|
||||
rda_ok = sum(1 for p in pasos if p["tipo"] == "rda" and p["ok"])
|
||||
rda_err = sum(1 for p in pasos if p["tipo"] == "rda" and not p["ok"])
|
||||
|
||||
return JSONResponse({
|
||||
"resumen": {
|
||||
"total_recepciones": len(grupos),
|
||||
"rda_exitosos": rda_ok,
|
||||
"rda_errores": rda_err,
|
||||
"pacientes_procesados": len(pacientes_unicos),
|
||||
},
|
||||
"pasos": pasos,
|
||||
})
|
||||
@@ -155,10 +155,12 @@ async def send_transaccion(
|
||||
resultados = []
|
||||
prof_def = configs.get("profesional_default", "")
|
||||
esp_def = configs.get("especialidad_default", "")
|
||||
remis_def = configs.get("remisionante_default", "00")
|
||||
prefijo_def = configs.get("prefijo_tns_default", "00")
|
||||
|
||||
async with httpx.AsyncClient(timeout=int(configs.get("api_timeout", 30))) as client:
|
||||
for id_recepcion, grupo_rows in grupos.items():
|
||||
trans_json = generar_rda_paciente(grupo_rows, prof_def, esp_def)
|
||||
trans_json = generar_rda_paciente(grupo_rows, prof_def, esp_def, remis_def, prefijo_def)
|
||||
|
||||
status_ok = False
|
||||
response_text = ""
|
||||
|
||||
Reference in New Issue
Block a user