feat(api): UDT-004 dominio + repositorio + application roles (tdd)
- Migraciones V003 (tabla Rol + 8 seeds canonicos) y V004 (drop CK + FK Usuario.Rol) - Dominio: Rol entity + 3 excepciones (RolNotFound/AlreadyExists/InUse) - Infraestructura: RolRepository (Dapper) con List/Get/ExistsActive/Add/Update/HasActiveUsuarios - Application: CRUD queries y commands (List, Get, Create, Update, Deactivate) + validators (codigo regex ^[a-z][a-z0-9_]*$) - Validator UDT-003: whitelist alineada a codigos canonicos (full IRolRepository lookup diferido a Phase 5.1) - Tests: 169 application + 15 api (todos verdes). Respawn configurado para re-seedear Rol canonical post-reset. - Estricto TDD: RED/GREEN/TRIANGULATE en todos los handlers nuevos.
This commit is contained in:
58
database/migrations/V003__create_rol.sql
Normal file
58
database/migrations/V003__create_rol.sql
Normal file
@@ -0,0 +1,58 @@
|
||||
-- V003__create_rol.sql
|
||||
-- Creates dbo.Rol master table (referenced by Usuario.Rol via FK in V004) and seeds
|
||||
-- the 8 canonical business roles (RBAC doc §2.4.2).
|
||||
-- Run on: SIGCM2 (prod) and SIGCM2_Test (integration tests)
|
||||
|
||||
SET QUOTED_IDENTIFIER ON;
|
||||
SET ANSI_NULLS ON;
|
||||
SET NOCOUNT ON;
|
||||
GO
|
||||
|
||||
IF OBJECT_ID(N'dbo.Rol', N'U') IS NULL
|
||||
BEGIN
|
||||
CREATE TABLE dbo.Rol
|
||||
(
|
||||
Id INT IDENTITY(1,1) NOT NULL CONSTRAINT PK_Rol PRIMARY KEY,
|
||||
Codigo VARCHAR(30) NOT NULL,
|
||||
Nombre NVARCHAR(60) NOT NULL,
|
||||
Descripcion NVARCHAR(250) NULL,
|
||||
Activo BIT NOT NULL CONSTRAINT DF_Rol_Activo DEFAULT(1),
|
||||
FechaCreacion DATETIME2(3) NOT NULL CONSTRAINT DF_Rol_FC DEFAULT(SYSUTCDATETIME()),
|
||||
FechaModificacion DATETIME2(3) NULL,
|
||||
CONSTRAINT UQ_Rol_Codigo UNIQUE (Codigo),
|
||||
-- Codigo format: lowercase letter followed by lowercase letters, digits or underscore.
|
||||
-- Using binary collation to enforce case-sensitivity (default DB collation is case-insensitive).
|
||||
CONSTRAINT CK_Rol_Codigo_Format CHECK (
|
||||
PATINDEX('[a-z]%', Codigo COLLATE Latin1_General_BIN2) = 1
|
||||
AND PATINDEX('%[^a-z0-9_]%', Codigo COLLATE Latin1_General_BIN2) = 0
|
||||
)
|
||||
);
|
||||
|
||||
PRINT 'Table dbo.Rol created successfully.';
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
PRINT 'Table dbo.Rol already exists — skipping create.';
|
||||
END
|
||||
GO
|
||||
|
||||
-- Seed 8 canonical roles (idempotent).
|
||||
MERGE dbo.Rol AS target
|
||||
USING (VALUES
|
||||
('admin', N'Administrador', N'Supervisor total del sistema'),
|
||||
('cajero', N'Cajero', N'Atención de mostrador, contado'),
|
||||
('operador_ctacte', N'Operador Cta Cte', N'Gestión de cuenta corriente'),
|
||||
('picadora', N'Picadora/Correctora', N'Edición de textos y corrección'),
|
||||
('jefe_publicidad', N'Jefe de Publicidad', N'Supervisión de pauta y recursos'),
|
||||
('productor', N'Productor', N'Consulta y carga restringida'),
|
||||
('diagramacion', N'Diagramación/Taller', N'Solo lectura de pauta'),
|
||||
('reportes', N'Reportes', N'Solo lectura de reportes y estadísticas')
|
||||
) AS source (Codigo, Nombre, Descripcion)
|
||||
ON target.Codigo = source.Codigo
|
||||
WHEN NOT MATCHED BY TARGET THEN
|
||||
INSERT (Codigo, Nombre, Descripcion, Activo)
|
||||
VALUES (source.Codigo, source.Nombre, source.Descripcion, 1);
|
||||
GO
|
||||
|
||||
PRINT 'Rol seeds applied (8 canonical roles).';
|
||||
GO
|
||||
44
database/migrations/V004__alter_usuario_rol_fk.sql
Normal file
44
database/migrations/V004__alter_usuario_rol_fk.sql
Normal file
@@ -0,0 +1,44 @@
|
||||
-- V004__alter_usuario_rol_fk.sql
|
||||
-- Replaces the hardcoded CHECK constraint on Usuario.Rol with a FOREIGN KEY
|
||||
-- against dbo.Rol(Codigo). Must run AFTER V003 (which creates dbo.Rol and seeds the
|
||||
-- codes already in use, including 'admin').
|
||||
-- Run on: SIGCM2 (prod) and SIGCM2_Test (integration tests)
|
||||
|
||||
SET QUOTED_IDENTIFIER ON;
|
||||
SET ANSI_NULLS ON;
|
||||
SET NOCOUNT ON;
|
||||
GO
|
||||
|
||||
-- 1) Drop the old hardcoded whitelist CHECK constraint (if still present).
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM sys.check_constraints
|
||||
WHERE name = 'CK_Usuario_Rol'
|
||||
AND parent_object_id = OBJECT_ID(N'dbo.Usuario')
|
||||
)
|
||||
BEGIN
|
||||
ALTER TABLE dbo.Usuario DROP CONSTRAINT CK_Usuario_Rol;
|
||||
PRINT 'Dropped CK_Usuario_Rol (hardcoded whitelist).';
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
PRINT 'CK_Usuario_Rol not present — skipping drop.';
|
||||
END
|
||||
GO
|
||||
|
||||
-- 2) Add the FK Usuario.Rol -> Rol.Codigo (only if not already present).
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM sys.foreign_keys
|
||||
WHERE name = 'FK_Usuario_Rol'
|
||||
AND parent_object_id = OBJECT_ID(N'dbo.Usuario')
|
||||
)
|
||||
BEGIN
|
||||
ALTER TABLE dbo.Usuario
|
||||
ADD CONSTRAINT FK_Usuario_Rol
|
||||
FOREIGN KEY (Rol) REFERENCES dbo.Rol(Codigo);
|
||||
PRINT 'Added FK_Usuario_Rol -> dbo.Rol(Codigo).';
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
PRINT 'FK_Usuario_Rol already present — skipping.';
|
||||
END
|
||||
GO
|
||||
Reference in New Issue
Block a user