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>
105 lines
4.3 KiB
Markdown
105 lines
4.3 KiB
Markdown
# 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:
|
|
|
|
```json
|
|
[
|
|
{"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:
|
|
|
|
```json
|
|
{"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=1` no 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.
|