diff --git a/app/routes/automation.py b/app/routes/automation.py index 8b71287..7191c97 100644 --- a/app/routes/automation.py +++ b/app/routes/automation.py @@ -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 diff --git a/app/routes/test_rda.py b/app/routes/test_rda.py index a670dad..dcfe623 100644 --- a/app/routes/test_rda.py +++ b/app/routes/test_rda.py @@ -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) diff --git a/app/routes/transaccion.py b/app/routes/transaccion.py index 558f9de..20a2bf4 100644 --- a/app/routes/transaccion.py +++ b/app/routes/transaccion.py @@ -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)} diff --git a/app/services/json_generator.py b/app/services/json_generator.py index a57e420..fbb08fb 100644 --- a/app/services/json_generator.py +++ b/app/services/json_generator.py @@ -142,17 +142,24 @@ def generar_rda_paciente(rows: list, default_profesional: str = "", default_espe autorizacion = str(h.get("AUTORIZACION") or "").strip() or None nit_empresa = str(h.get("NIT_EMPRESA") or "").strip() - es_particular = nit_empresa.upper() in ("PART", "PARTICULAR", "", "0") - cod_contrato = str(h.get("CODCONTRATO") or "").strip() or None - # Códigos configurados en TNS para este laboratorio (GET /v2/tablas/FormaPago/ObtenerFormasDePago) + cod_contrato_raw = str(h.get("CODCONTRATO") or "").strip() + # Particular: por NIT_EMPRESA "PART" o por contrato "12"/"012" + es_particular = ( + nit_empresa.upper() in ("PART", "PARTICULAR", "", "0") + or cod_contrato_raw.lstrip("0") == "12" + ) + cod_contrato = cod_contrato_raw or ("12" if es_particular else None) cod_forma_pago = "CLIP" if es_particular else "INST" - # tipousuario: usar TIPOUSUSISPRO; si es NULL derivar de TIPOUSU + NIT_EMPRESA + # tipousuario: particular siempre "12"; para convenios usar TIPOUSUSISPRO o derivar _TIPOUSU_MAP = {"1": "11", "5": "07"} # EPS→11, Póliza→07 - tipoususispro = str(h.get("TIPOUSUSISPRO") or "").strip() - if not tipoususispro: - tipousu = str(h.get("TIPOUSU") or "").strip() - tipoususispro = _TIPOUSU_MAP.get(tipousu, "12" if es_particular else "11") + if es_particular: + tipoususispro = "12" + else: + tipoususispro = str(h.get("TIPOUSUSISPRO") or "").strip() + if not tipoususispro: + tipousu = str(h.get("TIPOUSU") or "").strip() + tipoususispro = _TIPOUSU_MAP.get(tipousu, "11") # viaIngreso y modalidadAtencion no existen en RECEPCION → valores fijos de laboratorio via_ingreso = "01" # Demanda espontánea (pacientes llegan directo al lab) diff --git a/app/templates/automation.html b/app/templates/automation.html index 71e5e0e..196a7da 100644 --- a/app/templates/automation.html +++ b/app/templates/automation.html @@ -21,6 +21,11 @@ +
+ + +