Acumula COD_PACIENTE en terceros_fallidos cuando paso1 falla.
En paso2 y paso3, si el paciente está en esa lista se omite el envío
con mensaje claro "Omitido: tercero no creado en TNS".
Evita el error confuso "paciente no existe" en TNS.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Agrega columna cedula a tabla envios (migración automática)
- _guardar_envio almacena la cédula en los 4 pasos de automation
- pac_map (COD_PACIENTE→DOCIDENT) para RDA/preserv/ventas
- logs.py busca por cedula LIKE en vez de idrecepcion/contrato
- logs.html etiqueta el input como "Cédula paciente"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
- POST /envios/erp/sync-diagnosticos: reads all 12422 diagnosticos from
Firebird, sends to WhatsApp in batches of 500 via ingest_diagnosticos.php
- envios_erp.html: new "Migración CIE-10" card with Migrar button and result
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Supports ?q=filter and ?limit=N. Returns total count, column names,
and list of {cod, concepto} rows — used to plan DIAGNOSTICO migration.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
- 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>
- 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>
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>
- 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>
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>
Shows recent recepciones without filters, CURRENT_DATE from Firebird,
and counts with/without NUM_FACTURA filter to diagnose zero-result sync.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
- 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>
No enviar facturas cuyo número sea 0 — quedarían como "00000" en TNS.
Filtro aplicado en preview, run y reenviar-venta.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Lista unificada reemplaza los dos paneles separados (failures/successes)
- Tabs de filtro: Errores (default si hay), Enviados, Todos — con contadores
- Filas de error: borde rojo, badge del paso, factura+paciente, mensaje inline, botón Reenviar
- Reenvío actualiza la fila en tiempo real: rojo → verde sin recarga
- btn-run solo se habilita si los pasos activos tienen datos reales
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 4 chips toggleables (Terceros/RDA/Pre-servicios/Ventas) que controlan
qué pasos se ejecutan en vista previa y en el envío masivo
- Backend: parámetro `pasos` en run_automation para saltarse pasos no
seleccionados; agrega `idrecepcion`/`codigo` a cada detalle item
- Nuevos endpoints POST /automation/reenviar-tercero, /reenviar-rda y
/reenviar-venta para reenvío individual con SQL BY_ID para cada tipo
- Panel de resultados rediseñado: barra de resumen por paso, sección
de fallos expandida con botón "Reenviar" por item (actualiza row in-
place), sección de exitosos colapsada por defecto
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Los campos excluir/sin_contrato/excluir_ventas solo se aplican en INSERT
(contratos nuevos del seed). En contratos existentes solo se actualizan
los datos descriptivos (nit, tipo_usuario, descripcion) para preservar
la configuracion que el usuario haya definido desde la UI.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
- Nueva columna excluir_ventas en tabla contratos (migración automática)
- load_excluded_ventas_set() para filtrar en /ventas
- Toggle independiente en /contratos para RDA y Ventas por separado
- Modal edición con checkbox excluir_ventas
- 040 marcado excluir_rda=1, excluir_ventas=0 en seed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Las tablas queries y envios tenían CHECK constraints que solo permitían
terceros/transaccion. Se agrega ventas al constraint vía migración automática
en _migrate para bases de datos existentes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
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>
preview_automation usaba _defecto en vez de _default, causando que
profesional y especialidad salieran null en la vista previa del automation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
- Add 'RDA Pre-servicios por fecha' and 'RDA Pre-servicios por número'
default queries joining PRESSERV_DIAN (ID_PS, PS_PREFIJO, PS_NUMERO)
- Refactor _query_rows: only pass params the query actually uses;
prefix filter works for both PREFIJO and PS_PREFIJO columns
- Add _is_preserv(), _agrupar(), _build_rda() helpers that detect
pre-servicio rows and produce correct prefijo/numero overrides
- All 4 send endpoints (preview, send-one, send, send-direct) now
group by ID_PS for pre-servicio queries and pass correct RDA fields
- RCXC prefix still maps to '00' in the RDA codigoPrefijo
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
POST /logs/reenviar/{id} re-sends stored JSON to TNS and updates
status to 'success' in-place if it succeeds. Button appears only
on error rows with saved JSON; updates row color and badge without reload.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All generar_rda_paciente calls in transaccion.py (4) and test_rda.py (2)
now pass sin_contrato_set so contract 040 sends numeroContrato: null
consistently across all send paths.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move _rdaJsonStore={} reset to before both sections so ps-json-* keys
are not wiped when pacientes section renders
- Replace divide-y wrapper divs with explicit border-b on each row,
eliminating duplicate/misplaced dividers in the pre-servicios list
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
RCXC is an internal Firebird prefix not recognized by TNS; map it to '00'
in the RDA JSON. SC prefix remains unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
- Paso 3 usaba tipo='preserv' que no esta en CHECK(tipo IN ('terceros','transaccion'))
- Cambia a 'transaccion' (factura RCXC-XXXXX distingue el registro)
- _guardar_envio con try/except para que error de DB no detenga el loop de envios
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat: paso 1 registra terceros EPS de pre-servicios junto a particulares
- _rdaJsonStore guarda JSON por key, toggleRdaJson usa textContent
- escHtml() para caracteres especiales en diagnostico
- _SQL_PACIENTES_EPS obtiene pacientes de PRESSERV_DIAN
- rows_pac_todos fusiona particulares + EPS deduplicando por CODIGO
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
- Agrega preservicios_preview al endpoint /preview con el JSON generado por cada RCXC/SC
- Muestra sección naranja debajo de pacientes con factura, fecha, paciente y exámenes
- Clic en cada fila despliega el JSON completo que se enviará a TNS
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
- Tabla activity_log en SQLite (migración automática)
- Registra: login, login_fallido, rda_enviado, rda_masivo, rda_directo,
automation_run, contrato_creado, contrato_editado, contrato_toggle
- Endpoint GET /logs/actividad con filtros por usuario, acción y fecha
- UI /logs/actividad con badges de color por tipo de acción
- Enlace "Actividad" en navegación lateral
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- candidatos: omite contratos con excluir=1 del listado
- preview: retorna flag excluido y pasa contrato_map (fix bug)
- enviar: bloquea si contrato excluido, pasa contrato_map al generar RDA
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Columna excluir en tabla contratos (migración automática)
- 25 contratos nuevos sembrados con excluir=1 (laboratorios externos, clínicas, médicos particulares)
- load_excluded_set() en contratos.py para consultar el set en runtime
- preview: marca items excluidos y los reporta en el conteo
- send/send-one/send-direct: bloquea envío si contrato está excluido
- automation preview+run: filtra excluidos antes del loop, reporta count
- UI /contratos: columna Envío TNS con toggle Activo/Excluido, fila en rojo si excluida
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Nuevo endpoint POST /transaccion/send-direct y panel "Enviar RDA directo"
en la UI: ingresa Tipo (prefijo) y Número, busca en Firebird y envía a TNS
sin necesidad de rango de fechas ni pasar por el flujo de preview.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
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>
Queries Firebird RECEPCION+RELACION+EXAMEN for today's date,
filters by HORAINICIORECEPCION >= now-5min in Python, returns
list of {recepcion_id, hora, cod_examen, nombre, cups, precio}.
Auth via X-Lab-Key header (whatsapp_api_key config value).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
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>
- 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>
The number regex /:(\s*)(\d+)/ was matching colons inside time strings
like "14:51:26" turning them into "14: 51: 26". Changed to require the
space that JSON.stringify always adds after key-value colons (": \d+")
so colons inside string values are not affected.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Applies _clean_times when serving json_enviado in /logs/detalle
so old records with "12: 47: 37" display correctly as "12:47:37".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
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>
ensure_defaults() runs on every startup and was overwriting the db
queries with the old versions from QUERY_DEFAULTS — wiping any manual
fixes. Now the source-of-truth in code has the EXAMEN join and CUPS
column, so transaccion page always shows CUPs after any restart.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>