Files
whatsapp/migrations/003_turnero.sql
Lizandro GuarnizoandClaude Sonnet 4.6 1342d305ec fix: corregir migraciones fallidas, redis doble carga y cache de assets
- Eliminar .env del tracking de git (credenciales no deben ir en repo)
- Agregar .env y .env.* al .dockerignore (no hornear credenciales en imagen)
- Quitar extension=redis.so duplicada en php.ini
- Activar cache 7d para assets estáticos en nginx
- Corregir 5 migraciones: eliminar INSERT INTO migrations con columna incorrecta
  y remover semicolons en comentarios -- que partian el parser SQL

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 19:24:41 -05:00

261 lines
14 KiB
SQL
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- ============================================================
-- Migración 003 Módulo Turnero (Oleada 1)
-- Sistema de Turnero Inteligente con consentimientos informados
-- FASE 3.1 del plan ERP multi-módulo.
-- Segura para re-ejecutar (IF NOT EXISTS / INSERT IGNORE).
-- ============================================================
-- ── 1. Catálogo maestro de tipos de examen ──────────────────
-- Compartido con Oleada 2 (registro_exams).
-- En Migración 005 se agregan precio_base, requiere_ayunas, instrucciones.
CREATE TABLE IF NOT EXISTS exam_tipos (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
codigo VARCHAR(20) NOT NULL COMMENT 'Ej: HEM, GLU, URIN',
nombre VARCHAR(150) NOT NULL COMMENT 'Ej: Hemograma completo',
categoria VARCHAR(80) DEFAULT NULL COMMENT 'Ej: Hematología, Química',
activo TINYINT(1) NOT NULL DEFAULT 1,
creado_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uq_codigo (codigo),
INDEX idx_activo (activo),
INDEX idx_categoria (categoria)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Catálogo maestro de tipos de examen del laboratorio';
-- ── 2. Relación M:N examen ↔ consentimiento (formulario) ────
-- Si un examen no tiene registro aquí, no se exige consentimiento.
-- Varios exámenes del mismo turno que apunten al mismo formulario_id
-- generan UN SOLO consentimiento (deduplicado en la lógica PHP).
CREATE TABLE IF NOT EXISTS exam_tipo_consentimientos (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
exam_tipo_id INT UNSIGNED NOT NULL,
formulario_id INT NOT NULL COMMENT 'FK lab_formularios.id (tipo=consentimiento)',
PRIMARY KEY (id),
UNIQUE KEY uq_examen_form (exam_tipo_id, formulario_id),
INDEX idx_formulario (formulario_id),
CONSTRAINT fk_etc_exam FOREIGN KEY (exam_tipo_id) REFERENCES exam_tipos (id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='M:N entre tipos de examen y formularios de consentimiento';
-- ── 3. Lugares / estaciones de servicio configurables ───────
-- Recepcion NO es un lugar (es el paso implicito del flujo).
-- Cada lugar tiene su propia cola y su propia pantalla TV.
CREATE TABLE IF NOT EXISTS turnero_lugares (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
nombre VARCHAR(100) NOT NULL COMMENT 'Ej: Toma de Muestras 1, Rayos X',
descripcion TEXT DEFAULT NULL,
activo TINYINT(1) NOT NULL DEFAULT 1,
sort_order INT NOT NULL DEFAULT 99 COMMENT 'Orden en pantalla TV general',
creado_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
INDEX idx_activo (activo),
INDEX idx_sort (sort_order)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Estaciones de servicio configurables del turnero';
-- Lugares de ejemplo (ajustar según el laboratorio real)
INSERT IGNORE INTO turnero_lugares (id, nombre, descripcion, activo, sort_order) VALUES
(1, 'Toma de Muestras 1', 'Primera estación de toma de muestras', 1, 1),
(2, 'Toma de Muestras 2', 'Segunda estación de toma de muestras', 1, 2);
-- ── 4. Catálogo de prioridades (códigos A-F) ─────────────────
-- Editable desde el panel admin. orden_peso menor = mayor prioridad.
CREATE TABLE IF NOT EXISTS turnero_prioridades (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
codigo CHAR(1) NOT NULL COMMENT 'A, B, C, D, E, F',
nombre VARCHAR(100) NOT NULL,
orden_peso INT NOT NULL COMMENT 'Menor número = mayor prioridad',
color VARCHAR(7) NOT NULL DEFAULT '#6b7280' COMMENT 'Hex color para UI',
activo TINYINT(1) NOT NULL DEFAULT 1,
PRIMARY KEY (id),
UNIQUE KEY uq_codigo (codigo),
INDEX idx_orden (orden_peso)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Catálogo configurable de prioridades de atención';
-- Prioridades por defecto A-F
INSERT IGNORE INTO turnero_prioridades (codigo, nombre, orden_peso, color, activo) VALUES
('A', 'Niños', 1, '#ef4444', 1),
('B', 'Embarazadas', 2, '#f97316', 1),
('C', 'Adulto mayor', 3, '#eab308', 1),
('D', 'Discapacidad', 4, '#8b5cf6', 1),
('E', 'Paciente general', 5, '#3b82f6', 1),
('F', 'Muestra pendiente', 6, '#6b7280', 1);
-- ── 5. Sesiones diarias de atención ─────────────────────────
CREATE TABLE IF NOT EXISTS turnero_sesiones (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
fecha DATE NOT NULL,
abierto_por INT DEFAULT NULL COMMENT 'FK admin_users.id',
cerrado_por INT DEFAULT NULL COMMENT 'FK admin_users.id',
inicio_at DATETIME DEFAULT NULL,
fin_at DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uq_fecha (fecha),
INDEX idx_fecha (fecha)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Sesión de atención del día (una por fecha)';
-- ── 6. Turnos ────────────────────────────────────────────────
-- Motor principal del módulo. Un turno = un paciente en una visita.
CREATE TABLE IF NOT EXISTS turnero_turnos (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
sesion_id INT UNSIGNED NOT NULL,
numero INT NOT NULL COMMENT 'Correlativo dentro de la sesión',
codigo VARCHAR(10) NOT NULL COMMENT 'Ej: A001, E042',
prioridad_id INT UNSIGNED NOT NULL,
paciente_nombre VARCHAR(150) DEFAULT NULL COMMENT 'Capturado en kiosko (opcional)',
paciente_cel VARCHAR(20) DEFAULT NULL,
estado ENUM(
'espera',
'en_recepcion',
'en_espera_lugar',
'en_servicio',
'finalizado',
'ausente',
'cancelado'
) NOT NULL DEFAULT 'espera',
lugar_destino_id INT UNSIGNED DEFAULT NULL COMMENT 'Asignado por recepción',
-- Timestamps por etapa
creado_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
llamado_recepcion_at DATETIME DEFAULT NULL,
inicio_recepcion_at DATETIME DEFAULT NULL,
fin_recepcion_at DATETIME DEFAULT NULL,
llamado_lugar_at DATETIME DEFAULT NULL,
inicio_lugar_at DATETIME DEFAULT NULL,
fin_lugar_at DATETIME DEFAULT NULL,
-- Responsable de atención
atendido_recepcion_por INT DEFAULT NULL COMMENT 'FK admin_users.id',
atendido_lugar_por INT DEFAULT NULL COMMENT 'FK admin_users.id',
PRIMARY KEY (id),
UNIQUE KEY uq_sesion_numero (sesion_id, numero),
INDEX idx_sesion_estado (sesion_id, estado),
INDEX idx_lugar_estado (lugar_destino_id, estado),
INDEX idx_prioridad (prioridad_id),
INDEX idx_creado (creado_at),
CONSTRAINT fk_tt_sesion FOREIGN KEY (sesion_id) REFERENCES turnero_sesiones (id) ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT fk_tt_prioridad FOREIGN KEY (prioridad_id) REFERENCES turnero_prioridades (id) ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT fk_tt_lugar FOREIGN KEY (lugar_destino_id) REFERENCES turnero_lugares (id) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Turnos del sistema de atención';
-- ── 7. Solicitud interna (crea recepción) ────────────────────
-- Registra paciente, exámenes, pago y lugar destino.
-- Una solicitud por turno (UNIQUE turno_id).
CREATE TABLE IF NOT EXISTS turnero_solicitudes (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
turno_id INT UNSIGNED NOT NULL,
paciente_id INT NOT NULL COMMENT 'FK lab_pacientes.id',
lugar_id INT UNSIGNED NOT NULL COMMENT 'FK turnero_lugares.id',
total_cobrado DECIMAL(10,2) DEFAULT NULL,
metodo_pago ENUM(
'efectivo',
'transferencia',
'tarjeta',
'eps',
'cortesia'
) DEFAULT NULL,
observaciones TEXT DEFAULT NULL,
creado_por INT NOT NULL COMMENT 'FK admin_users.id',
creado_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uq_turno (turno_id),
INDEX idx_paciente (paciente_id),
INDEX idx_lugar (lugar_id),
INDEX idx_creado (creado_at),
CONSTRAINT fk_ts_turno FOREIGN KEY (turno_id) REFERENCES turnero_turnos (id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_ts_lugar FOREIGN KEY (lugar_id) REFERENCES turnero_lugares (id) ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Solicitud interna de agendamiento creada en recepción';
-- ── 8. Exámenes de la solicitud ──────────────────────────────
CREATE TABLE IF NOT EXISTS turnero_examen_items (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
solicitud_id INT UNSIGNED NOT NULL,
exam_tipo_id INT UNSIGNED NOT NULL,
creado_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
INDEX idx_solicitud (solicitud_id),
INDEX idx_exam_tipo (exam_tipo_id),
CONSTRAINT fk_tei_solicitud FOREIGN KEY (solicitud_id) REFERENCES turnero_solicitudes (id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_tei_exam FOREIGN KEY (exam_tipo_id) REFERENCES exam_tipos (id) ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Exámenes individuales de cada solicitud de turno';
-- ── 9. Consentimientos por turno (deduplicados) ──────────────
-- Un registro por formulario distinto requerido en el turno.
-- token = enlace único de firma (UUID).
CREATE TABLE IF NOT EXISTS turnero_consentimientos (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
turno_id INT UNSIGNED NOT NULL,
formulario_id INT NOT NULL COMMENT 'FK lab_formularios.id',
token VARCHAR(64) NOT NULL COMMENT 'UUID único para enlace de firma',
estado ENUM(
'pendiente',
'enviado',
'visto',
'firmado',
'rechazado'
) NOT NULL DEFAULT 'pendiente',
enviado_at DATETIME DEFAULT NULL,
firmado_at DATETIME DEFAULT NULL,
ip_firma VARCHAR(45) DEFAULT NULL COMMENT 'IPv4 o IPv6 del firmante',
ua_firma VARCHAR(500) DEFAULT NULL COMMENT 'User-Agent del dispositivo',
version_formulario INT DEFAULT NULL COMMENT 'Snapshot de versión al firmar',
PRIMARY KEY (id),
UNIQUE KEY uq_token (token),
UNIQUE KEY uq_turno_form (turno_id, formulario_id),
INDEX idx_turno (turno_id),
INDEX idx_estado (estado),
CONSTRAINT fk_tc_turno FOREIGN KEY (turno_id) REFERENCES turnero_turnos (id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Consentimientos informados vinculados a un turno (uno por formulario distinto)';
-- ── 10. Columna tipo en lab_formularios ──────────────────────
-- Permite marcar un formulario como consentimiento informado.
ALTER TABLE lab_formularios
ADD COLUMN IF NOT EXISTS tipo
ENUM('formulario', 'consentimiento') NOT NULL DEFAULT 'formulario'
COMMENT 'Tipo de formulario: genérico o consentimiento informado';
-- Índice para filtrar rápido en las consultas del turnero
ALTER TABLE lab_formularios
ADD INDEX IF NOT EXISTS idx_tipo (tipo);
-- ── 11. Asignar módulo turnero a roles ───────────────────────
-- Activar en system_modules si estaba inactivo
UPDATE system_modules
SET is_active = 1
WHERE slug = 'turnero';
-- Permisos del recepcionista sobre el módulo turnero
INSERT IGNORE INTO role_modules (role_id, module_slug, can_view, can_create, can_edit, can_delete, can_export)
SELECT r.id, 'turnero', 1, 1, 1, 0, 0
FROM roles r WHERE r.slug = 'recepcionista';
-- Permisos del bacteriólogo sobre el módulo turnero (solo lectura + edición de servicio)
INSERT IGNORE INTO role_modules (role_id, module_slug, can_view, can_create, can_edit, can_delete, can_export)
SELECT r.id, 'turnero', 1, 0, 1, 0, 0
FROM roles r WHERE r.slug = 'bacteriologo';
-- ── 12. Datos de ejemplo de tipos de examen ──────────────────
-- Solo si la tabla está vacía (evita duplicar en re-ejecución)
INSERT IGNORE INTO exam_tipos (codigo, nombre, categoria) VALUES
('HEM', 'Hemograma completo', 'Hematología'),
('GLU', 'Glucosa en ayunas', 'Química'),
('URIN', 'Uroanálisis completo', 'Urología'),
('UROCUL','Urocultivo', 'Microbiología'),
('COLEST','Perfil lipídico (colesterol)', 'Química'),
('TSH', 'TSH (Función tiroidea)', 'Endocrinología'),
('HIV', 'Prueba VIH', 'Serología'),
('HEPB', 'Hepatitis B (HBsAg)', 'Serología'),
('RXP', 'Rayos X de pecho', 'Radiología'),
('ECG', 'Electrocardiograma', 'Cardiología');
-- ── 13. Columna firma_svg en turnero_consentimientos ─────────
-- Almacena el dibujo de firma del paciente (data URL PNG).
-- MEDIUMTEXT soporta firmas de alta resolución (hasta 16 MB).
ALTER TABLE turnero_consentimientos
ADD COLUMN IF NOT EXISTS firma_svg MEDIUMTEXT DEFAULT NULL
COMMENT 'Dibujo de firma del paciente (data URL PNG base64)';