Commit Graph
84 Commits
Author SHA1 Message Date
Lizandro GuarnizoandClaude Sonnet 4.6 7ac17e29a9 fix: remove duplicate agrupar_por_factura that was overriding the correct one
The second definition (for RIPS 2.0 transacción grouping) was never called
but silently overrode the CMXC ventas grouper, causing grupos_vta to always
be empty (values were dicts, not lists, so isinstance(v, list) was False).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-28 15:45:56 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 3a0964e4ec fix: generar_factura_venta uses PREFIJO from row, not config default
codigoPrefijo in ventas JSON must be the actual Firebird PREFIJO
(e.g. "CMXC"), not the prefijo_tns_default config value which is
intended for RDA/transaccion. Falls back to default_prefijo if row
has no PREFIJO.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-28 09:59:40 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 408325192a fix: use FECHA_RECEPCION for exam query in scheduler, not HORAINICIORECEPCION
HORAINICIORECEPCION is a TIME field that can be NULL or outside the
2-minute window even when FECHA_RECEPCION matches, causing patients
to sync without their exams. Using the same FECHA_RECEPCION filter
for both queries ensures consistency.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-28 09:32:42 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 7f2dffc63e fix: group ventas by NUM_FACTURA, not IDRECEPCION
A single CMXC invoice (e.g. CMXC-307) can have multiple receptions.
Previously each reception was sent as a separate ventas, causing TNS
to accept the first and reject the rest with "ya existe" — leaving
only 1/N of the services registered.

Now agrupar_por_factura groups all receptions sharing the same
NUM_FACTURA into one ventas JSON with all their services in
detallePedido. One factura = one send.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-27 17:36:21 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 02b0defef5 fix: ventas CMXC usan NIT_EMPRESA como codTercero en lugar del código de paciente
Las facturas de venta de contratos externos (ej. CMXC) deben ir a nombre
de la empresa referente (NIT sin dígito de verificación), no del paciente.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-24 19:08:02 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 78e9c31d90 fix: usar FECHA_RECEPCION en SQL del scheduler en lugar de filtro Python por HORAINICIORECEPCION
El filtro Python era frágil: si HORAINICIORECEPCION era NULL o tenía formato inesperado, los pacientes no se capturaban. Ahora el filtro de ventana de tiempo va directo en Firebird usando DATEADD sobre FECHA_RECEPCION, que siempre está poblado.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-23 11:38:11 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 cffeae7e94 fix: parallelize WA sync and surface scheduler errors in historial
- whatsapp_sync: send patients concurrently (asyncio.gather + semaphore=10) instead of sequentially — eliminates long waits with many patients
- envios_erp: auto-refresh historial every 60s when scheduler tab is active
- main: wrap scheduler job to catch exceptions and log them to sync_wa_log so failures are visible in historial

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-23 11:34:30 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 03655427a5 fix(rips): discapacidad siempre 08, diagnostico default Z017 si vacío
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-22 22:33:59 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 0a234f056c fix: execute_query now handles positional tuple params directly
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-21 16:26:22 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 329157ca7a fix: use DATEADD(MINUTE, -n, CURRENT_TIMESTAMP) for Firebird interval
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-21 16:24:09 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 04d2eb5563 fix: remove NUM_FACTURA > 0 filter — sync all receptions by time window
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-21 16:03:03 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 a880b7fb81 feat: filter exams by time window in SQL (last N minutes), default 10min
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-21 15:59:11 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 35859e8d75 fix: auto-load libfbclient on macOS from Firebird.framework
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-21 08:58:26 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 6cbf0732e6 feat: resolve exam prices via TARIFA+EMPRESA JOIN, show company name
- PRECIO now falls back to catalog price (TARIFA) when 0/null
- JOIN EMPRESA + EMPRESA_SUB + TARIFA in exam query
- NOM_EMPRESA propagated to log and historial display

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-18 14:58:10 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 6157d2c2f9 fix: show raw PHP response in error message, clean up errores column UI
- sync_paciente: catch JSONDecodeError separately and include HTTP status
  + first 300 chars of raw response so PHP errors are visible in the log
- envios_erp.html: Errores column shows count only (not all inline messages)
  — error detail is already visible in the expandable Ver N row

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-17 09:57:45 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 cd63683869 feat: enrich sync log with exams, diagnosis, EPS and valor per patient
- scheduler.py: after sync_todos(), inject diagnostico, nit_empresa,
  valor_total and examenes_det (cups/nombre/precio) into each detalle entry
- whatsapp_sync.py: guardar_sync_log stores full enriched fields in detalle_json
- envios_erp.html: expanded row shows per-patient card with Dx, EPS, valor
  and a CUPS/examen/precio table — scrollable, max-h-96

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-17 09:52:08 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 e044f21e3e feat: store and display per-patient sync detail in historial
- Add detalle_json column to sync_wa_log via migration
- guardar_sync_log() now stores doc, nombre, action, examenes count per patient
- Historial table shows "Ver N" button that expands a row with patient list,
  action badge (Nuevo/Actualizado/Omitido) and exam count per patient

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-17 09:33:46 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 29b6aa4016 fix: pass user_id=None in scheduler sync log to avoid FK constraint
sync_wa_log.user_id has a FK to users; passing 0 fails since no user
with id=0 exists. Scheduler runs without a user context, so use NULL.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-17 09:25:17 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 863e012c12 fix: handle Firebird Decimal and datetime types in JSON serialization
- PRECIO and VALORTOTAL are Decimal from fdb driver; convert via _safe_num()
- HORAINICIORECEPCION is a datetime object; use _fmt_hora_str() for HH:MM:SS
- _parse_hora() now handles datetime objects directly (no str() roundtrip)
- Same fixes applied to pacientes.py exam builder
- sync-now endpoint catches exceptions and returns traceback as JSON

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-17 09:10:41 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 fc18acc761 fix: use CAST(FECHA_RECEPCION AS DATE) for Firebird timestamp comparison
FECHA_RECEPCION is a TIMESTAMP field, so comparing directly with
CURRENT_DATE (DATE) always returns 0 rows in Firebird. Cast to DATE first.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-16 21:45:25 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 dc18d128fb feat(envios/erp): add manual sync trigger endpoint and test UI
- POST /envios/erp/sync-now?ventana_min=N calls sync_recientes with configurable window
- sync_recientes() now returns a result dict (ok, total, created, updated, skipped, errores, detalle)
- ERP tab shows a "Prueba manual" card with ventana selector (2m / 30m / 2h / hoy) and result breakdown

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-16 21:40:54 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 8e27225326 feat: módulos de envío TNS/ERP Lab + sync de médico, empresa y valor total
- Hub /envios/tns con 5 sub-tabs (Terceros, Transacción RIPS, Facturas Venta, Prueba RDA, Automatización) via iframe lazy
- Hub /envios/erp con sub-tabs Pacientes y Sync Automático (historial scheduler)
- Modo embed (?embed=1) en base.html para cargar páginas sin sidebar/header dentro de iframes
- Sidebar simplificado: 6 ítems individuales reemplazados por módulos TNS y ERP Lab
- Scheduler y endpoint /pacientes/examenes ahora incluyen médico ordenante (DOCIDMEDICO), empresa/EPS (NIT_EMPRESA) y valor total (VALORTOTAL) desde Firebird

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-16 21:13:48 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 73062af8bc feat: paso 4 ventas en automation + fixes varios
- Automation: agrega Paso 4 (Ventas/Crear) con fetch _SQL_VENTAS,
  filtro por excluded_ventas_set, loop de envío y log de actividad
- Automation: _guardar_envio ahora almacena idrecepcion y contrato
  para que /ventas reconozca registros enviados por automation
- Contratos: corrige NameError en toggle_excluir (faltaba request: Request)
- Ventas: agrega check if not q en send_one y send para evitar TypeError
- Ventas HTML: agrega columna Contrato en la tabla de facturas
- json_generator: normaliza etnia >2 chars a '99' (ej: 999 → 99)
- json_generator: agrega campo ciudadExp usando el mismo cod_ciudad

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-11 14:29:39 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 6b84ecfd90 feat: módulo de facturas venta (Ventas/Crear) para contrato 040
- generar_factura_venta en json_generator.py con mapeo completo del JSON
- Queries default tipo 'ventas' (por fecha y por número) filtradas a contrato 040
- Ruta /ventas con preview, send-one y send masivo
- Template ventas.html con tabla, resumen y modal JSON
- Link en navegación lateral

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-11 11:16:42 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 6fb18a7af6 revert: especialidad siempre usa el default de config
Revertir el uso de COD_ESPECIALIDAD desde la BD; la especialidad
siempre debe venir del valor configurado (especialidad_default).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-11 10:23:33 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 3313337672 fix: usar COD_ESPECIALIDAD de la BD en detallePedido
La especialidad ahora toma el valor del campo COD_ESPECIALIDAD de la tabla
MEDICO (ya presente en todos los SELECTs), con fallback al default de config.
El profesional sigue viniendo de configuración en todos los flujos.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-11 10:19:42 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 2a7553267b Fix tipoDocumento PE/PT: map to single char 'E' for TNS API compatibility
TNS API (C#) expects System.Char for tipoDocumento; "PE" and "PT" caused
400 validation errors. Mapped both to "E" (Cédula Extranjería).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-11 08:31:33 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 9f2bd5164e feat: opcion 'sin contrato' para enviar numeroContrato null en TNS
- Agrega columna sin_contrato en tabla contratos (migration automatica)
- Contrato 040 marcado excluir=0, sin_contrato=1 en seed
- load_sin_contrato_set() retorna contratos que deben ir con null
- generar_rda_paciente acepta sin_contrato_set y fuerza numeroContrato=null
- Automation paso 2 y 3 pasan sin_contrato_set al generador
- Pagina contratos: columna y toggle "Sin contrato", checkbox en modal editar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-10 21:22:09 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 c2a092c0e7 feat: paso 3 pre-servicios RCXC/SC y limpieza de código copago
- Agrega paso 3 en automatización: envía RDA desde PRESSERV_DIAN (prefijos RCXC/SC) para pacientes EPS con PS_NUM IS NOT NULL
- Filtra paso 2 con PS_NUM IS NULL para separar particulares/empresas de EPS
- Elimina parámetro copago y función _calcular_descuento_pct (código muerto)
- Limpia referencia a d.copago en automation.html (UI paso 3)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-10 20:43:02 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 8e29e2d04f fix: tipoIngreso siempre "1" (ambulatorio) en todos los flujos
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-10 16:52:53 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 d9a414396f feat: campo descuento en RDA calculado desde VALORDESC/VALORTOTAL
descuento = round(VALORDESC * 100 / VALORTOTAL, 2), 0 si sin descuento

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-10 16:52:00 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 c5f84aa900 feat: numero de factura con ceros a la izquierda hasta 5 dígitos (zfill 5)
Ejemplo: 355 → 00355, 3726 → 03726
Aplica a todos los flujos: transacción, automation, send-direct, test-rda.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-10 16:08:38 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 d3a6ab7ab6 fix(tipousuario): contrato 012 no debe ser tratado como particular
El check `"012".lstrip("0") == "12"` causaba false positive marcando
contratos EPS como particulares. Ahora la heurística solo aplica si el
contrato no está en la tabla contratos; si está, el tipo_usuario del mapa
tiene precedencia y también controla es_particular.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 15:06:38 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 0f3a760de4 feat(contratos): tabla de contratos con tipo_usuario por contrato
Agrega tabla `contratos` en SQLite sembrada con los 17 contratos del
laboratorio. `generar_rda_paciente` consulta el mapa contrato→tipoUsuario
como primera prioridad antes de derivar de TIPOUSU/TIPOUSUSISPRO.
Incluye CRUD en /contratos con UI en sidebar.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 15:00:21 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 e2a9e7c4d3 fix(whatsapp_sync): enviar origen='lab' en lugar de 'rips'
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 11:35:39 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 b0175a2ca5 feat(sync-log): historial de sincronizaciones WhatsApp en sync_wa_log
- Nueva tabla sync_wa_log (total, created, skipped, updated, errores, origen, modo)
- Log guardado en /pacientes/sync-all, /terceros/send y /automation/run
- Ruta GET /pacientes/historial devuelve últimos 100 registros
- UI: tabla de historial con origen, modo, conteos y detalle de errores

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 11:13:28 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 1aab14f705 feat(pacientes): modo insertar en sync-all — solo crea nuevos, omite existentes
sync_todos() ahora acepta modo='insertar'|'upsert'. La ruta /pacientes/sync-all
usa insertar: skippea cédulas que ya están en lab_pacientes. Los hooks
automáticos (terceros/automation) siguen en upsert.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 11:06:14 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 e7c6bc794a feat(pacientes): sync de pacientes Firebird → WhatsApp Lab
- Nuevo servicio whatsapp_sync.py: mapeo de campos RIPS→lab_pacientes
  y cliente HTTP para /api/lab/ingest_paciente.php
- Nueva ruta /pacientes con sync masiva por rango de fechas o todos
- Hook en /terceros/send y /automation/run: sync silencioso a WhatsApp
  después de cada envío a TNS
- Config: campos whatsapp_url y whatsapp_api_key + botón probar conexión

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 10:49:00 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 a94e2cce9f feat: omit fechaAutorizacion and vigenciaAutorizacion when numeroAutorizacion is null
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 10:23:42 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 b88d727623 feat: add 5 minutes to fechaHoraEgreso in RDA
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-06 19:44:06 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 235e20e38d feat: remove unused fields from RDA JSON output
Remove codFormaPago, codBanco, descuento from header.
Remove tipoUnidad, descuento, porcentajeIva, impConsumo from detallePedido.
Remove detalleFormaPago entirely.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 21:59:02 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 3aa0e09aa6 chore: bump version marker in json_generator
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 09:31:24 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 72973973d4 feat: calculate real discount % from TARIFA vs RELACION.PRECIO
Joins TARIFA using EMPRESA.TARIFA to get the base price per exam,
then calculates descuento = round((sum(TARIFA) - sum(PRECIO)) / sum(TARIFA) * 100)
and sends it as integer percentage in the RDA header encabezado.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 17:26:49 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 15f6ca65db feat: add descuento from RECEPCION.VALORDESC to RDA header
Pulls r.VALORDESC in all SQL queries (automation, test_rda, queries
defaults) and maps it to the top-level descuento field in the
RdaPaciente JSON encabezado.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 17:13:46 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 68aa5e0355 feat: join ITEM on COD_PROTOCOLO to get CUPS_DETALLE as codigoMaterial
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 12:29:33 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 41ee88c1b4 fix: remove rel.DESCUENTO (column does not exist in RELACION table)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 12:26:09 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 d1c1d3205f revert: remove broken ITEM join until correct field name is known
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 12:24:01 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 ff08c1b3a8 feat: use ITEM.CUPS_DETALLE as codigoMaterial in RDA
LEFT JOIN ITEM on COD_EXAMEN to get CUPS_DETALLE. Falls back to
COD_EXAMEN if CUPS is empty. Applied to automation, test_rda, and
saved queries in SQLite.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 12:20:22 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 8732db9c1f fix: use Firebird PREFIJO as codigoPrefijo; add debug endpoint to find CUPs field
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 12:07:43 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 ed9fead353 fix: remove valor field from detallePedido; filter by PREFIJO in transaccion
- json_generator: remove valor entirely from detalle items (not even 0)
- transaccion: after fetching rows, filter by prefix if user typed full
  factura like "LHXC03726" and query returns PREFIJO column

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 12:00:42 -05:00