Files
whatsapp/database/06_master_sync.sql
T
Lizandro Guarnizo a497dc4b31 fix: migración SQL + red Docker para Coolify
06_master_sync.sql:
- Comentario tenía ';' (La columna role_id ya existe; la FK) →
  el parser de migrate.php divide por ; y el texto 'la FK' quedaba
  como sentencia SQL → error SQLSTATE[42000]

docker-compose.yml:
- Eliminar whatsapp-network (red bridge propia)
- Con 1 solo servicio no se necesita red interna
- Coolify añade automáticamente su red nj05x5... (donde está Traefik)
- La red bridge extra podría interferir con el routing de Traefik
2026-03-12 16:42:14 -05:00

199 lines
11 KiB
SQL

-- =============================================================================
-- 06_master_sync.sql
-- Migración maestra de sincronización local ↔ producción
-- Generada: 2025-01-01
--
-- INSTRUCCIONES DE APLICACIÓN:
-- LOCAL (Docker MySQL):
-- docker exec whatsapp-dev-mysql mysql -uroot -proot_password_2026 \
-- usite_whatsapp_bot < database/06_master_sync.sql
--
-- PRODUCCIÓN (desde el contenedor app):
-- - Aplica SOLO las secciones marcadas [SOLO PROD] si prod ya las tiene,
-- o la migración completa en instalaciones nuevas.
--
-- GARANTÍA DE SEGURIDAD:
-- Este script es COMPLETAMENTE NO DESTRUCTIVO. No contiene DROP TABLE,
-- TRUNCATE, DELETE ni UPDATE. Todas las operaciones usan IF NOT EXISTS o
-- INSERT IGNORE, por lo que correrlo en un servidor con datos existentes
-- solo creará lo que falte sin modificar ni eliminar nada.
--
-- RESUMEN DE CAMBIOS:
-- A) Tablas solo en LOCAL → agregar a producción y futuras instalaciones:
-- - scheduled_messages
-- - scheduled_messages_view (VIEW)
-- B) Tablas solo en PROD → agregar a local y futuras instalaciones:
-- - roles
-- - role_modules
-- - lab_config
-- C) Columnas en PROD que faltan en LOCAL y futuras instalaciones:
-- - admin_users.role_id
-- - lab_form_envios.hash_verificacion
-- - lab_formularios.doc_encabezado, doc_subtitulo, doc_logo_base64,
-- doc_color, doc_pie_pagina
-- D) Ya cubierto en 05_terms_acceptance.sql (no se repite):
-- - terms_versions, terms_acceptance
-- - users.terms_pending, terms_accepted_at, terms_version_id
-- =============================================================================
SET NAMES utf8mb4;
SET foreign_key_checks = 0;
-- =============================================================================
-- C0) COLUMNAS: message_templates (guard por si 02 no aplicó completo)
-- =============================================================================
ALTER TABLE `message_templates`
ADD COLUMN IF NOT EXISTS `body_text` TEXT DEFAULT NULL COMMENT 'Texto del cuerpo con variables {{1}},{{2}}…',
ADD COLUMN IF NOT EXISTS `components` LONGTEXT DEFAULT NULL COMMENT 'Componentes JSON completos de la plantilla',
ADD COLUMN IF NOT EXISTS `example_parameters` LONGTEXT DEFAULT NULL COMMENT 'Ejemplos de valores para los parámetros',
ADD COLUMN IF NOT EXISTS `header_text` VARCHAR(255) DEFAULT NULL COMMENT 'Texto del header',
ADD COLUMN IF NOT EXISTS `header_type` ENUM('text','image','video','document') DEFAULT NULL COMMENT 'Tipo de header',
ADD COLUMN IF NOT EXISTS `footer_text` VARCHAR(255) DEFAULT NULL COMMENT 'Texto del footer',
ADD COLUMN IF NOT EXISTS `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
ALTER TABLE `message_templates`
ADD INDEX IF NOT EXISTS `idx_template_language` (`template_name`, `language_code`);
-- =============================================================================
-- A1) TABLA: scheduled_messages (local → prod / instalaciones nuevas)
-- =============================================================================
CREATE TABLE IF NOT EXISTS `scheduled_messages` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`template_id` int(11) DEFAULT NULL,
`template_name` varchar(100) DEFAULT NULL,
`template_language` varchar(10) DEFAULT 'es',
`template_parameters` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL
COMMENT 'Parámetros para las variables de la plantilla'
CHECK (json_valid(`template_parameters`)),
`message_type` enum('text','template') DEFAULT 'template',
`message_content` text DEFAULT NULL COMMENT 'Contenido del mensaje si es tipo text',
`scheduled_date` date NOT NULL,
`scheduled_time` time NOT NULL,
`status` enum('pending','sent','failed','cancelled') DEFAULT 'pending',
`sent_at` datetime DEFAULT NULL,
`error_message` text DEFAULT NULL,
`created_by` int(11) DEFAULT NULL COMMENT 'ID del admin que creó el recordatorio',
`created_at` datetime DEFAULT current_timestamp(),
`updated_at` datetime DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_scheduled_date` (`scheduled_date`),
KEY `idx_status` (`status`),
KEY `idx_scheduled_datetime` (`scheduled_date`,`scheduled_time`,`status`),
CONSTRAINT `scheduled_messages_ibfk_1`
FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- =============================================================================
-- A2) VISTA: scheduled_messages_view (local → prod / instalaciones nuevas)
-- =============================================================================
CREATE OR REPLACE VIEW `scheduled_messages_view` AS
SELECT
`sm`.`id`,
`sm`.`user_id`,
`sm`.`template_id`,
`sm`.`template_name`,
`sm`.`template_language`,
`sm`.`template_parameters`,
`sm`.`message_type`,
`sm`.`message_content`,
`sm`.`scheduled_date`,
`sm`.`scheduled_time`,
`sm`.`status`,
`sm`.`sent_at`,
`sm`.`error_message`,
`sm`.`created_by`,
`sm`.`created_at`,
`sm`.`updated_at`,
`u`.`name` AS `user_name`,
`u`.`phone_number` AS `phone_number`,
`mt`.`name` AS `template_display_name`,
`mt`.`body_text` AS `template_body`,
`mt`.`status` AS `template_status`
FROM `scheduled_messages` `sm`
LEFT JOIN `users` `u` ON `sm`.`user_id` = `u`.`id`
LEFT JOIN `message_templates` `mt` ON `sm`.`template_id` = `mt`.`id`
ORDER BY `sm`.`scheduled_date` DESC, `sm`.`scheduled_time` DESC;
-- =============================================================================
-- B1) TABLA: roles (prod → local / instalaciones nuevas)
-- =============================================================================
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;
-- Datos semilla mínimos de roles del sistema
INSERT IGNORE INTO `roles` (`id`, `name`, `slug`, `description`, `is_system`) VALUES
(1, 'Administrador', 'admin', 'Acceso completo al sistema', 1),
(2, 'Enfermero', 'enfermero', 'Acceso a módulos de laboratorio y atención', 1);
-- =============================================================================
-- B2) TABLA: role_modules (prod → local / instalaciones nuevas)
-- =============================================================================
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;
-- =============================================================================
-- B3) TABLA: lab_config (prod → local / instalaciones nuevas)
-- =============================================================================
CREATE TABLE IF NOT EXISTS `lab_config` (
`clave` varchar(80) NOT NULL,
`valor` longtext NOT NULL DEFAULT '',
`updated_at` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (`clave`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- =============================================================================
-- C1) COLUMNA: admin_users.role_id (prod → local / instalaciones nuevas)
-- =============================================================================
ALTER TABLE `admin_users`
ADD COLUMN IF NOT EXISTS `role_id` int(11) DEFAULT NULL;
-- C1a) FK omitida: ADD CONSTRAINT IF NOT EXISTS no es estandar en MariaDB
-- La columna role_id ya existe y la integridad la gestiona la aplicacion.
-- =============================================================================
-- C2) COLUMNA: lab_form_envios.hash_verificacion (prod → local)
-- =============================================================================
ALTER TABLE `lab_form_envios`
ADD COLUMN IF NOT EXISTS `hash_verificacion` char(64) DEFAULT NULL
COMMENT 'SHA-256 del PDF generado, para verificación de integridad';
-- =============================================================================
-- C3) COLUMNAS: lab_formularios doc_* (prod → local / instalaciones nuevas)
-- =============================================================================
ALTER TABLE `lab_formularios`
ADD COLUMN IF NOT EXISTS `doc_encabezado` varchar(200) DEFAULT NULL COMMENT 'Título principal del documento PDF',
ADD COLUMN IF NOT EXISTS `doc_subtitulo` varchar(200) DEFAULT NULL COMMENT 'Subtítulo / razón social',
ADD COLUMN IF NOT EXISTS `doc_logo_base64` longtext DEFAULT NULL COMMENT 'Logo en Base64 para el PDF',
ADD COLUMN IF NOT EXISTS `doc_color` varchar(20) DEFAULT NULL COMMENT 'Color principal HEX (#RRGGBB)',
ADD COLUMN IF NOT EXISTS `doc_pie_pagina` varchar(500) DEFAULT NULL COMMENT 'Texto del pie de página';
-- =============================================================================
-- FIN
-- =============================================================================
SET foreign_key_checks = 1;
-- Verificación rápida (opcional, descomentar para depuración)
-- SELECT 'scheduled_messages' AS tabla, COUNT(*) AS filas FROM scheduled_messages
-- UNION ALL SELECT 'roles', COUNT(*) FROM roles
-- UNION ALL SELECT 'role_modules', COUNT(*) FROM role_modules
-- UNION ALL SELECT 'lab_config', COUNT(*) FROM lab_config;