ATN-005
Bloqueo por receta vencida
Garantizar que ninguna dispensación se entregue con base en una receta médica cuya vigencia haya expirado, y que el socio y la organización dispongan de información accionable para regularizar la situación.
1. Objetivo
Impedir que el sistema registre una dispensación cuando la receta médica del socio está vencida, y asegurar que tanto el socio como la organización tengan información suficiente para actuar.
Este protocolo es la contraparte operativa de [[wiki/entities/dispensai/protocolos/ADM-004]] (renovación de receta). ADM-004 cubre el proceso de ingreso y validación de documentación nueva. ATN-005 cubre lo que ocurre cuando ese documento ya no es válido en el momento de intentar una dispensación.
2. Alcance
Cubre el control de vigencia de receta en el momento de creación de una dispensación, la información que el sistema muestra al socio, y el registro del evento de bloqueo.
No cubre:
- El proceso de renovación de receta una vez detectado el vencimiento — eso es [[wiki/entities/dispensai/protocolos/ADM-004]].
- El primer ingreso de la receta — eso es ADM-003.
- La validación de identidad del socio en el momento de la dispensación — eso es otro control.
- El caso en que la receta aún no tiene fecha de vencimiento configurada — ese estado
(
requiresConfiguration) requiere atención administrativa diferente al vencimiento.
3. Definiciones
Receta vencida. Receta cuya prescription_expiration_date registrada en extracted_documents
es anterior a la fecha y hora actual al momento de la solicitud de dispensación.
Receta sin configurar. Receta cuya prescription_expiration_date es NULL. El sistema la
trata como requiresConfiguration: true, distinto de isExpired: true. Ambos estados bloquean
la dispensación, pero el camino de regularización es diferente.
Bloqueo. La interrupción del proceso de creación de dispensación antes de que se ejecute
ninguna mutación sobre la tabla dispensations.
Preventivo. Estado en que la receta tiene 30 días o menos de vigencia restante. El sistema lo detecta y puede advertir al socio sin bloquearlo aún.
4. Roles y responsables
| Rol | Responsabilidad |
|---|---|
| Sistema | Verificar vigencia antes de crear la dispensación; rechazar y registrar el evento si está vencida |
| Socio | Regularizar su documentación según lo que le indica el sistema |
| Admin / soporte | Detectar socios bloqueados y apoyar el proceso de renovación |
| Responsable de documentación | Verificar que la receta nueva cumple los requisitos antes de configurarla en el sistema |
5. Disparador de inicio
Un administrador o el sistema intenta crear una dispensación para un socio cuya receta médica está vencida o sin fecha de vencimiento configurada.
6. Requisitos previos
- La receta médica del socio está registrada en
extracted_documents. - El sistema tiene acceso a
prescription_expiration_datedel documento más reciente configurado.
7. Entradas
- Solicitud de creación de dispensación con el
user_iddel socio. - Registro del documento médico del socio en
extracted_documents.
8. Procedimiento paso a paso
- Verificación de vigencia. Antes de ejecutar ninguna mutación, el sistema consulta
checkPrescriptionStatus(userId)y comparaprescription_expiration_datecon la fecha actual. - Bloqueo por vencimiento. Si
isExpired === true(fecha pasada) o la receta está sin configurar, el sistema retorna un error y no crea ningún registro endispensations. - Información al operador. El sistema devuelve un mensaje de error que indica el motivo del
bloqueo. El dashboard del socio muestra
DocumentStatuscon razón"expired"o"missing_expiration_date". - Registro del evento. El sistema invoca
logISOSystemEvent("prescription_expired", ...). Hoy esa llamada no deja ningún registro: la tabla destino no existe y el plano ISO está apagado en todos los tenants (ver brecha 3 de la §17). El paso se conserva porque el código lo ejecuta, pero no produce evidencia verificable. - Orientación al socio. El dashboard del socio muestra el estado del documento. El socio puede iniciar el proceso de renovación conforme a [[wiki/entities/dispensai/protocolos/ADM-004]].
- Seguimiento por admin. El equipo administrativo revisa los socios con receta vencida y coordina la renovación.
9. Puntos de control
| # | Control | Dónde se ejecuta | Carácter |
|---|---|---|---|
| C1 | Guard isExpired antes de crear dispensación (path principal) | app/actions/dispensations.ts:183 | Duro — el servidor no crea la dispensación |
| C2 | Guard isExpired antes de crear dispensación (path alternativo) | app/actions/dispensations.ts:927 | Duro — mismo control en segundo path |
| C3 | Guard por BLOCKED_ROUTES (vía de administración) | app/actions/dispensations.ts:175 | Duro — independiente del guard de vigencia |
| C4 | Estado DocumentStatus visible en dashboard del socio | lib/document-status-checker.ts | Informativo |
| C5 | Llamada a logISOSystemEvent("prescription_expired", ...) | lib/prescription-checker.ts | Sin efecto hoy — no deja registro (ver §17, brecha 3) |
C1 y C2 son controles duros verificados en código. C3 es un control duro separado. C4 es un control de visibilidad. C5 se conserva como paso del código, pero hoy no produce evidencia: no tratarlo como registro auditable.
10. Criterios de decisión
| Estado de la receta | Resultado |
|---|---|
isExpired: true (fecha pasada) | Bloqueo. Sin dispensación. Sin registro del evento (§17, brecha 3) |
requiresConfiguration: true (fecha NULL) | Bloqueo. Sin dispensación. Se requiere configurar la fecha |
≤ 30 días de vigencia (preventive) | Sin bloqueo. Aviso en dashboard. Sin registro del evento (§17, brecha 3) |
| Vigente y configurada | Dispensación procede normalmente |
11. Excepciones y escalamiento
- Receta sin fecha configurada: El administrador debe revisar el documento, extraer la fecha de vencimiento e ingresar el dato al sistema. Hasta que no se configure, el sistema no puede distinguir vigente de vencido y bloquea preventivamente.
- Error en la lectura del documento: Si
extracted_documentsno tiene registro para el socio,checkPrescriptionStatusretornarequiresConfiguration: true. El bloqueo opera igual. - Receta vencida urgente: Si la situación clínica del socio requiere dispensación urgente, la organización puede coordinar la renovación acelerada. El sistema no tiene mecanismo de sobrepaso (bypass) habilitado por defecto.
12. Registros generados
| Registro | Contenido | Tabla |
|---|---|---|
| Estado de documento del socio | Razón "expired" o "missing_expiration_date" | Calculado en tiempo real; no persiste como fila separada |
Este protocolo no genera hoy ningún registro persistente propio. El bloqueo opera, pero no
deja rastro verificable en el sistema: la evidencia del evento de bloqueo se declaraba sobre
iso_system_events, tabla que ya no existe. Ver §17, brecha 3. La evidencia de que el control se
ejecutó es humana (reporte del socio, revisión del panel), no del sistema.
13. Indicadores
| Indicador | Fórmula | Meta | Fuente |
|---|---|---|---|
| Tasa de dispensaciones bloqueadas por receta vencida | Dispensaciones bloqueadas ÷ intentos de dispensación | Monitorear tendencia; no es una métrica que deba ser 0 siempre | No calculable hoy — no hay registro del bloqueo (§17, brecha 3) |
| Socios con receta vencida activa | COUNT socios con isExpired: true sin dispensación en 30 días | 0 sin seguimiento activo | extracted_documents |
| Tiempo de regularización | Fecha de nueva receta configurada − fecha del primer bloqueo | ≤ 7 días hábiles | extracted_documents para la fecha de la receta nueva; la fecha del primer bloqueo no está registrada (§17, brecha 3) |
14. Riesgos y controles
| Riesgo | Control | Tipo |
|---|---|---|
| Dispensación entregada sin receta vigente | Guards C1 y C2 en server action (código) | Preventivo duro |
| Bloqueo sin rastro auditable | Ningún control compensatorio en el sistema; los guards C1/C2 siguen operando pero no dejan registro | Brecha (§17, brecha 3) |
| Socio bloqueado sin orientación | Dashboard muestra estado y razón; protocolo ADM-004 para renovación | Informativo |
| Admin sin visibilidad de socios vencidos | No hay alerta automática — ver §17 | Brecha |
15. Normativa y referencias
| Norma | Referencia | Qué cubre en este protocolo |
|---|---|---|
| ISO 9001:2015 | §8.5.1 | Control de producción y provisión del servicio; la receta vigente es un requisito documentado para la dispensación |
| ISO 9001:2015 | §8.7 | Control de salidas no conformes; una dispensación sin receta vigente es una salida no conforme |
| Ley 20.000 | Art. 8 | Uso exclusivamente terapéutico; la receta médica vigente es el instrumento que acredita la condición médica del socio |
| ISO 27001:2022 | A.8.15 | Logging de eventos; el bloqueo por receta vencida debería quedar registrado. Cobertura abierta: hoy no se registra (§17, brecha 3) |
16. Anexos y formularios vinculados
- [[wiki/entities/dispensai/protocolos/ADM-004]] — Renovación de receta médica (contraparte).
- [[wiki/entities/dispensai/protocolos/ADM-003]] — Primer ingreso de documentación.
17. Implementación en el sistema
El bloqueo principal está implementado y confirmado en dos puntos independientes del código. El registro del evento no existe. La visibilidad del administrador es una brecha.
| Elemento | Ubicación | Estado |
|---|---|---|
| Guard principal de vigencia (path 1) | app/actions/dispensations.ts:183 | CONFIRMADO |
| Guard principal de vigencia (path 2) | app/actions/dispensations.ts:927 | CONFIRMADO |
Guard por BLOCKED_ROUTES | app/actions/dispensations.ts:175 | CONFIRMADO |
checkPrescriptionStatus() — distingue isExpired vs requiresConfiguration | lib/prescription-checker.ts | CONFIRMADO |
DocumentStatus visible en dashboard del socio | lib/document-status-checker.ts | CONFIRMADO |
Tipos prescription_expired y prescription_expiring en el enum ISO | lib/iso-event-logger.ts:89-90 | CONFIRMADO (el tipo existe; el destino no) |
| Registro persistente del evento de bloqueo | — | INEXISTENTE (brecha 3) |
Brechas conocidas:
-
La emisión del evento no falla ruidosamente. La llamada a
logISOSystemEvent("prescription_expired", ...)se ejecuta dentro de un bloquetry/catchque captura la excepción sin relanzarla. El bloqueo de la dispensación igual se ejecuta — C1 y C2 no dependen del evento — pero la ausencia de registro nunca se manifiesta como error. Esto es lo que hizo que la brecha 3 pasara inadvertida. -
Sin notificación al administrador cuando una receta vence. El sistema bloquea en silencio desde la perspectiva del equipo. El administrador descubre que un socio está bloqueado solo si: (a) el socio lo contacta, (b) el administrador revisa el panel de documentos del socio de forma proactiva. No existe cola, alerta ni vista filtrada de socios con receta vencida sin dispensación reciente. Esta es una brecha operativa con consecuencia directa: un socio puede permanecer bloqueado indefinidamente sin que la organización lo sepa.
-
La tabla
iso_system_eventsno existe y el plano ISO está apagado. Este protocolo declarabaiso_system_eventscomo registro de evidencia. Esa tabla fue eliminada por la migracióndispensai/supabase/migrations/20260515000000_iso_standby.sql:121(DROP TABLE IF EXISTS public.iso_system_events CASCADE) y vuelve a eliminarse en20260629000000_cross_tenant_dispensation_fix_canonical.sql:76. Ninguna migración posterior la recrea. El plano ISO se trasladó al Workerdispensai/backends/workers/iso(D1), detrás de la banderaISO_ENABLED, que está enfalseen los seis tenants con.envlocal. Con la bandera apagada,logISOSystemEventes un no-op silencioso — así lo documentadispensai/lib/iso-event-logger.ts:8. Consecuencia operativa: el control de bloqueo por receta vencida existe como procedimiento y se ejecuta en código, pero no deja rastro verificable en el sistema. No se puede acreditar ante un tercero cuántas veces operó, ni cuándo. La evidencia disponible es humana, no del sistema.
Lo que ejecuta la organización fuera del sistema: la comunicación al socio de que su receta vencida necesita renovación se hace por canales externos (llamada, correo). El sistema muestra el estado en el dashboard del socio, pero no envía notificación proactiva. La coordinación de la renovación entre el socio y el equipo médico también ocurre fuera del sistema.
18. Historial de versiones
| Versión | Fecha | Cambio | Autor |
|---|---|---|---|
| 1.0 | 2026-08-28 | Versión inicial. Redactada desde la corrida de evidencia 2026-08-28-ATN. Dos brechas declaradas: evento best-effort y ausencia de notificación al admin. | Sistema / admin |
| 1.1 | 2026-08-29 | Cierre de INFERIDO: guards en dispensations.ts:183 y :927 confirmados en código. Tipos prescription_expired y prescription_expiring confirmados en lib/iso-event-logger.ts:89-90. Todo §17 ya estaba CONFIRMADO o PARCIAL — sin cambio a brechas. | Sistema / admin |
| 1.2 | 2026-08-29 | Corrección de evidencia inexistente: iso_system_events sale del frontmatter (registro_evidencia, implementacion.tablas) y eventos_iso queda vacío. Nueva brecha 3 con la migración que eliminó la tabla y el estado de ISO_ENABLED. §8, §9, §10, §12, §13, §14 y §15 corregidos para no afirmar un registro que no ocurre. Brechas 2 → 3. | Sistema / admin |
Documentos asociados
- receta médica vigente
- extracted_documents
Registros y evidencia
- audit_logs
Indicadores
- 0 dispensaciones entregadas con receta vencida
- % de socios con receta vencida notificados o atendidos dentro del plazo
Normativa y referencias
- ISO 9001:2015 §8.5.1
- ISO 9001:2015 §8.7
- Ley 20.000 Art. 8
- ISO 27001:2022 A.8.15
Dónde deja rastro en el sistema
- Rutas
-
- /dashboard
- /admin/dispensaciones
- Tablas
-
- extracted_documents
- dispensations