Files
rips_manager/app/routes/automation.py
T

316 lines
11 KiB
Python

import json as json_lib
from datetime import datetime, date
from fastapi import APIRouter, Request, Form, 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="/automation", tags=["automation"])
# Query para obtener pacientes únicos por rango de fecha + contrato
_SQL_PACIENTES = """
SELECT DISTINCT
p.CODIGO,
p.TIPOIDENT,
p.DOCIDENT,
p.NOMBRES,
p.APELLIDOS,
p.DIRECCION,
p.CIUDAD AS COD_CIUDAD,
c.NOMBRE AS NOM_CIUDAD,
p.TELEFONOS,
p.EMAIL,
p.F_NACIMIENTO,
p.SEXO,
p.TIPORES,
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 + contrato
_SQL_RDA = """
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.DESCUENTO,
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 r.FECHA_RECEPCION BETWEEN :fecha_ini AND :fecha_fin
AND r.NUM_FACTURA > 0
{filtro_contrato_rda}
ORDER BY r.IDRECEPCION
"""
@router.get("")
async def automation_page(request: Request, user: dict = Depends(get_current_user)):
today = date.today().isoformat()
return request.app.state.templates.TemplateResponse("automation.html", {
"request": request, "user": user, "today": today,
})
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()}
conn.close()
fb, fb_ok, fb_msg = get_firebird_from_config(configs)
if not fb_ok:
return JSONResponse({"success": False, "message": f"Error Firebird: {fb_msg}"})
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())
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)
fb.disconnect()
if not ok2:
return JSONResponse({"success": False, "message": f"Error BD RDA: {err2}"})
grupos = agrupar_por_recepcion(rows_rda)
total_examenes = sum(len(v) for v in grupos.values())
# Agrupar RDA por COD_PACIENTE
rda_por_pac = {}
for id_rec, filas in grupos.items():
cod = str(filas[0].get("COD_PACIENTE", ""))
if cod not in rda_por_pac:
rda_por_pac[cod] = []
rda_por_pac[cod].append({
"id_recepcion": id_rec,
"factura": f"{str(filas[0].get('PREFIJO', '')).strip()}-{filas[0].get('NUM_FACTURA', '')}",
"fecha": str(filas[0].get("FECHA_RECEPCION", ""))[:10],
"examenes": len(filas),
"valor": float(filas[0].get("VALORTOTAL") or 0),
"diag": str(filas[0].get("DIAG_PPAL") or ""),
})
pacientes_preview = [
{
"codigo": str(p.get("CODIGO", "")),
"doc": str(p.get("DOCIDENT", "")),
"nombre": f"{p.get('NOMBRES', '')} {p.get('APELLIDOS', '')}".strip(),
"tipo": str(p.get("TIPOIDENT", "")),
"rda": rda_por_pac.get(str(p.get("CODIGO", "")), []),
}
for p in rows_pac
]
return JSONResponse({
"success": True,
"fecha": fecha,
"pacientes": len(rows_pac),
"recepciones": len(grupos),
"examenes": total_examenes,
"pacientes_preview": pacientes_preview,
})
@router.post("/run")
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()}
conn.close()
fb, fb_ok, fb_msg = get_firebird_from_config(configs)
if not fb_ok:
return JSONResponse({"success": False, "message": f"Error Firebird: {fb_msg}"})
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_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)
fb.disconnect()
if not ok2:
return JSONResponse({"success": False, "message": f"Error Firebird RDA: {err2}"})
if not rows_pac and not rows_rda:
return JSONResponse({"success": False, "message": f"No hay datos para la fecha {fecha}"})
# ── Login TNS ─────────────────────────────────────────────────────────────
token, token_err = await get_tns_token(
configs.get("tns_empresa", ""),
configs.get("tns_usuario", ""),
configs.get("tns_password", ""),
)
if not token:
return JSONResponse({"success": False, "message": f"Error login TNS: {token_err}"})
headers = {"Content-Type": "application/json", "Authorization": f"Bearer {token}"}
api_sucursal = configs.get("api_sucursal", "")
timeout = int(configs.get("api_timeout", 30))
resultado = {
"fecha": fecha,
"paso1_terceros": {"enviados": 0, "errores": 0, "detalle": []},
"paso2_rda": {"enviados": 0, "errores": 0, "detalle": []},
}
# ── PASO 1: Enviar Terceros ───────────────────────────────────────────────
async with httpx.AsyncClient(timeout=timeout) as client:
for row in rows_pac:
tercero_json = generar_tercero_api(row)
doc = tercero_json["nit"]
nombre = tercero_json["nombre"]
try:
resp = await client.post(
f"{TNS_BASE}/v2/tablas/Tercero/Crear",
json=tercero_json, headers=headers,
)
ok = resp.is_success
msg = resp.json().get("message", "") if ok else resp.text[:200]
except Exception as e:
ok = False
msg = str(e)
if ok:
resultado["paso1_terceros"]["enviados"] += 1
else:
resultado["paso1_terceros"]["errores"] += 1
resultado["paso1_terceros"]["detalle"].append({
"doc": doc, "nombre": nombre, "ok": ok, "msg": msg,
})
_guardar_envio(user["user_id"], "terceros", fecha, tercero_json, msg, ok)
# ── PASO 2: Enviar RDA Paciente ───────────────────────────────────────────
grupos = agrupar_por_recepcion(rows_rda)
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():
num_fac = str(grupo_rows[0].get("NUM_FACTURA") or "").strip()
num_override = 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
msg = resp.json().get("message", "") if ok else resp.text[:200]
except Exception as e:
ok = False
msg = str(e)
if ok:
resultado["paso2_rda"]["enviados"] += 1
else:
resultado["paso2_rda"]["errores"] += 1
resultado["paso2_rda"]["detalle"].append({
"factura": factura,
"paciente": str(grupo_rows[0].get("COD_PACIENTE", "")),
"examenes": len(grupo_rows),
"ok": ok, "msg": msg,
})
_guardar_envio(user["user_id"], "transaccion", factura, rda_json, msg, ok,
fecha_inicio=fecha, fecha_fin=fecha,
servicios=len(grupo_rows))
return JSONResponse({"success": True, "resultado": resultado})
def _guardar_envio(user_id, tipo, factura, json_data, respuesta, ok,
fecha_inicio=None, fecha_fin=None, servicios=0):
conn = get_connection()
conn.execute("""
INSERT INTO envios (user_id, tipo, factura, fecha_inicio, fecha_fin,
pacientes_count, servicios_count, status, json_enviado, respuesta_api, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
user_id, tipo, factura, fecha_inicio, fecha_fin,
1, servicios,
"success" if ok else "error",
json_lib.dumps(json_data, indent=2, ensure_ascii=False)[:5000],
respuesta[:1000] if respuesta else "",
datetime.now().isoformat(),
))
conn.commit()
conn.close()