21 documentos en cuatro secciones, escritos sobre el comportamiento real del sistema —incluidos los casos que costaron diagnosticar esta semana. Manual de usuario (visible para todos): primeros pasos, recepción, toma de muestras, portal del enfermero y administración. Orientado a tareas concretas, no a describir pantallas. Documentación técnica: índice de módulos, turnero, formularios y firma digital, WhatsApp y bot, domicilios, webhook (migrado de WEBHOOK_ENDPOINTS.md) e inventario de endpoints. Arquitectura: visión general, enrutamiento y registro de módulos, roles y permisos, modelo de datos, integración con WhatsApp, y decisiones tomadas con su deuda técnica asociada. Operación: runbook de incidentes ordenado por síntoma, configuraciones críticas —incluido qué vive en Meta y no en la base— y despliegue. Se documentan explícitamente las trampas conocidas: role/role_id que hay que mantener sincronizados, las columnas can_* que el control de acceso no lee, las URL de plantilla que no se cambian desde el código, y las columnas históricas que quedaron en NULL sin forma de recuperarlas. README_DOCS.md apunta al módulo y explica cómo agregar páginas. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4.3 KiB
Formularios y firma digital
Cómo se definen los formularios, cómo se envían y cómo se firman. Es transversal: lo usan el turnero, los domicilios y los envíos sueltos.
Definición
Un formulario es una fila en lab_formularios. Su estructura está en la columna esquema, un JSON con la lista de campos:
[
{"id": "_sep1", "tipo": "separador", "label": "Datos del paciente"},
{"id": "_nom", "tipo": "linked", "linked_key": "nombre_completo", "label": "Nombre"},
{"id": "_sint", "tipo": "checkbox", "label": "Síntomas", "options": ["Fiebre", "Tos"]},
{"id": "_fir", "tipo": "firma_profesional", "label": "Firma del profesional"}
]
Tipos de campo
| Tipo | Qué es |
|---|---|
separador |
Encabezado de sección; admite condicion |
parrafo |
Texto fijo (consentimientos, notas legales) |
texto, textarea, numero |
Entrada libre |
fecha, fecha_hoy, hora |
Fechas y horas |
radio, checkbox, select |
Opciones |
linked |
Se autocompleta con un dato del paciente vía linked_key |
firma |
Firma del paciente |
firma_profesional |
Firma del profesional |
Secciones condicionales
Un separador puede depender de otro campo:
{"id": "_sep_ins", "tipo": "separador", "label": "Insulina · Minuto 0",
"condicion": {"campo_id": "_examen", "valores": ["Insulina"]}}
La sección y todos sus campos se ocultan si la condición no se cumple. Los campos heredan el estado mediante el atributo data-sep-id.
Las dos vías de envío
Un mismo formulario se firma por dos caminos, con tablas y tokens distintos:
| Vía | Tabla | Token | Respuestas |
|---|---|---|---|
| Turnero | turnero_consentimientos |
UUID → ?token= |
datos_respuestas |
| Domicilios y envíos | lab_form_envios |
64 hex → ?t= |
datos_cliente |
Las dos las muestra ver_formulario_enviado.php, que distingue por el formato del token. De ahí que existan dos parámetros para lo que parece lo mismo.
form_cliente.php detecta tokens con formato UUID y redirige a ver_formulario_enviado.php — necesario porque la plantilla de WhatsApp aprobada en Meta apunta a la primera página.
Parámetros del visor
| Parámetro | Efecto |
|---|---|
token |
Consentimiento del turnero (UUID) |
t |
Envío de formulario (64 hex) |
id |
Acceso interno con sesión |
embed=1 |
Modo embebido; activa el filtrado de secciones |
compact=1 |
Grilla de campos y tarjetas de toma |
zoom |
Escala |
autoprint=1 |
Abre el diálogo de impresión |
embed=1no es cosmético: sin él no se aplica el filtrado de secciones de tomas prolongadas y el documento muestra secciones que no corresponden. Bandeja e historial lo pasan siempre.
Firma
Del paciente
Se dibuja en un canvas y se guarda como imagen en datos_cliente['<campo>_svg']. También se admite pad biométrico Topaz.
Del profesional
Se dibuja igual, pero además queda quién firmó. Hay tres endpoints según el contexto:
| Endpoint | Contexto |
|---|---|
modules/turnero/api/guardar_toma.php |
Tomas prolongadas — una firma por toma |
modules/turnero/api/firmar_profesional_consentimiento.php |
Consentimientos del turnero |
api/lab/firmar_profesional.php |
Envíos de formularios |
La identidad se guarda como _pro_nombre y _pro_cedula; en tomas prolongadas, además por campo (_tm30_f_pro_nombre), porque cada toma puede firmarla alguien distinto.
De dónde sale la identidad:
admin_users.cedula → personal en general
lab_enfermeras.numero_documento → enfermeros (vía admin_users.enfermera_id)
Al mostrar un documento firmado no se usa un valor por defecto: si no quedó guardado quién firmó, se muestra vacío. Antes se caía al usuario de la sesión actual, lo que atribuía la firma a quien simplemente estaba mirando el documento.
Precarga desde una visita anterior
modules/turnero/api/get_formulario_anterior.php devuelve las respuestas del último formulario firmado del mismo paciente, para no reescribir la historia clínica en cada visita.
Excluye deliberadamente firmas e identidad del profesional anterior: cada visita se firma de nuevo, con la fecha de hoy y quien atienda.
Diseñador
lab_formulario_builder.php permite armar el esquema desde la interfaz, sin escribir JSON a mano.