- configurar_tomas.php: merge config por examen en lugar de overwrite
- ver_formulario_enviado.php: auto-abrir panel de tiempos al cargar; soporte formularios solo_profesional con botón Guardar y completar
- get_consentimientos.php: leer campo solo_profesional de lab_formularios
- recepcion.php: formularios sin firma paciente muestran badge "Completa en laboratorio"
- lugar.php: botón "Completar" para formularios solo_profesional
DB: ALTER TABLE lab_formularios ADD solo_profesional; UPDATE Rubeola id=8; UPDATE descripcion Niños kiosko
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DB: columna muestra_espera_at en turnero_turnos
- Panel 'Esperando siguiente muestra' en la cola de lugar.php
· Muestra pacientes en espera con countdown a siguiente_toma_at
· Botón 'Llamar' los devuelve a la cola activa
- Botón 'Mandar a espera' en la ficha (visible cuando toma progresiva activa)
· Desaparece de la cola principal, queda en sección de espera
- get_cola.php: excluye muestra_espera_at IS NOT NULL de cola y activo
· Agrega array en_espera_muestra en la respuesta
- Nuevas APIs: poner_en_espera_muestra.php, llamar_desde_espera.php
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Sección en la parte superior de lugar.php muestra pacientes esperando
en Ginecología y Pediatría (inicio_lugar_at IS NOT NULL, fin_lugar_at IS NULL)
- Chips de acceso rápido (botones) al tope de la cola para navegar directo
- Contadores de espera actualizados cada 30s
- Clicking en un paciente o "Ir →" navega a esa vista completa para atenderlo
- Solo aparece si existen esos lugares en DB y hay pacientes esperando
- Nuevo: get_especialidades.php para consultar pacientes de lugares especiales
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Indicadores por tipo: Recep. (azul) / Toma (verde) / Gral. (amarillo)
- Label de estado del servicio: En servicio / En espera / Finalizado / etc.
- Query agrega SUM por tipo de comentario sin subquery adicional
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reemplaza SELECT MAX() (vulnerable a concurrencia) por UPDATE atómico sobre
turnero_consecutivos. Dos recepcionistas simultáneas nunca pueden recibir
el mismo número de orden. Sin UNIQUE constraint, sin riesgo en datos históricos.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Reemplaza activos/cerrados por pendientes (por revisar) / vistos
- Columna bandeja_visto_at en turnero_turnos; API marcar_visto.php
- Botón "Marcar como visto" → mueve paciente a sección inferior
- Filtro en tiempo real por nombre y por código de examen (GROUP_CONCAT)
- URL de consentimientos: relativa (consent.php?token=...)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Vista split: lista ordenada por numero_orden (activos arriba, cerrados abajo),
stats bar con totales, panel de detalle con exámenes por categoría, muestras,
consentimientos, comentarios, PDF via window.print() y acción de cerrar turno.
Auto-refresh cada 30s. Visible para bacteriólogos y admins.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Agrega botón de cambio de estado en cada fila del historial de turnos.
Admins (sin role_id) pueden reabrir turnos finalizados/ausentes/cancelados.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- cambiar_estado: permite en_espera_lugar→en_recepcion y en_servicio→en_recepcion
- Al volver de un lugar limpia lugar_destino_id, llamado_lugar_at, inicio_lugar_at,
fin_recepcion_at y atendido_lugar_por para que quede reasignable
- lugar.php: botón "A recepción" visible en en_espera_lugar y en_servicio
- resetFicha oculta el botón nuevo al limpiar la ficha
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- cancelar_toma_pendiente: añade JOIN por paciente_id para prevenir IDOR
- cancelar_toma_pendiente: requiere turno_id para validar propiedad
- get_tomas_pendientes: porta filtros condicionales y _tomas_config de
guardar_toma.php para mostrar conteo de firmas correcto
- lugar.php: refresca sección tomas previas al completar via turneroFirmado
- lugar.php: muestra error en catch de cargarTomasPrevias en vez de silenciar
- lugar.php: limpia _tomaAlertados al cancelar toma previa
- lugar.php: pasa turno_id en POST de cancelar para ownership check
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cuando el paciente regresa con un turno nuevo, toma de muestras muestra
las tomas en_progreso de visitas anteriores con botón Continuar o Cancelar.
Cancelar marca estado='rechazado' (campo ya existía en el enum).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a patient selects 'Muestra pendiente' (prioridad F) at the kiosk,
the turn is immediately set to en_espera_lugar pointing to the first
active muestras location. Recepcion never sees it; toma de muestras
picks it up from its queue with no consent forms required.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Was sending 'consentimiento_turno' (plain text fallback) while the
configured template is 'consentimiento_turno_v2'. Now reads
turnero_wa_template and turnero_wa_lang from lab_config, matching
the same logic used in create_turno.php (kiosk).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sin el sort, Otro (2 min) aparecía después de Minuto 15 en schema order,
haciendo que el countdown arrancara con 15 min en vez de 2.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Chip "0 min" queda marcado por defecto al abrir configurar tiempos
- Si se selecciona "Otro", bloquea guardar hasta que se ingrese la hora
- La hora de "Otro" se guarda en datos_respuestas._otro_hora
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DB: columna genero_permitido en exam_tipos (M/F/NULL)
GX,FV → solo femenino | PSA,FPSA → solo masculino
- recepcion.php: TomSelect muestra badge rojo y deshabilita opciones
incompatibles al seleccionar paciente; elimina los ya agregados
- create_solicitud.php: validación backend 422 si hay conflicto género/examen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Al guardar una solicitud en recepción, escribe los exámenes en la bandeja
temporal WhatsApp (rips_examenes_pendientes) con DELETE+INSERT del día.
Al volver a seleccionar el mismo paciente, get_examenes_rips los detecta
como fuente=cache y los auto-carga sin confirmación.
También fix en get_examenes_rips: exámenes con codigo_legacy=NULL ahora
se buscan por codigo como fallback (COALESCE).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- buscar_cie10.php: busca por código o descripción, límite 12 resultados
- inp-diag: dropdown estilo empresa, Enter selecciona primero
- selectOnTab:false en TomSelect de exámenes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
El badge 1/9 ignoraba la configuración guardada e incluía todas las tomas
sin condición. Ahora filtra relevantIds por los firma-ids en _tomas_config,
igual que lo hace guardar_toma.php y el render PHP del formulario.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- create_turno.php: reemplaza http:// por https:// en el link enviado
por WhatsApp, evitando que Android CCT bloquee páginas HTTP
- ver_formulario_enviado.php: agrega botón "Abrir en navegador" en la
barra de acción cuando el modo es turnero, como fallback para Android
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- get_cola.php: quita orden por orden_peso, deja solo creado_at ASC
- recepcion.php: cards con prioridad distinta a E (general) muestran
borde izquierdo y fondo tintado con el color de la prioridad
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Campo inp-recibo aparece/desaparece al seleccionar 'Tarjeta' en forma de pago
- Se incluye en el payload como numero_recibo y se guarda en turnero_solicitudes
- Migración: ADD COLUMN numero_recibo VARCHAR(40) en turnero_solicitudes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Sin empresa: usa precio_base de exam_tipos (tarifa particular)
- Panel de precios se muestra con solo seleccionar exámenes (no requiere convenio)
- Total se aplica automáticamente al campo de cobro sin clic manual
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Panel ya no se renderiza server-side, sino que JS lo muestra al instante
cuando el usuario selecciona un tipo de examen del selector _c8j2g16
- _mpGetGroupsForExam() filtra los grupos correspondientes al examen elegido
- Si solo hay 1 toma por grupo, auto-guarda sin mostrar panel
- Si al cargar ya hay examen seleccionado y sin _tomas_config, muestra panel
- configurar_tomas.php: guarda también _c8j2g16 (exam_type) en datos_respuestas
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Feature 1 — Ciclos configurables por paciente:
- ver_formulario_enviado.php: pantalla de configuración aparece la primera
vez que el profesional abre el formulario (antes de firmar); permite
seleccionar qué tomas realizar para cada protocolo. $_mpSkippedSepIds
oculta las secciones no seleccionadas en render. $_mpMap se reconstruye
desde los grupos filtrados para que next_firma_id salte tomas omitidas.
- configurar_tomas.php (nuevo): guarda _tomas_config en datos_respuestas
del consentimiento; valida que cada grupo tenga al menos una toma.
- guardar_toma.php: aplica el filtro _tomas_config al array firmasCampos
para que el conteo de tomas firmadas/pendientes sea correcto.
Feature 2 — Gestión de tipos de examen (admin):
- lab_tomas_config.php (nuevo): página admin que muestra los exámenes
actuales con sus tomas y permite añadir nuevos tipos con su esquema
completo (separador + hora + resultado + firma_profesional por toma).
- api/lab/save_tomas_config.php (nuevo): modifica el esquema JSON del
formulario id=15; add_exam agrega opción al selector y los campos de
cada toma; delete_exam elimina la opción y los separadores exclusivos.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- guardar_toma.php: reconoce firma campo_id_svg (compat. registros históricos)
- get_consentimientos.php: mismo fix para conteo tomas_firmadas
- ver_formulario_enviado.php: $_mpMap incluye hora fija (Cortisol 3pm/4pm),
agrupa por condición de separador para no mezclar Glicemia/Cortisol,
muestra resumen de tomas al completar antes de cerrar el modal
- lugar.php: banner rojo + beep cuando el countdown de una toma llega a cero,
resetea alerta al cerrar el modal; función _playBeepLugar() compartida
DB Form 15: +10 campos (firma_profesional Minuto 60 Glicemia + campo
Resultado numérico en cada toma Glicemia y Cortisol)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- guardar_toma.php: filtrar firmas por condición del examen seleccionado; corrige
countdown espurio y detección de 'completado' cuando hay múltiples tipos de examen.
También descarta siguiente_toma_at si ya es pasado (minuto 0).
- ver_formulario_enviado.php: auto-scroll a primera firma pendiente al cargar el modal;
si el examen se selecciona y la firma no está en DOM, guarda y recarga el iframe.
Al completar tomas, cierra el modal directamente sin botón "Finalizar".
- get_cola.php: incluye siguiente_toma_at y en_toma_progresiva por turno desde
turnero_consentimientos para mostrar cuenta regresiva en tarjetas.
- lugar.php: muestra chip de cuenta regresiva en tarjetas de cola para pacientes
en toma progresiva; color cambia a warn (<2 min) y crit (¡Ahora!).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- get_consentimientos: tomas_total ahora filtra por examen elegido en
datos_respuestas (no cuenta todos los 13 campos globales); badge
se oculta (total=0) hasta que se seleccione un examen.
- ver_formulario_enviado: agrega f.es_toma_progresiva al SELECT y lo
usa para forzar _soloFirmaPro=true, evitando que aparezca el canvas
de firma del paciente cuando la DB indica toma progresiva.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Agrega medico_nombre al __paciente prefilled de crear_envio_presencial.php
haciendo JOIN con medicos vía turnero_solicitudes.medico_id, de modo que
el campo linked 'Médico solicitante' del formulario id=15 (tomas prolongadas)
se auto-cargue con el médico registrado en recepción.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- get_consentimientos: skip auto-create of consentimientos when solo_muestras=1
- get_consentimientos: add paciente_id to turno query; fallback muestras fetch
when no solicitud exists but turno has paciente_id (F-turns without reception)
- lugar: badge "Solo entrega de muestras" in ficha header for prioridad F
- lugar: hide sec-consent and sec-form-embebido when solo_muestras
- lugar: skip consent polling and form embedding for F-turns
- lugar: restore sec-consent visibility on resetFicha
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Before creating a new turn, check if the patient (identified by
paciente_id) already has a turn in today's session that is not
finalizado/ausente/cancelado. Returns 409 with the existing turn
code so the receptionist or kiosko can show a clear message.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
- 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>
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>
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>
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>
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>
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>
- 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>
- 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>
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>