- 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>
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>
- 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>
- json_generator: send valor:0 per item instead of PRECIO from Firebird
- test_rda + transaccion: accept "LHXC03726" format — extracts numeric part
for NUM_FACTURA and letter prefix for PREFIJO filter when prefix present
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
HORAINICIORECEPCION es campo TIME en Firebird — fdb lo devuelve como
datetime.time sin fecha. _fmt_datetime recibía '06:08:29' y lo pasaba
tal cual al JSON. TNS rechazaba por formato inválido.
- _fmt_datetime ahora maneja entrada solo-fecha → agrega 00:00:00
- generar_rda_paciente combina HORAINICIORECEPCION + FECHA_RECEPCION
antes de llamar _fmt_datetime cuando detecta hora sin fecha
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds _clean_times() that recursively walks every string in the result
dict and applies re.sub(r'\s*:\s*', ':'), guaranteeing no "12: 47: 37"
spaces survive to the TNS API regardless of Firebird driver behaviour.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
re.sub('\s*:\s*', ':') captura cualquier tipo de espacio alrededor
de los dos puntos, incluyendo tipos no-ASCII que .replace() no atrapa.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
La documentacion marca CodFormaPago como Requerido=Si. Su ausencia
causaba 'Datos incorrectos' en TNS.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Elimina codFormaPago del header RDA (no va segun JSON de referencia)
- fechaAutorizacion usa fecha de hoy, no la de recepcion
- descuento toma rel.DESCUENTO de Firebird en vez de hardcodeado 0
- Agrega rel.DESCUENTO al SELECT en test_rda y automation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Usa strftime en vez de isoformat y aplica re.sub para eliminar
espacios alrededor de los dos puntos en strings de tiempo de fdb.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>