Agrega filtro convenio en automation/transaccion/test-rda y fix particular (contrato 12)

- automation: filtro contrato en SQL pacientes+RDA, numero LHXC en run()
- transaccion: filtro contrato en preview/send-one/send
- test-rda: filtro tipo_usuario (11/07/12), default contrato "12", acepta "12"/"012"
- json_generator: detecta particular por CODCONTRATO "12"/"012" además de NIT_EMPRESA PART; fuerza tipousuario="12", codFormaPago="CLIP", plazoDias="0"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-06-26 20:23:00 -05:00
co-authored by Claude Sonnet 4.6
parent d7afbe9b7d
commit 69372a5673
7 changed files with 136 additions and 30 deletions
+38 -9
View File
@@ -16,7 +16,7 @@ from app.services.api_client import get_tns_token, TNS_BASE
router = APIRouter(prefix="/automation", tags=["automation"])
# Query para obtener pacientes únicos por rango de fecha
# Query para obtener pacientes únicos por rango de fecha + contrato
_SQL_PACIENTES = """
SELECT DISTINCT
p.CODIGO,
@@ -35,11 +35,14 @@ SELECT DISTINCT
p.CODETNIA
FROM PACIENTE p
JOIN RECEPCION r ON r.COD_PACIENTE = p.CODIGO
LEFT JOIN EMPRESA e ON e.NIT = r.NIT_EMPRESA
LEFT JOIN CIUDAD c ON c.CODIGO = p.CIUDAD
WHERE r.FECHA_RECEPCION BETWEEN :fecha_ini AND :fecha_fin
AND r.NUM_FACTURA > 0
{filtro_contrato_pac}
"""
# Query para obtener recepciones con exámenes por rango de fecha
# Query para obtener recepciones con exámenes por rango de fecha + contrato
_SQL_RDA = """
SELECT
r.IDRECEPCION,
@@ -68,6 +71,7 @@ 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
{filtro_contrato_rda}
ORDER BY r.IDRECEPCION
"""
@@ -80,11 +84,25 @@ async def automation_page(request: Request, user: dict = Depends(get_current_use
})
def _build_sql(contrato: str):
if contrato:
fp = "AND e.CODCONTRATO = :contrato"
fr = "AND e.CODCONTRATO = :contrato"
else:
fp = ""
fr = ""
return (
_SQL_PACIENTES.format(filtro_contrato_pac=fp),
_SQL_RDA.format(filtro_contrato_rda=fr),
)
@router.post("/preview")
async def preview_automation(
request: Request,
user: dict = Depends(get_current_user),
fecha: str = Form(...),
contrato: str = Form(""),
):
conn = get_connection()
configs = {row["key"]: row["value"] for row in conn.execute("SELECT * FROM config").fetchall()}
@@ -97,13 +115,17 @@ async def preview_automation(
fecha_ini = f"{fecha} 00:00:00"
fecha_fin = f"{fecha} 23:59:59"
params = {"fecha_ini": fecha_ini, "fecha_fin": fecha_fin}
if contrato:
params["contrato"] = contrato.strip()
ok1, err1, rows_pac = fb.execute_query(_SQL_PACIENTES, params)
sql_pac, sql_rda = _build_sql(contrato.strip())
ok1, err1, rows_pac = fb.execute_query(sql_pac, params)
if not ok1:
fb.disconnect()
return JSONResponse({"success": False, "message": f"Error BD pacientes: {err1}"})
ok2, err2, rows_rda = fb.execute_query(_SQL_RDA, params)
ok2, err2, rows_rda = fb.execute_query(sql_rda, params)
fb.disconnect()
if not ok2:
return JSONResponse({"success": False, "message": f"Error BD RDA: {err2}"})
@@ -152,6 +174,7 @@ async def run_automation(
request: Request,
user: dict = Depends(get_current_user),
fecha: str = Form(...),
contrato: str = Form(""),
):
conn = get_connection()
configs = {row["key"]: row["value"] for row in conn.execute("SELECT * FROM config").fetchall()}
@@ -161,19 +184,22 @@ async def run_automation(
if not fb_ok:
return JSONResponse({"success": False, "message": f"Error Firebird: {fb_msg}"})
# Rango: día completo de la fecha seleccionada
fecha_ini = f"{fecha} 00:00:00"
fecha_fin = f"{fecha} 23:59:59"
params = {"fecha_ini": fecha_ini, "fecha_fin": fecha_fin}
if contrato:
params["contrato"] = contrato.strip()
sql_pac, sql_rda = _build_sql(contrato.strip())
# ── Paso 1: obtener pacientes únicos ─────────────────────────────────────
ok1, err1, rows_pac = fb.execute_query(_SQL_PACIENTES, params)
ok1, err1, rows_pac = fb.execute_query(sql_pac, params)
if not ok1:
fb.disconnect()
return JSONResponse({"success": False, "message": f"Error Firebird pacientes: {err1}"})
# ── Paso 2: obtener recepciones ───────────────────────────────────────────
ok2, err2, rows_rda = fb.execute_query(_SQL_RDA, params)
ok2, err2, rows_rda = fb.execute_query(sql_rda, params)
fb.disconnect()
if not ok2:
return JSONResponse({"success": False, "message": f"Error Firebird RDA: {err2}"})
@@ -238,8 +264,11 @@ async def run_automation(
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, remis_def, prefijo_def)
factura = str(grupo_rows[0].get("NUM_FACTURA", id_recepcion))
num_fac = str(grupo_rows[0].get("NUM_FACTURA") or "").strip()
prefijo_fb = str(grupo_rows[0].get("PREFIJO") or "").strip()
num_override = f"{prefijo_fb}{num_fac.zfill(5)}" if prefijo_fb and num_fac else num_fac
rda_json = generar_rda_paciente(grupo_rows, prof_def, esp_def, remis_def, prefijo_def, num_override)
factura = num_override or str(id_recepcion)
try:
resp = await client.post(endpoint, json=rda_json, headers=headers)
ok = resp.is_success
+33 -11
View File
@@ -84,7 +84,7 @@ async def get_enviados(request: Request, user: dict = Depends(get_current_user))
@router.get("/candidatos")
async def get_candidatos(request: Request, user: dict = Depends(get_current_user),
contrato: str = "", factura: str = "", articulos: str = "",
offset: int = 0):
tipo_usuario: str = "", offset: int = 0):
db = get_connection()
cfg = {r[0]: r[1] for r in db.execute("SELECT key, value FROM config").fetchall()}
db.close()
@@ -93,23 +93,45 @@ async def get_candidatos(request: Request, user: dict = Depends(get_current_user
if not fb_ok:
return JSONResponse({"error": f"Error Firebird: {fb_msg}"})
# Artículos configurados con especialidad en TNS
arts_default = "CH4,CREA,TGP,VSG,PCR,AU,FER,TPO,TSH,TGO,GLI,BUN,COL,TRI,HDL,LDL,HBA1,VB12,VITD,PO,K,NA,MG,URO,PRL,T4L,T3,T4,T3L,HIV,VDRL,PSA,AFP,CEA,CA125,CA199,FV,TP,TPT"
arts_str = articulos.strip() if articulos.strip() else arts_default
arts_list = [a.strip().upper() for a in arts_str.split(",") if a.strip()]
arts_in = ", ".join(f"'{a}'" for a in arts_list)
filtros = [
"r.NUM_FACTURA > 0",
"e.CODCONTRATO IS NOT NULL",
"TRIM(e.CODCONTRATO) <> ''",
# Solo registros donde TODOS los exámenes están en el listado permitido
f"NOT EXISTS (SELECT 1 FROM RELACION rx WHERE rx.IDRECEPCION = r.IDRECEPCION AND rx.COD_EXAMEN NOT IN ({arts_in}))",
]
es_particular = tipo_usuario.strip() == "12"
filtros = ["r.NUM_FACTURA > 0"]
if not es_particular:
# Para convenios: solo registros con contrato registrado
filtros += [
"e.CODCONTRATO IS NOT NULL",
"TRIM(e.CODCONTRATO) <> ''",
]
# Solo exámenes configurados en TNS
filtros.append(
f"NOT EXISTS (SELECT 1 FROM RELACION rx WHERE rx.IDRECEPCION = r.IDRECEPCION AND rx.COD_EXAMEN NOT IN ({arts_in}))"
)
params = {}
if contrato:
filtros.append("e.CODCONTRATO = :contrato")
params["contrato"] = contrato
c = contrato.strip()
c_alt = c.lstrip("0") or c # "012" → "12", "12" → "12"
c_pad = c.zfill(max(3, len(c))) # "12" → "012", "011" → "011"
filtros.append("TRIM(e.CODCONTRATO) IN (:contrato, :contrato_alt, :contrato_pad)")
params["contrato"] = c
params["contrato_alt"] = c_alt
params["contrato_pad"] = c_pad
elif es_particular:
# Particulares: NIT_EMPRESA es PART/PARTIC o sin contrato
filtros.append(
"(r.TIPOUSUSISPRO = '12' OR r.NIT_EMPRESA IN ('PART', 'PARTIC', 'PARTICULAR') "
"OR COALESCE(TRIM(e.CODCONTRATO), '') = '')"
)
if tipo_usuario and not es_particular:
filtros.append("r.TIPOUSUSISPRO = :tipo_usuario")
params["tipo_usuario"] = tipo_usuario.strip()
if factura:
filtros.append("r.NUM_FACTURA = :factura")
params["factura"] = int(factura)
+9 -1
View File
@@ -67,6 +67,7 @@ async def preview_transaccion(
request: Request, user: dict = Depends(get_current_user),
query_id: int = Form(...), factura: str = Form(""),
fecha_inicio: str = Form(...), fecha_fin: str = Form(...),
contrato: str = Form(""),
):
conn = get_connection()
q = conn.execute("SELECT * FROM queries WHERE id = ?", (query_id,)).fetchone()
@@ -82,6 +83,9 @@ async def preview_transaccion(
return JSONResponse({"success": False, "message": "Sin datos"})
grupos = agrupar_por_recepcion(rows)
if contrato:
grupos = {k: v for k, v in grupos.items()
if str(v[0].get("CODCONTRATO") or "").strip() == contrato.strip()}
prof_def = cfg.get("profesional_default", "")
esp_def = cfg.get("especialidad_default", "")
remis_def = cfg.get("remisionante_default", "00")
@@ -121,6 +125,7 @@ async def send_one(
request: Request, user: dict = Depends(get_current_user),
idrecepcion: int = Form(...), query_id: int = Form(...),
factura: str = Form(""), fecha_inicio: str = Form(...), fecha_fin: str = Form(...),
contrato: str = Form(""),
):
conn = get_connection()
q = conn.execute("SELECT * FROM queries WHERE id = ?", (query_id,)).fetchone()
@@ -204,7 +209,7 @@ async def send_transaccion(
request: Request, user: dict = Depends(get_current_user),
query_id: int = Form(...), factura: str = Form(""),
fecha_inicio: str = Form(...), fecha_fin: str = Form(...),
solo_pendientes: str = Form("0"),
solo_pendientes: str = Form("0"), contrato: str = Form(""),
):
conn = get_connection()
q = conn.execute("SELECT * FROM queries WHERE id = ?", (query_id,)).fetchone()
@@ -218,6 +223,9 @@ async def send_transaccion(
return JSONResponse({"success": False, "message": "Sin datos"})
grupos = agrupar_por_recepcion(rows)
if contrato:
grupos = {k: v for k, v in grupos.items()
if str(v[0].get("CODCONTRATO") or "").strip() == contrato.strip()}
if solo_pendientes == "1":
grupos = {k: v for k, v in grupos.items() if not _is_sent(k)}