This commit is contained in:
Lizandro Guarnizo
2026-03-10 18:42:51 -05:00
parent b57fc16ab3
commit db469b2134
98 changed files with 16365 additions and 303 deletions
+34
View File
@@ -0,0 +1,34 @@
-- ============================================================
-- Migration: 20260302_lab_01_pacientes
-- Módulo Administrativo: Pacientes del laboratorio
-- Extiende la tabla users (contactos WhatsApp existentes)
-- ============================================================
CREATE TABLE IF NOT EXISTS `lab_pacientes` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`user_id` INT(11) DEFAULT NULL COMMENT 'FK a users.id (teléfono WhatsApp)',
`numero_documento` VARCHAR(30) DEFAULT NULL COMMENT 'Cédula / NIT / Pasaporte',
`tipo_documento` ENUM('CC','CE','TI','PA','NIT','RC','MS') DEFAULT 'CC',
`nombre_completo` VARCHAR(150) NOT NULL,
`telefono` VARCHAR(20) DEFAULT NULL,
`email` VARCHAR(100) DEFAULT NULL,
`fecha_nacimiento` DATE DEFAULT NULL,
`genero` ENUM('M','F','O') DEFAULT NULL,
`direccion` TEXT DEFAULT NULL,
`ciudad` VARCHAR(100) DEFAULT NULL,
`barrio` VARCHAR(100) DEFAULT NULL,
`eps` VARCHAR(100) DEFAULT NULL COMMENT 'Entidad promotora de salud',
`notas_admin` TEXT DEFAULT NULL COMMENT 'Notas internas del administrador',
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_numero_documento` (`numero_documento`),
KEY `idx_user_id` (`user_id`),
KEY `idx_nombre` (`nombre_completo`),
KEY `idx_telefono` (`telefono`),
KEY `idx_documento` (`numero_documento`),
CONSTRAINT `fk_lab_pac_user` FOREIGN KEY (`user_id`)
REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Pacientes del laboratorio (módulo administrativo)';
+24
View File
@@ -0,0 +1,24 @@
-- ============================================================
-- Migration: 20260302_lab_02_enfermeras
-- Módulo Administrativo: Personal de enfermería
-- ============================================================
CREATE TABLE IF NOT EXISTS `lab_enfermeras` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`numero_documento` VARCHAR(30) NOT NULL,
`tipo_documento` ENUM('CC','CE','TI','PA') DEFAULT 'CC',
`nombre_completo` VARCHAR(150) NOT NULL,
`telefono` VARCHAR(20) NOT NULL,
`telefono_alt` VARCHAR(20) DEFAULT NULL,
`email` VARCHAR(100) DEFAULT NULL,
`zona` VARCHAR(100) DEFAULT NULL COMMENT 'Zona/barrio asignada habitualmente',
`notas` TEXT DEFAULT NULL,
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_enf_documento` (`numero_documento`),
KEY `idx_enf_nombre` (`nombre_completo`),
KEY `idx_enf_activa` (`is_active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Personal de enfermería para domicilios';
@@ -0,0 +1,56 @@
-- ============================================================
-- Migration: 20260302_lab_03_ordenes_medicas
-- Módulo Administrativo: Órdenes médicas recibidas por WhatsApp
-- Vincula una imagen del chat con un paciente y un estado
-- ============================================================
CREATE TABLE IF NOT EXISTS `lab_ordenes_medicas` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`paciente_id` INT(11) NOT NULL COMMENT 'FK a lab_pacientes.id',
`conversation_id` INT(11) DEFAULT NULL COMMENT 'FK a conversations.id (mensaje con la imagen)',
`whatsapp_media_id` VARCHAR(255) DEFAULT NULL COMMENT 'Media ID de WhatsApp para referencia',
`local_file` VARCHAR(255) DEFAULT NULL COMMENT 'Ruta local de la imagen de la orden',
-- Estado del flujo de revisión
`estado` ENUM(
'pendiente',
'en_revision',
'autorizada',
'rechazada',
'en_domicilio',
'completada'
) NOT NULL DEFAULT 'pendiente',
-- Datos de la orden médica (ingresados manualmente)
`medico_nombre` VARCHAR(150) DEFAULT NULL,
`medico_registro` VARCHAR(50) DEFAULT NULL,
`fecha_orden` DATE DEFAULT NULL COMMENT 'Fecha que aparece en la orden impresa',
`diagnostico` TEXT DEFAULT NULL,
`examenes_solicitados` TEXT DEFAULT NULL COMMENT 'Lista de exámenes de la orden',
`requiere_ayuno` TINYINT(1) DEFAULT 0,
`horas_ayuno` TINYINT(3) DEFAULT NULL,
`indicaciones` TEXT DEFAULT NULL,
-- Gestión administrativa
`revisada_por` INT(11) DEFAULT NULL COMMENT 'FK a admin_users.id',
`revisada_at` DATETIME DEFAULT NULL,
`autorizada_por` INT(11) DEFAULT NULL COMMENT 'FK a admin_users.id',
`autorizada_at` DATETIME DEFAULT NULL,
`comentario_revision` TEXT DEFAULT NULL,
`notas_admin` TEXT DEFAULT NULL,
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_ord_paciente` (`paciente_id`),
KEY `idx_ord_conversation` (`conversation_id`),
KEY `idx_ord_estado` (`estado`),
KEY `idx_ord_fecha` (`fecha_orden`),
KEY `idx_ord_revisada_por` (`revisada_por`),
KEY `idx_ord_autorizada` (`autorizada_por`),
CONSTRAINT `fk_ord_paciente` FOREIGN KEY (`paciente_id`)
REFERENCES `lab_pacientes` (`id`) ON DELETE RESTRICT,
CONSTRAINT `fk_ord_conversation` FOREIGN KEY (`conversation_id`)
REFERENCES `conversations` (`id`) ON DELETE SET NULL,
CONSTRAINT `fk_ord_revisada` FOREIGN KEY (`revisada_por`)
REFERENCES `admin_users` (`id`) ON DELETE SET NULL,
CONSTRAINT `fk_ord_autorizada` FOREIGN KEY (`autorizada_por`)
REFERENCES `admin_users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Órdenes médicas recibidas vía WhatsApp';
+53
View File
@@ -0,0 +1,53 @@
-- ============================================================
-- Migration: 20260302_lab_04_domicilios
-- Módulo Administrativo: Servicios a domicilio
-- ============================================================
CREATE TABLE IF NOT EXISTS `lab_domicilios` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`orden_id` INT(11) DEFAULT NULL COMMENT 'FK a lab_ordenes_medicas.id',
`paciente_id` INT(11) NOT NULL COMMENT 'FK a lab_pacientes.id',
-- Datos del domicilio
`direccion` TEXT NOT NULL,
`ciudad` VARCHAR(100) DEFAULT NULL,
`barrio` VARCHAR(100) DEFAULT NULL,
`indicaciones_dir` TEXT DEFAULT NULL COMMENT 'Referencia de la dirección',
`fecha_programada` DATE NOT NULL,
`hora_programada` TIME DEFAULT NULL,
`tipo_servicio` VARCHAR(100) DEFAULT NULL COMMENT 'Tipo de toma de muestra',
-- Estado del domicilio
`estado` ENUM(
'programado',
'confirmado',
'en_camino',
'en_domicilio',
'completado',
'cancelado',
'reprogramado'
) NOT NULL DEFAULT 'programado',
`motivo_cancelacion` TEXT DEFAULT NULL,
`fecha_reprogramada` DATE DEFAULT NULL,
-- Resultados
`hora_llegada` TIME DEFAULT NULL,
`hora_salida` TIME DEFAULT NULL,
`observaciones` TEXT DEFAULT NULL,
`muestras_tomadas` TEXT DEFAULT NULL COMMENT 'Lista de muestras obtenidas',
-- Gestión
`creado_por` INT(11) DEFAULT NULL COMMENT 'FK a admin_users.id',
`notas_admin` TEXT DEFAULT NULL,
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_dom_orden` (`orden_id`),
KEY `idx_dom_paciente` (`paciente_id`),
KEY `idx_dom_fecha` (`fecha_programada`),
KEY `idx_dom_estado` (`estado`),
KEY `idx_dom_creado` (`creado_por`),
CONSTRAINT `fk_dom_orden` FOREIGN KEY (`orden_id`)
REFERENCES `lab_ordenes_medicas` (`id`) ON DELETE SET NULL,
CONSTRAINT `fk_dom_paciente` FOREIGN KEY (`paciente_id`)
REFERENCES `lab_pacientes` (`id`) ON DELETE RESTRICT,
CONSTRAINT `fk_dom_creado` FOREIGN KEY (`creado_por`)
REFERENCES `admin_users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Servicios a domicilio programados';
@@ -0,0 +1,33 @@
-- ============================================================
-- Migration: 20260302_lab_05_asignaciones
-- Módulo Administrativo: Asignación de enfermera a domicilio
-- ============================================================
CREATE TABLE IF NOT EXISTS `lab_asignaciones` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`domicilio_id` INT(11) NOT NULL COMMENT 'FK a lab_domicilios.id',
`enfermera_id` INT(11) NOT NULL COMMENT 'FK a lab_enfermeras.id',
`asignada_por` INT(11) DEFAULT NULL COMMENT 'FK a admin_users.id',
`estado` ENUM(
'asignada',
'confirmada',
'liberada',
'completada'
) NOT NULL DEFAULT 'asignada',
`notas` TEXT DEFAULT NULL,
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
-- Solo una enfermera activa por domicilio
UNIQUE KEY `uq_asig_domicilio` (`domicilio_id`),
KEY `idx_asig_enfermera` (`enfermera_id`),
KEY `idx_asig_estado` (`estado`),
KEY `idx_asig_asignada` (`asignada_por`),
CONSTRAINT `fk_asig_domicilio` FOREIGN KEY (`domicilio_id`)
REFERENCES `lab_domicilios` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_asig_enfermera` FOREIGN KEY (`enfermera_id`)
REFERENCES `lab_enfermeras` (`id`) ON DELETE RESTRICT,
CONSTRAINT `fk_asig_admin` FOREIGN KEY (`asignada_por`)
REFERENCES `admin_users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Asignación de enfermeras a domicilios';
@@ -0,0 +1,34 @@
-- ============================================================
-- Migration: 20260302_lab_06_autorizaciones
-- Módulo Administrativo: Log de cambios de estado de órdenes
-- Cada cambio de estado genera un registro aquí (trazabilidad)
-- ============================================================
CREATE TABLE IF NOT EXISTS `lab_autorizaciones` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`orden_id` INT(11) NOT NULL COMMENT 'FK a lab_ordenes_medicas.id',
`accion` ENUM(
'creada',
'en_revision',
'autorizada',
'rechazada',
'en_domicilio',
'completada',
'editada'
) NOT NULL,
`estado_anterior` VARCHAR(50) DEFAULT NULL,
`estado_nuevo` VARCHAR(50) DEFAULT NULL,
`realizada_por` INT(11) DEFAULT NULL COMMENT 'FK a admin_users.id',
`comentario` TEXT DEFAULT NULL,
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_aut_orden` (`orden_id`),
KEY `idx_aut_admin` (`realizada_por`),
KEY `idx_aut_accion` (`accion`),
KEY `idx_aut_fecha` (`created_at`),
CONSTRAINT `fk_aut_orden` FOREIGN KEY (`orden_id`)
REFERENCES `lab_ordenes_medicas` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_aut_admin` FOREIGN KEY (`realizada_por`)
REFERENCES `admin_users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Historial de cambios de estado de órdenes médicas';
@@ -0,0 +1,27 @@
-- ============================================================
-- Migration: 20260302_lab_07_actividad_admin
-- Módulo Administrativo: Trazabilidad general del módulo
-- Registra TODA acción realizada por administradoras
-- ============================================================
CREATE TABLE IF NOT EXISTS `lab_actividad_admin` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`admin_id` INT(11) DEFAULT NULL COMMENT 'FK a admin_users.id',
`admin_nombre` VARCHAR(150) DEFAULT NULL COMMENT 'Snapshot del nombre (por si se elimina)',
`modulo` VARCHAR(50) NOT NULL COMMENT 'pacientes|ordenes|domicilios|enfermeras|asignaciones',
`accion` VARCHAR(100) NOT NULL COMMENT 'crear|editar|eliminar|autorizar|rechazar|asignar...',
`entidad_id` INT(11) DEFAULT NULL COMMENT 'ID del registro afectado',
`detalle` TEXT DEFAULT NULL COMMENT 'JSON o descripción del cambio',
`ip_address` VARCHAR(45) DEFAULT NULL,
`user_agent` VARCHAR(255) DEFAULT NULL,
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_act_admin` (`admin_id`),
KEY `idx_act_modulo` (`modulo`),
KEY `idx_act_accion` (`accion`),
KEY `idx_act_entidad` (`entidad_id`),
KEY `idx_act_fecha` (`created_at`),
CONSTRAINT `fk_act_admin` FOREIGN KEY (`admin_id`)
REFERENCES `admin_users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Log de trazabilidad del módulo administrativo';
@@ -0,0 +1,47 @@
-- ============================================================
-- Migration: 20260302_lab_08_roles_enfermeros
-- Módulo Laboratorio: Rol enfermero + servicios extra
-- ============================================================
-- 1. Agregar columnas role y enfermera_id a admin_users
ALTER TABLE `admin_users`
ADD COLUMN IF NOT EXISTS `role` ENUM('admin','enfermero') NOT NULL DEFAULT 'admin'
COMMENT 'Rol del usuario: admin = acceso total, enfermero = solo portal propio',
ADD COLUMN IF NOT EXISTS `enfermera_id` INT(11) NULL DEFAULT NULL
COMMENT 'Vincula el usuario con su registro en lab_enfermeras';
-- Índice para lookups rápidos por rol
ALTER TABLE `admin_users`
ADD INDEX IF NOT EXISTS `idx_role` (`role`),
ADD INDEX IF NOT EXISTS `idx_enf_id` (`enfermera_id`);
-- 2. Tabla de servicios extra (sin orden médica)
-- El enfermero puede agregar servicios adicionales durante la visita
CREATE TABLE IF NOT EXISTS `lab_servicios_extra` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`domicilio_id` INT(11) NOT NULL,
`descripcion` VARCHAR(255) NOT NULL COMMENT 'Ej: Inyección, Cura, Nebulización',
`tipo` ENUM(
'inyeccion',
'cura',
'nebulizacion',
'toma_muestra',
'tension_arterial',
'glucometria',
'otro'
) NOT NULL DEFAULT 'otro',
`notas` TEXT DEFAULT NULL,
`requiere_pago` TINYINT(1) NOT NULL DEFAULT 0,
`valor` DECIMAL(10,2) DEFAULT NULL,
`realizado_por` INT(11) DEFAULT NULL COMMENT 'ID de lab_enfermeras',
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_se_domicilio` (`domicilio_id`),
KEY `idx_se_enfermera` (`realizado_por`),
CONSTRAINT `fk_se_domicilio` FOREIGN KEY (`domicilio_id`)
REFERENCES `lab_domicilios` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_se_enfermera` FOREIGN KEY (`realizado_por`)
REFERENCES `lab_enfermeras` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Servicios adicionales agregados por enfermeros durante visitas';
@@ -0,0 +1,63 @@
-- ============================================================
-- Migration: 20260302_lab_09_formularios
-- Módulo Formularios: creador drag&drop, envío a clientes, firma
-- ============================================================
-- 1. Plantillas de formularios (diseñadas por admins)
CREATE TABLE IF NOT EXISTS `lab_formularios` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`nombre` VARCHAR(150) NOT NULL,
`descripcion` TEXT DEFAULT NULL,
`categoria` ENUM(
'consentimiento',
'historia_clinica',
'autorizacion',
'encuesta',
'otro'
) NOT NULL DEFAULT 'otro',
`esquema` LONGTEXT NOT NULL COMMENT 'JSON con el array de campos del formulario',
`permite_firma` TINYINT(1) NOT NULL DEFAULT 1,
`requiere_firma` TINYINT(1) NOT NULL DEFAULT 0,
`version` SMALLINT NOT NULL DEFAULT 1,
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`creado_por` INT(11) DEFAULT NULL,
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_form_activo` (`is_active`),
KEY `idx_form_categ` (`categoria`),
CONSTRAINT `fk_form_admin` FOREIGN KEY (`creado_por`)
REFERENCES `admin_users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Plantillas de formularios creadas por administradores';
-- 2. Envíos (instancias de un formulario enviado a un paciente)
CREATE TABLE IF NOT EXISTS `lab_form_envios` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`formulario_id` INT(11) NOT NULL,
`paciente_id` INT(11) DEFAULT NULL,
`domicilio_id` INT(11) DEFAULT NULL,
`token` CHAR(64) NOT NULL UNIQUE COMMENT 'Token público único para acceso del cliente',
`datos_prefilled` LONGTEXT DEFAULT NULL COMMENT 'JSON con datos pre-llenados por el enfermero',
`datos_cliente` LONGTEXT DEFAULT NULL COMMENT 'JSON con respuestas del cliente',
`firma_svg` LONGTEXT DEFAULT NULL COMMENT 'Firma del cliente como SVG/PNG base64',
`ip_cliente` VARCHAR(45) DEFAULT NULL,
`user_agent` VARCHAR(512) DEFAULT NULL,
`estado` ENUM('pendiente','completado','firmado','expirado') NOT NULL DEFAULT 'pendiente',
`enviado_por` INT(11) DEFAULT NULL COMMENT 'ID admin_users que envió el link',
`enviado_via` ENUM('whatsapp','email','link') DEFAULT 'whatsapp',
`expira_en` DATETIME DEFAULT NULL,
`completado_en` DATETIME DEFAULT NULL,
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_fenv_form` (`formulario_id`),
KEY `idx_fenv_paciente` (`paciente_id`),
KEY `idx_fenv_token` (`token`),
KEY `idx_fenv_estado` (`estado`),
CONSTRAINT `fk_fenv_form` FOREIGN KEY (`formulario_id`) REFERENCES `lab_formularios` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_fenv_paciente` FOREIGN KEY (`paciente_id`) REFERENCES `lab_pacientes` (`id`) ON DELETE SET NULL,
CONSTRAINT `fk_fenv_domicilio` FOREIGN KEY (`domicilio_id`) REFERENCES `lab_domicilios` (`id`) ON DELETE SET NULL,
CONSTRAINT `fk_fenv_enviado` FOREIGN KEY (`enviado_por`) REFERENCES `admin_users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Instancias de formularios enviados a pacientes';
+113
View File
@@ -0,0 +1,113 @@
<?php
/**
* Migración: Módulo Administrativo de Laboratorio
* Ejecuta las 7 migraciones SQL del módulo en orden
*
* Uso:
* php migrations/20260302_lab_run_migrations.php
* php migrations/20260302_lab_run_migrations.php --rollback
*
* Las tablas se crean en este orden para respetar las FK:
* 1. lab_pacientes (depende de users)
* 2. lab_enfermeras (independiente)
* 3. lab_ordenes_medicas (depende de lab_pacientes, conversations, admin_users)
* 4. lab_domicilios (depende de lab_ordenes_medicas, lab_pacientes)
* 5. lab_asignaciones (depende de lab_domicilios, lab_enfermeras)
* 6. lab_autorizaciones (depende de lab_ordenes_medicas)
* 7. lab_actividad_admin (depende de admin_users)
*/
require_once __DIR__ . '/../config/config.php';
$isRollback = in_array('--rollback', $argv ?? []);
$line = str_repeat('─', 60);
echo "\n$line\n";
echo $isRollback
? " 🔄 ROLLBACK — Módulo Administrativo Lab\n"
: " 🚀 MIGRACIÓN — Módulo Administrativo Lab\n";
echo "$line\n\n";
// ── Archivos en orden de ejecución (rollback = inverso) ──────────────────────
$migrations = [
'20260302_lab_01_pacientes.sql',
'20260302_lab_02_enfermeras.sql',
'20260302_lab_03_ordenes_medicas.sql',
'20260302_lab_04_domicilios.sql',
'20260302_lab_05_asignaciones.sql',
'20260302_lab_06_autorizaciones.sql',
'20260302_lab_07_actividad_admin.sql',
];
// ── Sentencias DROP para rollback (orden inverso para respetar FK) ───────────
$rollback = [
'DROP TABLE IF EXISTS `lab_actividad_admin`',
'DROP TABLE IF EXISTS `lab_autorizaciones`',
'DROP TABLE IF EXISTS `lab_asignaciones`',
'DROP TABLE IF EXISTS `lab_domicilios`',
'DROP TABLE IF EXISTS `lab_ordenes_medicas`',
'DROP TABLE IF EXISTS `lab_enfermeras`',
'DROP TABLE IF EXISTS `lab_pacientes`',
];
try {
$db = Database::getInstance();
$pdo = $db->getConnection();
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
if ($isRollback) {
// ── ROLLBACK ─────────────────────────────────────────────────────────
foreach ($rollback as $sql) {
$table = preg_replace('/.*`(lab_\w+)`.*/', '$1', $sql);
$pdo->exec($sql);
echo " ✅ DROP $table\n";
}
echo "\n ✔ Rollback completado correctamente.\n\n";
} else {
// ── MIGRACIÓN ────────────────────────────────────────────────────────
$dir = __DIR__;
foreach ($migrations as $file) {
$path = "$dir/$file";
if (!file_exists($path)) {
echo " ⚠️ Archivo no encontrado: $file (omitido)\n";
continue;
}
$sql = file_get_contents($path);
// Separar sentencias por ';' pero solo las CREATE TABLE
// (Los archivos contienen una sola sentencia CREATE TABLE)
$pdo->exec($sql);
// Extraer nombre de tabla del archivo para feedback
preg_match('/CREATE TABLE IF NOT EXISTS `(\w+)`/i', $sql, $m);
$table = $m[1] ?? $file;
echo "$table\n";
}
echo "\n$line\n";
echo " ✔ Todas las migraciones ejecutadas correctamente.\n";
echo "$line\n\n";
// ── Tabla de control de migraciones ejecutadas en system_logs ────────
try {
$pdo->prepare("
INSERT INTO system_logs (level, message, context, created_at)
VALUES ('info', 'lab_module_migration', ?, NOW())
")->execute([json_encode([
'version' => '20260302',
'module' => 'lab_admin',
'tables' => 7,
'executed' => date('Y-m-d H:i:s'),
])]);
} catch (Exception $e) {
// system_logs puede no tener estas columnas — no falla la migración
}
}
} catch (PDOException $e) {
echo "\n ❌ ERROR: " . $e->getMessage() . "\n\n";
echo " Ejecuta --rollback para deshacer los cambios parciales.\n\n";
exit(1);
}
@@ -0,0 +1,14 @@
-- ============================================================
-- Migration: 20260303_lab_10_domicilio_extras
-- Agrega tipo_cliente (particular/seguro) y examenes_solicitados
-- a la tabla lab_domicilios, para cuando no hay orden médica.
-- ============================================================
ALTER TABLE `lab_domicilios`
ADD COLUMN `tipo_cliente` ENUM('particular','seguro','eps') NOT NULL DEFAULT 'particular'
COMMENT 'Si el servicio es particular, por seguro o EPS'
AFTER `tipo_servicio`,
ADD COLUMN `examenes_solicitados` TEXT DEFAULT NULL
COMMENT 'Exámenes escritos (cuando no hay foto de orden médica)'
AFTER `tipo_cliente`;
@@ -0,0 +1,25 @@
-- Migración: agregar columnas de contenido a message_templates (producción)
-- Las columnas body_text, header_*, footer_text, components, example_parameters
-- existen en el schema local pero faltaban en producción.
ALTER TABLE `message_templates`
ADD COLUMN IF NOT EXISTS `body_text` TEXT DEFAULT NULL
COMMENT 'Texto del cuerpo de la plantilla'
AFTER `status`,
ADD COLUMN IF NOT EXISTS `header_text` VARCHAR(255) DEFAULT NULL
COMMENT 'Texto del encabezado (si header_type=text)'
AFTER `body_text`,
ADD COLUMN IF NOT EXISTS `header_type` ENUM('text','image','video','document') DEFAULT NULL
COMMENT 'Tipo de encabezado multimedia'
AFTER `header_text`,
ADD COLUMN IF NOT EXISTS `footer_text` VARCHAR(255) DEFAULT NULL
COMMENT 'Texto del pie de la plantilla'
AFTER `header_type`,
ADD COLUMN IF NOT EXISTS `components` LONGTEXT DEFAULT NULL
COMMENT 'JSON completo de componentes de la plantilla'
AFTER `footer_text`,
ADD COLUMN IF NOT EXISTS `example_parameters` LONGTEXT DEFAULT NULL
COMMENT 'JSON con parámetros de ejemplo'
AFTER `components`,
ADD COLUMN IF NOT EXISTS `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
AFTER `created_at`;
@@ -0,0 +1,50 @@
<?php
require_once __DIR__ . '/../config/config.php';
$db = Database::getInstance();
// ── 1. Tabla lab_config ───────────────────────────────────────────────
$db->query('CREATE TABLE IF NOT EXISTS lab_config (
clave VARCHAR(80) NOT NULL PRIMARY KEY,
valor LONGTEXT NOT NULL DEFAULT "",
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4');
echo "tabla lab_config OK\n";
// Valores iniciales
$defaults = [
['empresa_nombre', 'Laboratorio Clínico Ximena'],
['empresa_subtitulo', 'Análisis Clínicos'],
['empresa_direccion', ''],
['empresa_telefono', ''],
['empresa_email', ''],
['empresa_ciudad', ''],
['doc_color', '#1565c0'],
['doc_logo_base64', ''],
['doc_pie_pagina', 'Documento generado digitalmente. Verifique su autenticidad en el sistema.'],
];
foreach ($defaults as [$k, $v]) {
$db->query('INSERT IGNORE INTO lab_config(clave, valor) VALUES(?, ?)', [$k, $v]);
}
echo count($defaults) . " valores iniciales insertados (IGNORE)\n";
// ── 2. Columnas nuevas en lab_formularios ─────────────────────────────
$cols = [
'doc_encabezado VARCHAR(200) NULL DEFAULT NULL',
'doc_subtitulo VARCHAR(200) NULL DEFAULT NULL',
'doc_logo_base64 LONGTEXT NULL DEFAULT NULL',
'doc_color VARCHAR(20) NULL DEFAULT NULL',
'doc_pie_pagina VARCHAR(500) NULL DEFAULT NULL',
];
foreach ($cols as $def) {
$name = explode(' ', trim($def))[0];
$exists = $db->fetchAll("SHOW COLUMNS FROM lab_formularios WHERE Field = '$name'");
if (!$exists) {
$db->query("ALTER TABLE lab_formularios ADD COLUMN $def");
echo "Columna agregada: $name\n";
} else {
echo "Ya existía: $name\n";
}
}
echo "\nMigración completada OK\n";
+75
View File
@@ -0,0 +1,75 @@
-- ============================================================
-- Migración: Sistema de Roles y Módulos
-- Fecha: 2026-03-04
-- Descripción: Crea tablas roles y role_modules; vincula admin_users
-- ============================================================
-- ── 1. Tabla de roles ────────────────────────────────────────
CREATE TABLE IF NOT EXISTS `roles` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL COMMENT 'Nombre legible del rol',
`slug` VARCHAR(50) NOT NULL COMMENT 'Clave interna (enfermero, admin, …)',
`description` TEXT DEFAULT NULL,
`color` VARCHAR(20) DEFAULT '#6c757d' COMMENT 'Color HEX para la UI',
`is_system` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '1 = no se puede eliminar',
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_slug` (`slug`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- ── 2. Tabla de módulos asignados por rol ────────────────────
-- Cada fila «activa» el módulo para ese rol.
CREATE TABLE IF NOT EXISTS `role_modules` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`role_id` INT(11) NOT NULL,
`module_slug` VARCHAR(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_role_module` (`role_id`, `module_slug`),
CONSTRAINT `fk_rm_role` FOREIGN KEY (`role_id`)
REFERENCES `roles`(`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- ── 3. Vincular admin_users con roles ────────────────────────
-- (Se ignora si la columna ya existe - compatible MySQL y MariaDB)
ALTER TABLE `admin_users`
ADD COLUMN `role_id` INT(11) NULL DEFAULT NULL
COMMENT 'FK a roles.id';
ALTER TABLE `admin_users`
ADD INDEX `idx_role_id` (`role_id`);
-- ── 4. Roles del sistema (no borrables) ─────────────────────
INSERT IGNORE INTO `roles` (`id`, `name`, `slug`, `description`, `color`, `is_system`) VALUES
(1, 'Administrador', 'admin', 'Acceso completo a todos los módulos', '#0d6efd', 1),
(2, 'Enfermero', 'enfermero', 'Solo portal de domicilios asignados y formularios','#6f42c1', 1);
-- ── 5. Módulos para el rol Administrador (todos) ─────────────
INSERT IGNORE INTO `role_modules` (`role_id`, `module_slug`) VALUES
(1, 'whatsapp'),
(1, 'lab_dashboard'),
(1, 'lab_ordenes'),
(1, 'lab_pacientes'),
(1, 'lab_domicilios'),
(1, 'lab_enfermeras'),
(1, 'lab_formularios'),
(1, 'lab_reportes'),
(1, 'lab_configuracion'),
(1, 'usuarios');
-- ── 6. Módulos para el rol Enfermero ────────────────────────
INSERT IGNORE INTO `role_modules` (`role_id`, `module_slug`) VALUES
(2, 'enfermero_portal'),
(2, 'lab_formularios');
-- ── 7. Vincular usuarios existentes con su role_id ───────────
UPDATE `admin_users` SET `role_id` = 1 WHERE `role` = 'admin' AND `role_id` IS NULL;
UPDATE `admin_users` SET `role_id` = 2 WHERE `role` = 'enfermero' AND `role_id` IS NULL;
-- Si algún usuario no tiene rol asignado, asignarle admin por defecto
UPDATE `admin_users` SET `role_id` = 1 WHERE `role_id` IS NULL;
-- ── 8. FK suave (solo si FK no existe aún) ──────────────────
-- (MariaDB/MySQL no tiene ADD CONSTRAINT IF NOT EXISTS, usamos el IGNORE del INSERT)
-- La FK se omite intencionalmente para no bloquear si ya existe;
-- conéctala manualmente si el motor lo permite:
-- ALTER TABLE admin_users ADD CONSTRAINT fk_au_role FOREIGN KEY (role_id) REFERENCES roles(id);