Commit Graph
870 Commits
Author SHA1 Message Date
Lizandro GuarnizoandClaude Sonnet 4.6 32c166360c add: runner para migración dispositivos token
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 11:06:11 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 046803bd15 feat(ingest_paciente): modo insertar/upsert — migración inicial no pisa existentes
Nuevo campo 'modo' en el body:
- "insertar": si la cédula ya existe devuelve skipped sin tocar la BD
- "upsert" (default): crea o actualiza como antes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 11:05:58 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 3204671edc fix(ingest_paciente): agregar CORS headers para preflight OPTIONS
Incluye Access-Control-Allow-Headers con X-Lab-Key para que el
navegador pueda hacer el preflight desde RIPS Manager.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 10:56:55 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 d5b6de1f46 turnero: restricción por token de navegador en lugar de IP pública
- Token UUID generado en localStorage del navegador, persistido como cookie
- turnero_dispositivos.token: nueva columna para identificar equipos
- recepcion.php y lugar.php: chequean token (cookie) primero, IP como fallback
- configuracion.php: banner con token del equipo actual + botón "Registrar este equipo"
- save_dispositivo.php: acepta campo token al crear/editar dispositivo
- Migración: 20260709_dispositivos_token.sql

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 10:56:53 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 9651f14302 feat(lab): endpoint server-to-server ingest_paciente para sync desde RIPS
Agrega /api/lab/ingest_paciente.php con autenticación por X-Lab-Key
(LAB_SYNC_KEY en config). Hace UPSERT por numero_documento sin requerir
sesión de admin — permite que RIPS Manager sincronice pacientes directo
a lab_pacientes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 10:48:50 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 1f45f58205 sidebar: mostrar IP del servidor (REMOTE_ADDR) y LAN separadas para diagnóstico
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 10:47:59 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 abe4ed4df1 sidebar: mejorar detección IP local con STUN y modal de ayuda si no detecta
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 10:45:33 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 8b3188b21b sidebar: detectar IP LAN local via WebRTC y mostrarla en el pie del menú
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 10:35:54 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 60d186253e sidebar: mostrar IP local del cliente al pie del menú
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 10:32:23 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 f5b38a331e turnero/configuracion: mostrar IP actual del cliente en sección dispositivos
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 10:27:47 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 9afadb9a6f turnero: chequeo IP en vivo en cada carga de página
En lugar de confiar solo en la sesión (seteada al login), recepcion.php
y lugar.php ahora consultan turnero_dispositivos por IP en cada request.
Esto cubre sesiones preexistentes y evita que aparezca el selector cuando
la IP ya tiene un lugar asignado.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 10:25:22 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 003d16b6a2 turnero: auto-redirigir a desk/lugar asignado sin mostrar selector
- authenticateUser incluye turnero_lugar_id en la sesión
- login.php redirige con desk_id/lugar_id si el usuario tiene lugar fijo (sin necesitar IP)
- recepcion.php redirige con desk_id si la sesión tiene turnero_lugar_id, evitando el modal de selección

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 10:17:48 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 f4da864db7 feat(turnero): toma prolongada secuencial + bloqueo muestras/cierre
- ver_formulario_enviado: auto-fill médico (nombres/especialidad/código) en datos_prefilled desde solicitud
- ver_formulario_enviado: canvases de toma progresiva bloqueados secuencialmente — solo el turno activo es firmable, los siguientes aparecen al expirar el countdown
- ver_formulario_enviado: countdown prominente con reloj grande, color dinámico, sonido (Web Audio) y vibración al llegar la hora
- lugar: bloquear cierre del modal durante toma progresiva activa (botón X deshabilitado hasta mp_completar)
- lugar: bloquear Finalizar atención si hay muestras pendientes sin decisión (recibida/rechazada)
- lugar: _syncBtnFinalizar combina consentimientos + muestras para el aviso y estado del botón
- lugar: restricción de usuario a lugar específico via turnero_lugar_id (admin_users) + bloqueo JS en confirmarLugar()
- migrations: turnero_lugar_id en admin_users para restringir bacteriólogos por usuario

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 21:16:07 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 0e116359f8 feat(recepcion): agregar método de pago Bono
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 18:46:04 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 8a44cb8bae feat(turnero): mostrar muestras pendientes de visitas anteriores en entrega
Cuando la solicitud tiene solo_muestras=1, get_consentimientos.php
busca turnero_muestras pendientes de otras solicitudes del mismo
paciente y las antepone en el array muestras con es_pendiente_anterior=1.

lugar.php renderiza un tag 'visita anterior' en cada muestra previa
para que el bacteriólogo identifique el origen.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 18:42:19 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 6f1e2adafe feat(turnero): mostrar nota de recepción en toma de muestras
El campo observaciones de la solicitud ya venía en el JSON de
get_consentimientos.php pero nunca se renderizaba en lugar.php.
Se agrega un bloque amber visible solo cuando sol.observaciones
tiene contenido, y se limpia al hacer resetFicha.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 18:13:58 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 71e97f2d95 fix(turnero): asignar recepcion_desk_id al llamar turno específico
llamarTurnoEspecifico usaba cambiar_estado.php sin pasar desk_id, por
lo que recepcion_desk_id quedaba NULL. El display TV filtra por
recepcion_desk_id = ?, así el turno llamado no aparecía en pantalla.

También se corrige llamado_recepcion_at: era COALESCE (no actualizaba
si ya tenía valor), ahora es NOW() para que el display reaccione al
re-llamar.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 16:07:26 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 bfab8418b6 fix(turnero): restaurar consentimientos en recepción
El else branch de 3062cd7 marcaba todos los formularios de lugar con
origen_lugar_id=-1 cuando lugar_destino_id es NULL, ocultándolos en
recepción. Como create_solicitud ya no crea consentimientos de lugar,
el else es innecesario — en recepción fidsDeLugar queda vacío y los
consentimientos de examen pasan el filtro !c.origen_lugar_id.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 15:20:24 -05:00
Lizandro Guarnizo bb02f3d6f1 fix: migration uses id=10, not codigo (column doesn't exist) 2026-07-08 10:26:46 -05:00
Lizandro Guarnizo 08ec1eda14 fix: allow explicit es_toma_progresiva override per form; F-LAB-28 forced off 2026-07-08 10:23:19 -05:00
Lizandro Guarnizo 2d3eebea4b temp: debug form schema without underscore prefix 2026-07-08 10:20:40 -05:00
Lizandro Guarnizo 392b8629f7 temp: debug scripts to inspect F-LAB-28 schema 2026-07-08 10:12:16 -05:00
Lizandro Guarnizo 726cbdd21a temp: debug script to inspect lab_formularios schema for F-LAB-28 2026-07-08 10:07:24 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 5109db4a4b feat: borrador de comentario en toma de muestras (localStorage)
Al escribir en el textarea de comentarios se guarda automáticamente
en localStorage con clave toma_draft_<turno_id>. Al abrir la ficha
del mismo turno el texto se restaura con borde amarillo de aviso.
Al guardar exitosamente o al hacer resetFicha se borra el draft.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 10:02:06 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 3062cd7e95 fix: no mostrar consentimientos de toma de muestras al guardar en recepción
create_solicitud.php: cambia de crear consents por lugar (toma de muestras)
a crear consents por examen (exam_tipo_consentimientos). Los de lugar se
crean cuando el paciente llega a la estación vía get_consentimientos.php.

get_consentimientos.php: cuando lugar_destino_id es NULL (turno en recepción),
busca TODOS los formularios de turnero_lugar_consentimientos y los marca con
origen_lugar_id = -1 (truthy), para que el filtro !c.origen_lugar_id de
recepcion.php los excluya correctamente aunque no haya destino asignado aún.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 09:23:54 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 7b4827f70f fix: filtrar por sesión del día al llamar siguiente turno
siguienteTurnoEnCola ahora acepta sesion_id opcional. llamar_turno.php
pasa la sesión de hoy para que nunca agarre turnos viejos de días
anteriores que quedaron en estado 'espera'.

Sin el filtro, el primer llamado del día podía agarrar un turno de ayer,
la ficha abría en recepción pero la pantalla TV (get_display_recepcion.php)
filtra por sesion_id=hoy y no lo encontraba → el número no salía en display.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 09:14:48 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 8aa2f35585 fix(turnero/chat): recepcionista siempre vuelve a recepción, no a dashboard
IP registrada → su desk; sin IP registrada → primer desk activo de recepción.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 08:56:57 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 1f2198225c fix(turnero): IP real con X-Forwarded-For; chat back solo para recepcionista
- App.php: clientIp() público resuelve X-Forwarded-For/X-Real-IP
- module.php: usa el mismo IP resolution en sidebar de recepcionista
- chat.php: botón Menú regresa al desk solo si rol=recepcionista con IP
  registrada; bacteriólogo no aplica lógica de IP en este flujo

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 08:53:42 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 6f56734b53 fix(turnero/chat): botón Menú regresa al desk asignado por IP
Si la IP está registrada en turnero_dispositivos para una recepción,
el botón volver apunta a ese desk en lugar de al dashboard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 08:48:55 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 985bb7eace fix(formulario): checkboxes en fila horizontal con flex-wrap
Los checkboxes del campo tipo checkbox quedaban apilados verticalmente.
Se envuelven en d-flex flex-wrap gap-2 para quedar lado a lado.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 08:47:14 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 ea1f0e30c9 feat(turnero): tomas de muestras prolongadas con temporizador y progreso
- DB: ALTER TABLE turnero_consentimientos ADD siguiente_toma_at, toma_inicio_at;
  MODIFY estado para incluir 'en_progreso'
- guardar_toma.php (nuevo): guarda firma parcial por campo_id, parsea el
  esquema para calcular siguiente_toma_at desde 'Minuto X' o hora fija,
  marca 'en_progreso' o 'firmado' según tomas restantes
- get_consentimientos.php: expone siguiente_toma_at, es_toma_progresiva,
  tomas_total, tomas_firmadas, campos_firma_pro en cada consentimiento
- lugar.php: estado en_progreso con badge X/Y, countdown en tiempo real,
  animación de alerta cuando el tiempo llega, botón 'Siguiente toma' que
  abre el modal embebido en modo toma_progresiva

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 08:42:43 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 cf8d011f09 feat(lab_examenes): múltiples consentimientos por examen
Mismo patrón que turnero/configuracion: form_ids[] en lugar de
formulario_id único, checkboxes en la vista, INSERT múltiple en save.php

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 08:19:06 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 b2bc0f950a feat(turnero): múltiples consentimientos por tipo de examen
- save_ex_tipo.php: GET devuelve form_ids[], POST acepta form_ids[] y
  hace DELETE+INSERT por cada uno (la tabla ya soportaba muchos a muchos)
- configuracion.php: reemplaza select único por checkboxes en formulario
  Agregar y modal Editar; JS lee los checks y envía form_ids[]

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 08:10:57 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 b6179574c3 feat(turnero): recepcionista con restricción de desk por IP
- module.php: si la IP está registrada en turnero_dispositivos para un
  lugar de tipo recepción, el sidebar muestra solo ese desk; resto de
  vistas (chat, verificar, TV) siempre visibles
- App.php: guard que redirige al recepcionista a su desk si intenta
  acceder manualmente a otro desk_id; IP libre no tiene restricción

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 08:03:48 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 b2e7130762 feat(turnero): bacteriólogo sin dashboard/historial, redirige por IP
- Auth: agrega isBacteriologo()
- App: guard que bloquea dashboard e historial para bacteriólogo y
  redirige según IP (turnero_dispositivos); IP registrada → ese lugar,
  IP libre → primer lugar de muestras
- module.php: sidebar bacteriólogo sin Dashboard/Historial; IP
  registrada muestra solo ese lugar, IP libre muestra TV + todos los
  lugares muestras

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 07:54:48 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 a776c5733c fix(turnero): origen_lugar_id calculado al vuelo, sin depender de columna DB
Elimina dependencia de tc.origen_lugar_id en SELECT; calcula el flag
dinámicamente consultando turnero_lugar_consentimientos contra el lugar
destino del turno. Funciona para registros históricos y sin migración.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 23:19:46 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 233144c017 feat(usuarios): gestión de firma por profesional desde perfil de usuario
Botón firma en tabla de usuarios (verde si ya tiene, gris si no).
Modal con canvas para dibujar o subir imagen, vista previa de firma
actual y opción de eliminar. APIs get/save_firma_usuario.php para admins.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 23:16:26 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 bad3d42a92 feat(turnero): gestión de dispositivos/tablets en configuración
Sección nueva en tab Lugares con tabla CRUD de turnero_dispositivos:
IP, nombre, lugar asignado, estado activo/inactivo.
API save_dispositivo.php para crear/editar/eliminar.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 23:08:33 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 fb0e77523c feat: módulo lab_examenes — CRUD catálogo de exámenes, items y tarifas
- Nuevo módulo lab_examenes con lista paginada, buscador y filtro por categoría
- Vista de detalle/edición con campos completos de exam_tipos (CUPS, protocolo,
  tipo muestra, nivel, abreviatura, seremite, ayuno, instrucciones)
- Sub-tabla inline de items de resultado (lab_items_resultado) con edición por fila
- Sub-tabla de precios por tarifa (lab_tarifas) con modal de edición
- APIs: list, get, save, save_item, save_tarifa, get_tarifas
- Script ETL Firebird→MySQL (scripts/etl_examenes.php) para la migración inicial

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 23:06:54 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 229977785b fix(turnero): no mostrar consentimientos de toma de muestras en recepcion
Agrega origen_lugar_id a turnero_consentimientos para distinguir consents
creados por examen (null) vs por estación (= lugar_id destino).
Recepcion filtra los de origen_lugar_id != null; lugar.php muestra
solo los suyos + los de examen.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 23:05:51 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 333e856f38 feat(turnero): buscador y paginación en tab de examenes
Filtrado en tiempo real + paginación client-side (15 por página).
Categorías vacías se ocultan automáticamente al buscar.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 22:53:16 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 18d50a3a16 fix: evitar datos del paciente duplicados en formularios con campos linked
Cuando el esquema tiene campos tipo linked que ya muestran nombre, documento,
etc., el bloque top Datos del paciente queda oculto para no repetir la info.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 22:51:09 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 d24b234e30 fix(display): corregir turnos que no aparecen en pantalla TV
- sse_turno.php: sleep(2)→sleep(1) para detectar notificarSSE en máx 1s
- display*.php: visibilitychange + focus → fetch inmediato al despertar pantalla
- display*.php: Wake Lock API para evitar throttling del browser en TVs
- display.php: SSE open → cargarSnapshot() inmediato al reconectar

Causa raíz: browsers throttlean setInterval a >1min en tabs sin foco;
las TVs inactivas dejaban de recibir actualizaciones aunque el servidor
las enviara correctamente.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 22:47:13 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 8760b685ad feat(turnero): dispositivos por IP, firmas, formularios y muestras prolongadas
- login.php: detectar dispositivo por IP al autenticar y redirigir al lugar asignado
- dashboard.php / lugar.php / recepcion.php: bloquear acceso por dispositivo (IP→lugar)
- lugar.php: modal firma profesional pre-guardada; compact 2-col en iframe; botón WA
  solo cuando el formulario requiere firma del paciente
- ver_formulario_enviado.php: modo compact tablet, auto-save borrador (merge), firma
  profesional en 1 clic, Muestras Prolongadas (countdown + Finalizar), auto_profesional
  pre-fill desde sesión, fecha→fecha_hoy en campos de toma/recepción
- get_consentimientos.php: añadir requiere_firma_paciente para controlar botón WA
- save_firma_profesional.php: API para guardar firma pre-configurada del profesional
- migrations: turnero_dispositivos, admin_users.firma_svg, clon VIH F-LAB-05 para turnero

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 21:58:52 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 4de9c2f9b4 turnero: simplificar URLs de pantalla TV y añadir URLs de gestión por lugar
- Quita pantallas individuales por estación de muestras y kiosko del bloque anterior
- Deja solo Pantalla Global y Kiosko como accesos de visualización
- Añade bloque de URLs de gestión por escritorio de Recepción (recepcion&desk_id=X)
- Añade bloque de URLs de gestión por estación de Muestras (lugar&lugar_id=X)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 10:34:32 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 27c1ac7c5d chore: quitar display_errors de depuración
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-06 09:02:18 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 a9f2528a2d fix: agregar columna numero_orden a lab_domicilios y turnero_solicitudes
La columna se usaba en ver_formulario_enviado.php pero no existía
en la BD, causando error 500.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-06 08:54:26 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 30a146ad5c debug: habilitar display_errors temporalmente para diagnosticar 500
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-06 08:52:17 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 f695b04f5b feat(caja): generar lab_recepciones al confirmar solicitud en turnero
- create_solicitud.php crea lab_recepciones (es_historico=0) dentro de
  la transacción: num_factura auto-secuencial para registros nuevos,
  campos empresa/subgrupo/autorización/diagnóstico tomados de la solicitud
- Inserta lab_relaciones por cada examen con precio del snapshot JSON
- Inserta lab_pagos + lab_pagos_det si hay metodo_pago registrado
- Bloque try/catch: si migración 11 aún no corrió solo se loguea,
  no se revierte la solicitud principal
- recepcion.php muestra toast "Recepción #N generada" al guardar
- Migración 11: ALTER lab_recepciones.id → AUTO_INCREMENT,
  ADD solicitud_id, ALTER lab_pagos.numcaja_legacy → nullable

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 22:12:48 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 a7aac706ee feat(recepcion): motor de precios + selector empresa/convenio
- Nueva sección empresa/convenio en recepcion.php: búsqueda live por
  nombre/NIT, selector de subgrupo, campo autorización (cuando req_autoriza),
  diagnóstico CIE-10
- Tabla de precios calculados en tiempo real: resolución por tarifa de la
  empresa o subgrupo, descuento empresa aplicado, botón "Aplicar al cobro"
- Al agregar/quitar exámenes o cambiar empresa/subgrupo se recalcula automáticamente
- Nuevo API get_precios_examenes.php: resuelve precios desde lab_tarifas
  con soporte de tarifas derivadas por porcentaje
- create_solicitud.php guarda nit_empresa, subgrupo_id, autorizacion,
  diag_ppal, items_precio (snapshot JSON al momento de la recepción)
- Migración 10: ALTER turnero_solicitudes añade los 5 campos nuevos

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 22:09:26 -05:00