-- V011__create_medio_seccion.sql -- ADM-001 (Fase 1 CRITICAL PATH): Medios y Secciones — catálogo fundacional. -- -- Cambios: -- 1. dbo.Medio (Codigo UQ global, TipoMedio enum 1..4, PlataformaEmpresaId NULL, SYSTEM_VERSIONING ON). -- 2. dbo.Seccion (FK MedioId, Codigo UQ por Medio, Tipo CHECK, SYSTEM_VERSIONING ON). -- 3. Permiso 'administracion:secciones:gestionar' + asignación a rol 'admin'. -- El permiso 'administracion:medios:gestionar' ya existía desde V005. -- -- Patrón: V007 (permisos MERGE) + V010 (Temporal Tables con retention 10 años + PAGE compression en history). -- Idempotente: seguro para re-ejecutar. -- Reversa: V011_ROLLBACK.sql. -- Run on: SIGCM2 (dev) y SIGCM2_Test (integration tests). -- -- Source of truth: Obsidian/02-ARQUITECTURA-y-TECH-STACK/2.10 📋 UDTs Módulo Administración.md (ADM-001) -- Entidades: Obsidian/03-MODELO-de-DATOS/3.2 Entidades Core/3.2.1 🏢 Medio.md -- Auditoría: Obsidian/02-ARQUITECTURA-y-TECH-STACK/2.5 📋 Auditoría.md SET QUOTED_IDENTIFIER ON; SET ANSI_NULLS ON; SET NOCOUNT ON; GO -- ═══════════════════════════════════════════════════════════════════════ -- 1. dbo.Medio -- ═══════════════════════════════════════════════════════════════════════ IF OBJECT_ID(N'dbo.Medio', N'U') IS NULL BEGIN CREATE TABLE dbo.Medio ( Id INT IDENTITY(1,1) NOT NULL CONSTRAINT PK_Medio PRIMARY KEY, Codigo VARCHAR(30) NOT NULL, Nombre NVARCHAR(100) NOT NULL, Tipo TINYINT NOT NULL, -- TipoMedio: 1=Diario, 2=Radio, 3=Web, 4=Poster PlataformaEmpresaId INT NULL, -- FK futura a INT-003 (IMAC mapping) Activo BIT NOT NULL CONSTRAINT DF_Medio_Activo DEFAULT(1), FechaCreacion DATETIME2(3) NOT NULL CONSTRAINT DF_Medio_FechaCreacion DEFAULT(SYSUTCDATETIME()), FechaModificacion DATETIME2(3) NULL, CONSTRAINT UQ_Medio_Codigo UNIQUE (Codigo), CONSTRAINT CK_Medio_Tipo CHECK (Tipo BETWEEN 1 AND 4) ); PRINT 'Table dbo.Medio created.'; END ELSE PRINT 'Table dbo.Medio already exists — skip.'; GO IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'IX_Medio_Activo_Tipo' AND object_id = OBJECT_ID('dbo.Medio')) BEGIN CREATE INDEX IX_Medio_Activo_Tipo ON dbo.Medio(Activo, Tipo) INCLUDE (Codigo, Nombre, PlataformaEmpresaId); PRINT 'Index IX_Medio_Activo_Tipo created.'; END GO -- ═══════════════════════════════════════════════════════════════════════ -- 2. dbo.Seccion -- ═══════════════════════════════════════════════════════════════════════ IF OBJECT_ID(N'dbo.Seccion', N'U') IS NULL BEGIN CREATE TABLE dbo.Seccion ( Id INT IDENTITY(1,1) NOT NULL CONSTRAINT PK_Seccion PRIMARY KEY, MedioId INT NOT NULL, Codigo VARCHAR(30) NOT NULL, Nombre NVARCHAR(100) NOT NULL, Tipo VARCHAR(20) NOT NULL, -- 'clasificados' | 'notables' | 'suplementos' Activo BIT NOT NULL CONSTRAINT DF_Seccion_Activo DEFAULT(1), FechaCreacion DATETIME2(3) NOT NULL CONSTRAINT DF_Seccion_FechaCreacion DEFAULT(SYSUTCDATETIME()), FechaModificacion DATETIME2(3) NULL, CONSTRAINT FK_Seccion_Medio FOREIGN KEY (MedioId) REFERENCES dbo.Medio(Id) ON DELETE NO ACTION, CONSTRAINT UQ_Seccion_MedioId_Codigo UNIQUE (MedioId, Codigo), CONSTRAINT CK_Seccion_Tipo CHECK (Tipo IN ('clasificados','notables','suplementos')) ); PRINT 'Table dbo.Seccion created.'; END ELSE PRINT 'Table dbo.Seccion already exists — skip.'; GO IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'IX_Seccion_MedioId_Activo' AND object_id = OBJECT_ID('dbo.Seccion')) BEGIN CREATE INDEX IX_Seccion_MedioId_Activo ON dbo.Seccion(MedioId, Activo) INCLUDE (Codigo, Nombre, Tipo); PRINT 'Index IX_Seccion_MedioId_Activo created.'; END GO -- ═══════════════════════════════════════════════════════════════════════ -- 3. SYSTEM_VERSIONING — Medio -- ═══════════════════════════════════════════════════════════════════════ IF COL_LENGTH('dbo.Medio', 'ValidFrom') IS NULL BEGIN ALTER TABLE dbo.Medio ADD ValidFrom DATETIME2(3) GENERATED ALWAYS AS ROW START HIDDEN NOT NULL CONSTRAINT DF_Medio_ValidFrom DEFAULT(SYSUTCDATETIME()), ValidTo DATETIME2(3) GENERATED ALWAYS AS ROW END HIDDEN NOT NULL CONSTRAINT DF_Medio_ValidTo DEFAULT(CONVERT(DATETIME2(3), '9999-12-31 23:59:59.999')), PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo); PRINT 'Medio: PERIOD FOR SYSTEM_TIME added.'; END GO IF NOT EXISTS (SELECT 1 FROM sys.tables WHERE object_id = OBJECT_ID('dbo.Medio') AND temporal_type = 2) BEGIN ALTER TABLE dbo.Medio SET (SYSTEM_VERSIONING = ON ( HISTORY_TABLE = dbo.Medio_History, HISTORY_RETENTION_PERIOD = 10 YEARS )); PRINT 'Medio: SYSTEM_VERSIONING = ON (history: dbo.Medio_History, retention: 10 years).'; END ELSE PRINT 'Medio: SYSTEM_VERSIONING already ON — skip.'; GO IF EXISTS (SELECT 1 FROM sys.tables WHERE name = 'Medio_History' AND schema_id = SCHEMA_ID('dbo')) AND NOT EXISTS ( SELECT 1 FROM sys.partitions p JOIN sys.tables t ON t.object_id = p.object_id WHERE t.name = 'Medio_History' AND p.data_compression = 2 ) BEGIN ALTER TABLE dbo.Medio_History REBUILD WITH (DATA_COMPRESSION = PAGE); PRINT 'Medio_History: rebuilt with PAGE compression.'; END GO -- ═══════════════════════════════════════════════════════════════════════ -- 4. SYSTEM_VERSIONING — Seccion -- ═══════════════════════════════════════════════════════════════════════ IF COL_LENGTH('dbo.Seccion', 'ValidFrom') IS NULL BEGIN ALTER TABLE dbo.Seccion ADD ValidFrom DATETIME2(3) GENERATED ALWAYS AS ROW START HIDDEN NOT NULL CONSTRAINT DF_Seccion_ValidFrom DEFAULT(SYSUTCDATETIME()), ValidTo DATETIME2(3) GENERATED ALWAYS AS ROW END HIDDEN NOT NULL CONSTRAINT DF_Seccion_ValidTo DEFAULT(CONVERT(DATETIME2(3), '9999-12-31 23:59:59.999')), PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo); PRINT 'Seccion: PERIOD FOR SYSTEM_TIME added.'; END GO IF NOT EXISTS (SELECT 1 FROM sys.tables WHERE object_id = OBJECT_ID('dbo.Seccion') AND temporal_type = 2) BEGIN ALTER TABLE dbo.Seccion SET (SYSTEM_VERSIONING = ON ( HISTORY_TABLE = dbo.Seccion_History, HISTORY_RETENTION_PERIOD = 10 YEARS )); PRINT 'Seccion: SYSTEM_VERSIONING = ON.'; END ELSE PRINT 'Seccion: SYSTEM_VERSIONING already ON — skip.'; GO IF EXISTS (SELECT 1 FROM sys.tables WHERE name = 'Seccion_History' AND schema_id = SCHEMA_ID('dbo')) AND NOT EXISTS ( SELECT 1 FROM sys.partitions p JOIN sys.tables t ON t.object_id = p.object_id WHERE t.name = 'Seccion_History' AND p.data_compression = 2 ) BEGIN ALTER TABLE dbo.Seccion_History REBUILD WITH (DATA_COMPRESSION = PAGE); PRINT 'Seccion_History: rebuilt with PAGE compression.'; END GO -- ═══════════════════════════════════════════════════════════════════════ -- 5. Permiso nuevo: administracion:secciones:gestionar -- ('administracion:medios:gestionar' ya fue sembrado en V005 — no se toca). -- ═══════════════════════════════════════════════════════════════════════ MERGE dbo.Permiso AS t USING (VALUES ('administracion:secciones:gestionar', N'Gestionar secciones por medio', N'Crear, editar y desactivar secciones de un medio', 'administracion') ) AS s (Codigo, Nombre, Descripcion, Modulo) ON t.Codigo = s.Codigo WHEN NOT MATCHED BY TARGET THEN INSERT (Codigo, Nombre, Descripcion, Modulo) VALUES (s.Codigo, s.Nombre, s.Descripcion, s.Modulo); GO MERGE dbo.RolPermiso AS t USING ( SELECT r.Id AS RolId, p.Id AS PermisoId FROM (VALUES ('admin', 'administracion:secciones:gestionar') ) AS x (RolCodigo, PermisoCodigo) JOIN dbo.Rol r ON r.Codigo = x.RolCodigo JOIN dbo.Permiso p ON p.Codigo = x.PermisoCodigo ) AS s ON t.RolId = s.RolId AND t.PermisoId = s.PermisoId WHEN NOT MATCHED BY TARGET THEN INSERT (RolId, PermisoId) VALUES (s.RolId, s.PermisoId); GO PRINT ''; PRINT 'V011 applied successfully — dbo.Medio + dbo.Seccion (temporal, retention 10y) + permiso secciones.'; PRINT 'Next: V012__seed_medios.sql (seed ELDIA, ELPLATA).'; GO