Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
316 lines
11 KiB
Python
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()
|