diff --git a/app/routes/automation.py b/app/routes/automation.py index 9b3b88f..3d1967b 100644 --- a/app/routes/automation.py +++ b/app/routes/automation.py @@ -1,26 +1,78 @@ import json as json_lib -import httpx -from datetime import datetime +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_terceros, generar_transaccion, agrupar_por_factura +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 +_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 CIUDAD c ON c.CODIGO = p.CIUDAD +WHERE r.FECHA_RECEPCION BETWEEN :fecha_ini AND :fecha_fin +""" + +# Query para obtener recepciones con exámenes por rango de fecha +_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.FECHA_REPORTADO, + m.COD_ESPECIALIDAD, + r.USUARIO AS profesional +FROM RECEPCION r +JOIN RELACION rel ON rel.IDRECEPCION = r.IDRECEPCION +LEFT JOIN MEDICO m ON m.CODIGO = r.COD_MEDICO +WHERE r.FECHA_RECEPCION BETWEEN :fecha_ini AND :fecha_fin +ORDER BY r.IDRECEPCION +""" + @router.get("") async def automation_page(request: Request, user: dict = Depends(get_current_user)): - conn = get_connection() - queries = conn.execute("SELECT * FROM queries ORDER BY query_type, name").fetchall() - configs = {row["key"]: row["value"] for row in conn.execute("SELECT * FROM config").fetchall()} - conn.close() - + today = date.today().isoformat() return request.app.state.templates.TemplateResponse("automation.html", { - "request": request, "user": user, - "queries": queries, "configs": configs, + "request": request, "user": user, "today": today, }) @@ -28,167 +80,134 @@ async def automation_page(request: Request, user: dict = Depends(get_current_use async def run_automation( request: Request, user: dict = Depends(get_current_user), - query_terceros_id: int = Form(...), - query_transaccion_id: int = Form(...), - fecha_inicio: str = Form(...), - fecha_fin: str = Form(...), - factura: str = Form(""), + fecha: str = Form(...), ): conn = get_connection() - q_terceros = conn.execute("SELECT * FROM queries WHERE id = ?", (query_terceros_id,)).fetchone() - q_trans = conn.execute("SELECT * FROM queries WHERE id = ?", (query_transaccion_id,)).fetchone() configs = {row["key"]: row["value"] for row in conn.execute("SELECT * FROM config").fetchall()} conn.close() - if not q_terceros or not q_trans: - return JSONResponse({"success": False, "message": "Consultas no encontradas"}) - fb, fb_ok, fb_msg = get_firebird_from_config(configs) if not fb_ok: return JSONResponse({"success": False, "message": f"Error Firebird: {fb_msg}"}) - api_url = configs.get("api_url", "") - api_key = configs.get("api_key", "") - api_method = configs.get("api_method", "POST") - headers = {"Content-Type": "application/json"} - if api_key: - headers["Authorization"] = f"Bearer {api_key}" + # 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} - resultado = {"paso1_terceros": {"status": "pendiente"}, "paso2_transaccion": {"status": "pendiente"}} - - # --------------------------------------------------------------- - # PASO 1: Enviar TERCEROS - # --------------------------------------------------------------- - params = {"fecha_ini": fecha_inicio, "fecha_fin": fecha_fin} - if ":factura" in q_terceros["query_text"] and factura: - params["factura"] = factura - if ":doc_num" in q_terceros["query_text"]: - params["doc_num"] = "" - - success, error, rows = fb.execute_query( - q_terceros["query_text"], - params if ":fecha_ini" in q_terceros["query_text"] else None - ) - - if not success: - resultado["paso1_terceros"] = {"status": "error", "message": error} - elif not rows: - resultado["paso1_terceros"] = {"status": "error", "message": "No hay pacientes para enviar"} - else: - terceros_enviados = 0 - terceros_errores = 0 - pacientes_enviados = [] - - async with httpx.AsyncClient(timeout=int(configs.get("api_timeout", 30))) as client: - for row in rows: - tercero_json = generar_terceros(row) - doc_id = tercero_json["numDocumentoIdentificacion"] - if doc_id in pacientes_enviados: - continue - pacientes_enviados.append(doc_id) - - resp_ok = False - resp_text = "" - try: - if api_method == "POST": - resp = await client.post(api_url + "/terceros", json=tercero_json, headers=headers) - else: - resp = await client.put(api_url + "/terceros", json=tercero_json, headers=headers) - resp_ok = resp.is_success - resp_text = resp.text[:1000] - if resp_ok: - terceros_enviados += 1 - else: - terceros_errores += 1 - except Exception as e: - terceros_errores += 1 - resp_text = str(e) - - conn = get_connection() - conn.execute(""" - INSERT INTO envios (user_id, tipo, factura, status, json_enviado, respuesta_api, created_at) - VALUES (?, ?, ?, ?, ?, ?, ?) - """, ( - user["user_id"], "terceros", factura or "AUTO", - "success" if resp_ok else "error", - json_lib.dumps(tercero_json, indent=2, ensure_ascii=False), - resp_text, - datetime.now().isoformat(), - )) - conn.commit() - conn.close() - - resultado["paso1_terceros"] = { - "status": "success" if terceros_errores == 0 else "partial", - "enviados": terceros_enviados, - "errores": terceros_errores, - } - - # --------------------------------------------------------------- - # PASO 2: Enviar TRANSACCION - # --------------------------------------------------------------- - params = {"fecha_ini": fecha_inicio, "fecha_fin": fecha_fin} - if ":factura" in q_trans["query_text"] and factura: - params["factura"] = factura - - success, error, rows = fb.execute_query(q_trans["query_text"], params) - - if not success: - resultado["paso2_transaccion"] = {"status": "error", "message": error} - elif not rows: - resultado["paso2_transaccion"] = {"status": "error", "message": "No hay servicios para enviar"} - else: - grupos = agrupar_por_factura(rows, factura) - trans_enviados = 0 - trans_errores = 0 - - async with httpx.AsyncClient(timeout=int(configs.get("api_timeout", 30))) as client: - for (fact, doc_key), grupo in grupos.items(): - paciente_data = {"tipoDocumentoIdentificacion": doc_key[0], "numDocumentoIdentificacion": doc_key[1]} - trans_json = generar_transaccion( - fact, configs.get("num_documento_obligado", ""), - paciente_data, grupo["procedimientos"], - ) - - status_ok = False - resp_text = "" - try: - if api_method == "POST": - resp = await client.post(api_url + "/transaccion", json=trans_json, headers=headers) - else: - resp = await client.put(api_url + "/transaccion", json=trans_json, headers=headers) - status_ok = resp.is_success - resp_text = resp.text[:1000] - except Exception as e: - resp_text = str(e) - - if status_ok: - trans_enviados += 1 - else: - trans_errores += 1 - - 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["user_id"], "transaccion", fact, - fecha_inicio, fecha_fin, - 1, len(grupo["procedimientos"]), - "success" if status_ok else "error", - json_lib.dumps(trans_json, indent=2, ensure_ascii=False)[:5000], - resp_text, - datetime.now().isoformat(), - )) - conn.commit() - conn.close() - - resultado["paso2_transaccion"] = { - "status": "success" if trans_errores == 0 else "partial", - "enviados": trans_enviados, - "errores": trans_errores, - } + # ── Paso 1: obtener pacientes únicos ───────────────────────────────────── + ok1, err1, rows_pac = fb.execute_query(_SQL_PACIENTES, 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" + if api_sucursal: + endpoint += f"?codigosucursal={api_sucursal}" + + async with httpx.AsyncClient(timeout=timeout) as client: + for id_recepcion, grupo_rows in grupos.items(): + rda_json = generar_rda_paciente(grupo_rows) + factura = str(grupo_rows[0].get("NUM_FACTURA", 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() diff --git a/app/templates/automation.html b/app/templates/automation.html index 9229bfc..4cfe87f 100644 --- a/app/templates/automation.html +++ b/app/templates/automation.html @@ -1,163 +1,152 @@ {% extends "base.html" %} -{% block title %}Automatización{% endblock %} -{% block header %}Automatización Completa{% endblock %} +{% block title %}Envío Masivo{% endblock %} +{% block header %}Envío Masivo a TNS{% endblock %} {% block content %} -
+
+ + +
+
+

+ Envío automático por fecha +

+

+ Selecciona una fecha, el sistema enviará todos los pacientes y sus RDA a TNS. +

+
+
+
+
+ + +
+ +
+
+
+ + +