Files
whatsapp/modules/soporte/docs/arquitectura/60-decisiones-y-deuda.md
T
Lizandro GuarnizoandClaude Opus 5 32c209710c Documentación completa del proyecto en el módulo Soporte
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>
2026-08-04 10:14:32 -05:00

105 lines
4.6 KiB
Markdown

# Decisiones y deuda técnica
Por qué el sistema es como es, y qué cosas conviene saber antes de tocarlo.
## Decisiones tomadas a propósito
### Migración gradual, sin corte
Conviven el sistema original (archivos en la raíz) y el nuevo (módulos). No hubo una reescritura de golpe.
**Por qué:** el laboratorio opera todos los días. Una reescritura completa implicaba congelar el desarrollo o mantener dos sistemas en paralelo.
**Costo:** hay que saber en cuál de los dos está el código de cada pantalla. Los `lab_*` suelen estar en la raíz; el turnero está en el módulo.
### Sin framework de frontend
Las vistas son PHP que emiten HTML, con JavaScript embebido en la misma vista.
**Por qué:** despliegue por `git pull`, sin build ni compilación. Un archivo se edita y ya está en producción.
**Costo:** hay código repetido entre vistas, y las vistas grandes (el turnero) pasan de las 2.000 líneas.
### Esquemas de formulario en JSON
Los formularios se definen en JSON dentro de `lab_formularios.esquema`, no en tablas normalizadas.
**Por qué:** las fichas clínicas cambian seguido y cada una tiene campos distintos. Normalizarlas obligaba a migrar el esquema con cada formulario nuevo.
**Costo:** no se puede consultar por SQL «todos los pacientes con fiebre». Las respuestas viven dentro de un JSON.
### La identidad de quien firma se resuelve en el servidor
Nunca se acepta del navegador quién firmó algo.
**Por qué:** es un dato con valor legal. Un cliente puede mentir; la sesión no.
### El turno original nunca se modifica
Cuando una muestra pendiente se completa en una visita posterior, el turno original **queda como estaba**. Solo se registra el vínculo.
**Por qué:** el turno cerrado es un registro histórico. Alterarlo retroactivamente falsea los tiempos de atención y la facturación de aquel día.
### Inventarios generados, no escritos
Las tablas de módulos, endpoints, tablas y roles de esta documentación se leen del código y la base en cada carga.
**Por qué:** una lista escrita a mano envejece sin que nadie se entere. Una generada no puede mentir.
## Deuda técnica conocida
### Columnas `can_*` que no hacen nada
`role_modules` tiene `can_view`, `can_create`, `can_edit`, `can_delete`, `can_export` — y **el control de acceso no las lee**. Solo usa `permission` (`read`/`write`).
**Riesgo:** poner `can_edit = 0` da falsa sensación de haber restringido algo. Ya causó confusión.
**Arreglo:** o se usan de verdad, o se eliminan. Mientras tanto, conviene mantenerlas coherentes con `permission`.
### `role` y `role_id` duplicados
Un usuario tiene el rol en dos columnas. La interfaz lee una, los permisos salen de la otra.
**Riesgo:** cambiar solo `role` deja al usuario con permisos que no corresponden.
**Arreglo:** derivar `role` de `role_id` en lugar de almacenarlo.
### Dos vías para el mismo formulario
`turnero_consentimientos` y `lab_form_envios` guardan lo mismo con estructuras distintas y tokens de formato distinto. `ver_formulario_enviado.php` tiene que manejar ambos, y `form_cliente.php` existe solo para redirigir entre ellos.
**Por qué sigue así:** unificarlas obliga a cambiar la plantilla aprobada en Meta y migrar los registros históricos.
### Convenciones de nombre mezcladas
Conviven `creado_at` y `created_at`, `creado_por` y `enviado_por`, español e inglés. Depende de la época de cada tabla.
### Vistas muy grandes
`ver_formulario_enviado.php` supera las 3.000 líneas y mezcla render, lógica de tomas prolongadas y JavaScript. Es el archivo más delicado de tocar del sistema.
### Datos históricos incompletos
Algunas columnas se agregaron después y las filas viejas quedaron en `NULL`, sin forma de recuperarlas:
| Columna | Desde | Antes |
|---|---|---|
| `turnero_consentimientos.creado_por` | 3 ago 2026 | `NULL` |
| Identidad por toma en F-LAB-28 | 3 ago 2026 | No se guardaba |
| `admin_users.cedula` | 3 ago 2026 | Solo enfermeros la tenían |
No hay traza de auditoría que permita reconstruirlos.
### El dominio se deduce de cada petición
`APP_URL` sale del `HTTP_HOST`. Si alguien entra por una IP o un dominio alterno, los enlaces que se generen llevarán esa dirección — y quedan guardados así en el WhatsApp del paciente.
**Arreglo:** fijar `APP_URL` explícitamente.
## Al hacer cambios
- **Cambio de esquema** → dejá la migración en `migrations/`, idempotente.
- **Endpoint nuevo** → verificá permisos ahí adentro, no confíes en la vista.
- **Tocar el turnero** → es lo que más gente usa a diario; probá con un turno real.
- **Tocar formularios firmados** → tienen valor legal. Un render roto es un documento inválido.