From 6d5a85c55f3d9cc47f05397e214c25cbd9312f0c Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Thu, 25 Jun 2026 18:56:31 -0500 Subject: [PATCH] =?UTF-8?q?feat:=20integraci=C3=B3n=20completa=20con=20API?= =?UTF-8?q?=20TNS=20v2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - api_client: agrega get_tns_token() — login en /v2/Acceso/Login y retorna JWT - json_generator: campos en camelCase según swagger v2; agrega nomRegTri y email placeholder cuando el paciente no tiene correo - terceros/transaccion routes: obtienen token TNS antes de cada envío, usan endpoints /v2/tablas/Tercero/Crear y /v2/rda/RdaPaciente/Insertar - config: agrega tns_empresa, tns_usuario, tns_password Co-Authored-By: Claude Sonnet 4.6 --- app/routes/config.py | 3 + app/routes/terceros.py | 17 +++--- app/routes/transaccion.py | 20 ++++--- app/services/api_client.py | 20 ++++++- app/services/json_generator.py | 103 +++++++++++++++++---------------- rips_manager.db | Bin 40960 -> 40960 bytes 6 files changed, 96 insertions(+), 67 deletions(-) diff --git a/app/routes/config.py b/app/routes/config.py index 6eba0b0..13ae93d 100644 --- a/app/routes/config.py +++ b/app/routes/config.py @@ -17,6 +17,9 @@ DEFAULT_KEYS = [ ("api_timeout", "30"), ("api_version", "1"), ("api_sucursal", ""), + ("tns_empresa", "9002787299"), + ("tns_usuario", "DOCUXER"), + ("tns_password", "Nicolas2796*+"), ("num_documento_obligado", ""), ("cod_prestador", ""), ] diff --git a/app/routes/terceros.py b/app/routes/terceros.py index b8c041a..33871b1 100644 --- a/app/routes/terceros.py +++ b/app/routes/terceros.py @@ -7,6 +7,7 @@ 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 +from app.services.api_client import get_tns_token, TNS_BASE router = APIRouter(prefix="/terceros", tags=["terceros"]) @@ -137,14 +138,16 @@ async def send_terceros( tercero_json = generar_tercero_api(rows[0]) - api_url = configs.get("api_url", "") - api_version = configs.get("api_version", "1") - api_key = configs.get("api_key", "") - headers = {"Content-Type": "application/json"} - if api_key: - headers["Authorization"] = f"Bearer {api_key}" + 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}"}) - endpoint = f"{api_url}/v{api_version}/tablas/Tercero/Crear" + headers = {"Content-Type": "application/json", "Authorization": f"Bearer {token}"} + endpoint = f"{TNS_BASE}/v2/tablas/Tercero/Crear" resp_ok = False resp_text = "" diff --git a/app/routes/transaccion.py b/app/routes/transaccion.py index b0a5e8c..44ab0bf 100644 --- a/app/routes/transaccion.py +++ b/app/routes/transaccion.py @@ -7,6 +7,7 @@ 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_rda_paciente, agrupar_por_recepcion +from app.services.api_client import get_tns_token, TNS_BASE router = APIRouter(prefix="/transaccion", tags=["transaccion"]) @@ -116,15 +117,18 @@ async def send_transaccion( return JSONResponse({"success": False, "message": "No se encontraron datos"}) grupos = agrupar_por_recepcion(rows) - api_url = configs.get("api_url", "") - api_version = configs.get("api_version", "1") - api_sucursal = configs.get("api_sucursal", "") - api_key = configs.get("api_key", "") - headers = {"Content-Type": "application/json"} - if api_key: - headers["Authorization"] = f"Bearer {api_key}" - endpoint = f"{api_url}/v{api_version}/rda/RdaPaciente/Insertar" + 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", "") + endpoint = f"{TNS_BASE}/v2/rda/RdaPaciente/Insertar" if api_sucursal: endpoint += f"?codigosucursal={api_sucursal}" diff --git a/app/services/api_client.py b/app/services/api_client.py index 730770f..1f87d15 100644 --- a/app/services/api_client.py +++ b/app/services/api_client.py @@ -1,8 +1,26 @@ import httpx -import json from typing import Optional +TNS_BASE = "https://api.tns.co" + + +async def get_tns_token(empresa: str, usuario: str, password: str) -> tuple: + """Hace login en TNS v2 y devuelve (token, error_msg).""" + try: + async with httpx.AsyncClient(timeout=30) as client: + resp = await client.post( + f"{TNS_BASE}/v2/Acceso/Login", + json={"codigoEmpresa": empresa, "nombreUsuario": usuario, "contrasenia": password}, + ) + body = resp.json() + if resp.is_success and body.get("status"): + return body["data"], None + return None, body.get("message") or f"HTTP {resp.status_code}" + except Exception as e: + return None, str(e) + + async def send_json( url: str, json_data: dict, diff --git a/app/services/json_generator.py b/app/services/json_generator.py index 49d2203..5aeef09 100644 --- a/app/services/json_generator.py +++ b/app/services/json_generator.py @@ -52,34 +52,35 @@ def generar_tercero_api(row: dict) -> dict: zona = _ZONA_MAP.get(str(row.get("TIPORES") or "U").strip(), "01") return { - "Codigo": str(row.get("CODIGO") or "").strip(), - "NatJuridica": "N", - "TipoDocumento": tipo_doc, - "Nit": str(row.get("DOCIDENT") or "").strip(), - "Nombre": nombre_completo, - "Nombre1": nombres[0] if nombres else "", - "Nombre2": nombres[1] if len(nombres) > 1 else "", - "Apellido1": apellidos[0] if apellidos else "", - "Apellido2": apellidos[1] if len(apellidos) > 1 else "", - "Direccion": (row.get("DIRECCION") or "").strip(), - "CodigoCiudad": str(row.get("COD_CIUDAD") or "00").strip(), - "NombreCiudad": (row.get("NOM_CIUDAD") or "SIN CIUDAD").strip().upper(), - "Zona1": "00", - "Clasificacion": "00", - "Telefono": str(row.get("TELEFONOS") or "").strip(), - "Email": (row.get("EMAIL") or "").strip(), - "Inactivo": False, - "Privada": "N", - "Mixta": "N", - "CodigoBarrio": "00", - "Comision": 0, - "FechaNacimiento": _fmt_fecha(row.get("F_NACIMIENTO")), - "Sexo": str(row.get("SEXO") or "M").strip(), - "Zona": zona, - "Etnia": str(row.get("CODETNIA") or "99").strip(), - "Cliente": "S", - "EnfermedadCronica": False, - "NoEnviarMinSalud": False, + "codigo": str(row.get("CODIGO") or "").strip(), + "natJuridica": "N", + "tipoDocumento": tipo_doc, + "nit": str(row.get("DOCIDENT") or "").strip(), + "nombre": nombre_completo, + "nombre1": nombres[0] if nombres else "", + "nombre2": nombres[1] if len(nombres) > 1 else "", + "apellido1": apellidos[0] if apellidos else "", + "apellido2": apellidos[1] if len(apellidos) > 1 else "", + "direccion": (row.get("DIRECCION") or "").strip(), + "codigoCiudad": str(row.get("COD_CIUDAD") or "00").strip(), + "nombreCiudad": (row.get("NOM_CIUDAD") or "SIN CIUDAD").strip().upper(), + "zona1": "00", + "clasificacion": "00", + "nomRegTri": nombre_completo, + "telefono": str(row.get("TELEFONOS") or "").strip(), + "email": (row.get("EMAIL") or "").strip() or f"{str(row.get('DOCIDENT') or 'paciente').strip()}@sinregistro.co", + "inactivo": False, + "privada": "N", + "mixta": "N", + "codigoBarrio": "00", + "comision": 0, + "fechaNacimiento": _fmt_fecha(row.get("F_NACIMIENTO")), + "sexo": str(row.get("SEXO") or "M").strip(), + "zona": zona, + "etnia": str(row.get("CODETNIA") or "99").strip(), + "cliente": "S", + "enfermedadCronica": False, + "noEnviarMinSalud": False, } @@ -109,15 +110,15 @@ def generar_rda_paciente(rows: list) -> dict: else h.get("FECHA_RECEPCION") ) detalle_pedido.append({ - "CodigoMaterial": str(row.get("COD_EXAMEN") or "").strip(), - "CodigoBodega": "00", - "Cantidad": 1, - "TipoUnidad": "D", - "Valor": float(row.get("PRECIO") or 0), - "Descuento": 0, - "PorcentajeIva": 0, - "ImpConsumo": 0, - "Observacion": "", + "codigoMaterial": str(row.get("COD_EXAMEN") or "").strip(), + "codigoBodega": "00", + "cantidad": 1, + "tipoUnidad": "D", + "valor": float(row.get("PRECIO") or 0), + "descuento": 0, + "porcentajeIva": 0, + "impConsumo": 0, + "observacion": "", "profesional": str(h.get("profesional") or "").strip(), "especialidad": str(row.get("COD_ESPECIALIDAD") or "").strip(), "diagnosticoprincipal": str(h.get("DIAG_PPAL") or "").strip(), @@ -127,14 +128,14 @@ def generar_rda_paciente(rows: list) -> dict: autorizacion = str(h.get("AUTORIZACION") or "").strip() or None return { - "CodigoPrefijo": str(h.get("PREFIJO") or "00").strip(), - "Numero": "", - "Fecha": fecha, - "CodTercero": str(h.get("COD_PACIENTE") or "").strip(), - "CodVendedor": "00", - "CodFormaPago": "CO", - "CodBanco": "00", - "CodigoCentroCosto": "00", + "codigoPrefijo": str(h.get("PREFIJO") or "00").strip(), + "numero": "", + "fecha": fecha, + "codTercero": str(h.get("COD_PACIENTE") or "").strip(), + "codVendedor": "00", + "codFormaPago": "CO", + "codBanco": "00", + "codigoCentroCosto": "00", "tipoIngreso": str(h.get("CLASEPROC") or "1").strip() or "1", "fechaHoraIngreso": ingreso, "fechaHoraEgreso": egreso, @@ -146,12 +147,12 @@ def generar_rda_paciente(rows: list) -> dict: "esTerapia": False, "esProcedimiento": False, "numeroAutorizacion": autorizacion, - "DetallePedido": detalle_pedido, - "DetalleFormaPago": [{ - "CodigoFormaPago": "CO", - "PlazoDias": "0", - "FechaVencimiento": fecha, - "Valor": str(int(float(h.get("VALORTOTAL") or 0))), + "detallePedido": detalle_pedido, + "detalleFormaPago": [{ + "codigoFormaPago": "CO", + "plazoDias": "0", + "fechaVencimiento": fecha, + "valor": str(int(float(h.get("VALORTOTAL") or 0))), }], } diff --git a/rips_manager.db b/rips_manager.db index 2dc52c71f081f034ac5ed0e97373c5e33691d7dd..379d2d51b79f3be25a4157bbdd33d18efecc1a80 100644 GIT binary patch delta 263 zcmZoTz|?SnX~S24V*v&R1}-250$zT51_3?~M!r4#XZh;+JNPsB-TAfo<@mYz-t*nz z-_5_0{|R3@pT}lF0R=vDSpgOXRrQj*;`oBZ;^OlBq7=W(vX17M%VeNitoe`gzvrLNf0utNzb`)*-%GwZWdJd!M9l||Cs>*V@e