feat: PRD-002 Product CRUD #40
67
database/migrations/V018_ROLLBACK.sql
Normal file
67
database/migrations/V018_ROLLBACK.sql
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
-- V018_ROLLBACK.sql
|
||||||
|
-- Reversa de V018__create_product.sql — PRD-002.
|
||||||
|
--
|
||||||
|
-- Idempotente: cada paso usa IF EXISTS guards.
|
||||||
|
-- ADVERTENCIA: Ejecutar antes de V017_ROLLBACK (FK desde Product hacia ProductType).
|
||||||
|
|
||||||
|
SET QUOTED_IDENTIFIER ON;
|
||||||
|
SET ANSI_NULLS ON;
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- 1. SYSTEM_VERSIONING OFF
|
||||||
|
IF EXISTS (SELECT 1 FROM sys.tables WHERE object_id = OBJECT_ID('dbo.Product') AND temporal_type = 2)
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE dbo.Product SET (SYSTEM_VERSIONING = OFF);
|
||||||
|
PRINT 'Product: SYSTEM_VERSIONING = OFF.';
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- 2. DROP PERIOD
|
||||||
|
IF EXISTS (SELECT 1 FROM sys.periods WHERE object_id = OBJECT_ID('dbo.Product'))
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE dbo.Product DROP PERIOD FOR SYSTEM_TIME;
|
||||||
|
PRINT 'Product: PERIOD FOR SYSTEM_TIME dropped.';
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- 3. Drop HIDDEN columns + default constraints
|
||||||
|
IF COL_LENGTH('dbo.Product', 'ValidFrom') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE dbo.Product DROP CONSTRAINT IF EXISTS DF_Product_ValidFrom;
|
||||||
|
ALTER TABLE dbo.Product DROP CONSTRAINT IF EXISTS DF_Product_ValidTo;
|
||||||
|
ALTER TABLE dbo.Product DROP COLUMN ValidFrom, ValidTo;
|
||||||
|
PRINT 'Product: ValidFrom/ValidTo columns dropped.';
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- 4. Drop history
|
||||||
|
IF OBJECT_ID(N'dbo.Product_History', N'U') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
DROP TABLE dbo.Product_History;
|
||||||
|
PRINT 'Table dbo.Product_History dropped.';
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- 5. Drop main
|
||||||
|
IF OBJECT_ID(N'dbo.Product', N'U') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
DROP TABLE dbo.Product;
|
||||||
|
PRINT 'Table dbo.Product dropped.';
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- 6. Remove RolPermiso / Permiso
|
||||||
|
DELETE rp FROM dbo.RolPermiso rp
|
||||||
|
JOIN dbo.Permiso p ON p.Id = rp.PermisoId
|
||||||
|
WHERE p.Codigo = 'catalogo:productos:gestionar';
|
||||||
|
PRINT 'RolPermiso rows for catalogo:productos:gestionar deleted.';
|
||||||
|
GO
|
||||||
|
|
||||||
|
DELETE FROM dbo.Permiso WHERE Codigo = 'catalogo:productos:gestionar';
|
||||||
|
PRINT 'Permiso catalogo:productos:gestionar deleted.';
|
||||||
|
GO
|
||||||
|
|
||||||
|
PRINT '';
|
||||||
|
PRINT 'V018 rolled back successfully.';
|
||||||
|
GO
|
||||||
172
database/migrations/V018__create_product.sql
Normal file
172
database/migrations/V018__create_product.sql
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
-- V018__create_product.sql
|
||||||
|
-- PRD-002: Product — entidad vendible concreta del catálogo comercial.
|
||||||
|
--
|
||||||
|
-- Cambios:
|
||||||
|
-- 1. dbo.Product (FK Medio/ProductType/Rubro, SYSTEM_VERSIONING ON, retention 10 años).
|
||||||
|
-- 2. Índices: filtered UQ por (MedioId, ProductTypeId, Nombre) activos; cover por ProductTypeId
|
||||||
|
-- (para IProductQueryRepository); cover por MedioId; cover filtrado por RubroId.
|
||||||
|
-- 3. Permiso 'catalogo:productos:gestionar' + asignación a rol 'admin'.
|
||||||
|
--
|
||||||
|
-- Patrón: V017 (dbo.ProductType con SYSTEM_VERSIONING + PAGE compression + MERGE permisos).
|
||||||
|
-- Idempotente: seguro para re-ejecutar.
|
||||||
|
-- Reversa: V018_ROLLBACK.sql.
|
||||||
|
-- Run on: SIGCM2 (dev) y SIGCM2_Test (integration tests).
|
||||||
|
--
|
||||||
|
-- Notas:
|
||||||
|
-- - SIN seed de datos — PRD-008 (V019) seedea los 12 productos legacy.
|
||||||
|
-- - Validación de flags (RequiresCategory, HasDuration) vive en Application layer:
|
||||||
|
-- un ProductType puede cambiar flags; la Product queda en estado snapshot.
|
||||||
|
-- - UQ filtered WHERE IsActive=1: permite reusar nombres tras soft-delete.
|
||||||
|
--
|
||||||
|
-- SDD Design: engram sdd/prd-002-product-crud/design
|
||||||
|
|
||||||
|
SET QUOTED_IDENTIFIER ON;
|
||||||
|
SET ANSI_NULLS ON;
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- ═══════════════════════════════════════════════════════════════════════
|
||||||
|
-- 1. dbo.Product
|
||||||
|
-- ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'dbo.Product', N'U') IS NULL
|
||||||
|
BEGIN
|
||||||
|
CREATE TABLE dbo.Product (
|
||||||
|
Id INT IDENTITY(1,1) NOT NULL CONSTRAINT PK_Product PRIMARY KEY,
|
||||||
|
Nombre NVARCHAR(300) COLLATE SQL_Latin1_General_CP1_CI_AI NOT NULL,
|
||||||
|
MedioId INT NOT NULL,
|
||||||
|
ProductTypeId INT NOT NULL,
|
||||||
|
RubroId INT NULL,
|
||||||
|
BasePrice DECIMAL(18,4) NOT NULL,
|
||||||
|
PriceDurationDays INT NULL,
|
||||||
|
IsActive BIT NOT NULL CONSTRAINT DF_Product_IsActive DEFAULT(1),
|
||||||
|
FechaCreacion DATETIME2(3) NOT NULL CONSTRAINT DF_Product_FechaCreacion DEFAULT(SYSUTCDATETIME()),
|
||||||
|
FechaModificacion DATETIME2(3) NULL,
|
||||||
|
CONSTRAINT FK_Product_Medio FOREIGN KEY (MedioId) REFERENCES dbo.Medio(Id) ON DELETE NO ACTION,
|
||||||
|
CONSTRAINT FK_Product_ProductType FOREIGN KEY (ProductTypeId) REFERENCES dbo.ProductType(Id) ON DELETE NO ACTION,
|
||||||
|
CONSTRAINT FK_Product_Rubro FOREIGN KEY (RubroId) REFERENCES dbo.Rubro(Id) ON DELETE NO ACTION,
|
||||||
|
CONSTRAINT CK_Product_BasePrice_NonNegative CHECK (BasePrice >= 0),
|
||||||
|
CONSTRAINT CK_Product_PriceDurationDays_Positive CHECK (PriceDurationDays IS NULL OR PriceDurationDays >= 1)
|
||||||
|
);
|
||||||
|
PRINT 'Table dbo.Product created.';
|
||||||
|
END
|
||||||
|
ELSE
|
||||||
|
PRINT 'Table dbo.Product already exists — skip.';
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- ═══════════════════════════════════════════════════════════════════════
|
||||||
|
-- 2. SYSTEM_VERSIONING — Product
|
||||||
|
-- ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
IF COL_LENGTH('dbo.Product', 'ValidFrom') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE dbo.Product
|
||||||
|
ADD
|
||||||
|
ValidFrom DATETIME2(3) GENERATED ALWAYS AS ROW START HIDDEN NOT NULL
|
||||||
|
CONSTRAINT DF_Product_ValidFrom DEFAULT(SYSUTCDATETIME()),
|
||||||
|
ValidTo DATETIME2(3) GENERATED ALWAYS AS ROW END HIDDEN NOT NULL
|
||||||
|
CONSTRAINT DF_Product_ValidTo DEFAULT(CONVERT(DATETIME2(3), '9999-12-31 23:59:59.999')),
|
||||||
|
PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo);
|
||||||
|
PRINT 'Product: PERIOD FOR SYSTEM_TIME added.';
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM sys.tables WHERE object_id = OBJECT_ID('dbo.Product') AND temporal_type = 2)
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE dbo.Product
|
||||||
|
SET (SYSTEM_VERSIONING = ON (
|
||||||
|
HISTORY_TABLE = dbo.Product_History,
|
||||||
|
HISTORY_RETENTION_PERIOD = 10 YEARS
|
||||||
|
));
|
||||||
|
PRINT 'Product: SYSTEM_VERSIONING = ON (history: dbo.Product_History, retention: 10 years).';
|
||||||
|
END
|
||||||
|
ELSE
|
||||||
|
PRINT 'Product: SYSTEM_VERSIONING already ON — skip.';
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF EXISTS (SELECT 1 FROM sys.tables WHERE name = 'Product_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 = 'Product_History' AND p.data_compression = 2
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE dbo.Product_History REBUILD WITH (DATA_COMPRESSION = PAGE);
|
||||||
|
PRINT 'Product_History: rebuilt with PAGE compression.';
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- ═══════════════════════════════════════════════════════════════════════
|
||||||
|
-- 3. Índices
|
||||||
|
-- ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
-- Filtered UQ: unicidad activa por (Medio, Tipo, Nombre). Permite reusar nombres tras soft-delete.
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'UQ_Product_MedioId_ProductTypeId_Nombre_Active' AND object_id = OBJECT_ID('dbo.Product'))
|
||||||
|
BEGIN
|
||||||
|
CREATE UNIQUE INDEX UQ_Product_MedioId_ProductTypeId_Nombre_Active
|
||||||
|
ON dbo.Product (MedioId, ProductTypeId, Nombre)
|
||||||
|
WHERE IsActive = 1;
|
||||||
|
PRINT 'Index UQ_Product_MedioId_ProductTypeId_Nombre_Active created.';
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- Cover para IProductQueryRepository.ExistsActiveByProductTypeAsync
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'IX_Product_ProductTypeId_IsActive' AND object_id = OBJECT_ID('dbo.Product'))
|
||||||
|
BEGIN
|
||||||
|
CREATE INDEX IX_Product_ProductTypeId_IsActive
|
||||||
|
ON dbo.Product (ProductTypeId, IsActive);
|
||||||
|
PRINT 'Index IX_Product_ProductTypeId_IsActive created.';
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- Cover para list filtered by MedioId
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'IX_Product_MedioId_IsActive' AND object_id = OBJECT_ID('dbo.Product'))
|
||||||
|
BEGIN
|
||||||
|
CREATE INDEX IX_Product_MedioId_IsActive
|
||||||
|
ON dbo.Product (MedioId, IsActive);
|
||||||
|
PRINT 'Index IX_Product_MedioId_IsActive created.';
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- Cover para list filtered by RubroId
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'IX_Product_RubroId_IsActive' AND object_id = OBJECT_ID('dbo.Product'))
|
||||||
|
BEGIN
|
||||||
|
CREATE INDEX IX_Product_RubroId_IsActive
|
||||||
|
ON dbo.Product (RubroId, IsActive)
|
||||||
|
WHERE RubroId IS NOT NULL;
|
||||||
|
PRINT 'Index IX_Product_RubroId_IsActive created.';
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- ═══════════════════════════════════════════════════════════════════════
|
||||||
|
-- 4. Permiso: catalogo:productos:gestionar + asignación a rol 'admin'
|
||||||
|
-- ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
MERGE dbo.Permiso AS t
|
||||||
|
USING (VALUES
|
||||||
|
('catalogo:productos:gestionar',
|
||||||
|
N'Gestionar productos del catálogo',
|
||||||
|
N'Crear, editar y desactivar productos del catálogo comercial',
|
||||||
|
'catalogo')
|
||||||
|
) 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', 'catalogo:productos: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 'V018 applied — dbo.Product (temporal, retention 10y) + permiso catalogo:productos:gestionar.';
|
||||||
|
PRINT 'Next: V019 (PRD-008 — seed 12 productos legacy).';
|
||||||
|
GO
|
||||||
@@ -63,6 +63,9 @@ public sealed class SqlTestFixture : IAsyncLifetime
|
|||||||
// V017 (PRD-001): ensure dbo.ProductType + temporal + permiso 'catalogo:tipos:gestionar'.
|
// V017 (PRD-001): ensure dbo.ProductType + temporal + permiso 'catalogo:tipos:gestionar'.
|
||||||
await EnsureV017SchemaAsync();
|
await EnsureV017SchemaAsync();
|
||||||
|
|
||||||
|
// V018 (PRD-002): ensure dbo.Product + temporal + permiso 'catalogo:productos:gestionar'.
|
||||||
|
await EnsureV018SchemaAsync();
|
||||||
|
|
||||||
_respawner = await Respawner.CreateAsync(_connection, new RespawnerOptions
|
_respawner = await Respawner.CreateAsync(_connection, new RespawnerOptions
|
||||||
{
|
{
|
||||||
DbAdapter = DbAdapter.SqlServer,
|
DbAdapter = DbAdapter.SqlServer,
|
||||||
@@ -91,6 +94,8 @@ public sealed class SqlTestFixture : IAsyncLifetime
|
|||||||
new Respawn.Graph.Table("dbo", "Rubro_History"),
|
new Respawn.Graph.Table("dbo", "Rubro_History"),
|
||||||
// PRD-001 (V017): ProductType es temporal — history no puede deletearse directo.
|
// PRD-001 (V017): ProductType es temporal — history no puede deletearse directo.
|
||||||
new Respawn.Graph.Table("dbo", "ProductType_History"),
|
new Respawn.Graph.Table("dbo", "ProductType_History"),
|
||||||
|
// PRD-002 (V018): Product es temporal — history no puede deletearse directo.
|
||||||
|
new Respawn.Graph.Table("dbo", "Product_History"),
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -213,7 +218,11 @@ public sealed class SqlTestFixture : IAsyncLifetime
|
|||||||
-- V014 (ADM-009): permiso para tablas fiscales
|
-- V014 (ADM-009): permiso para tablas fiscales
|
||||||
('administracion:fiscal:gestionar', N'Gestionar tablas fiscales', N'Gestionar tablas fiscales (IVA, IIBB)', 'administracion'),
|
('administracion:fiscal:gestionar', N'Gestionar tablas fiscales', N'Gestionar tablas fiscales (IVA, IIBB)', 'administracion'),
|
||||||
-- V016 (CAT-001): permiso para gestionar árbol de rubros
|
-- V016 (CAT-001): permiso para gestionar árbol de rubros
|
||||||
('catalogo:rubros:gestionar', N'Gestionar rubros del catálogo', N'Crear, editar, mover y desactivar rubros del árbol de catálogo comercial', 'catalogo')
|
('catalogo:rubros:gestionar', N'Gestionar rubros del catálogo', N'Crear, editar, mover y desactivar rubros del árbol de catálogo comercial', 'catalogo'),
|
||||||
|
-- V017 (PRD-001): permiso para gestionar tipos de producto
|
||||||
|
('catalogo:tipos:gestionar', N'Gestionar tipos de producto', N'Crear, editar y desactivar ProductTypes del catálogo (flags + límites multimedia)', 'catalogo'),
|
||||||
|
-- V018 (PRD-002): permiso para gestionar productos del catálogo
|
||||||
|
('catalogo:productos:gestionar', N'Gestionar productos del catálogo', N'Crear, editar y desactivar productos del catálogo comercial', 'catalogo')
|
||||||
) AS s (Codigo, Nombre, Descripcion, Modulo)
|
) AS s (Codigo, Nombre, Descripcion, Modulo)
|
||||||
ON t.Codigo = s.Codigo
|
ON t.Codigo = s.Codigo
|
||||||
WHEN NOT MATCHED BY TARGET THEN
|
WHEN NOT MATCHED BY TARGET THEN
|
||||||
@@ -261,6 +270,10 @@ public sealed class SqlTestFixture : IAsyncLifetime
|
|||||||
('admin', 'administracion:fiscal:gestionar'),
|
('admin', 'administracion:fiscal:gestionar'),
|
||||||
-- V016 (CAT-001)
|
-- V016 (CAT-001)
|
||||||
('admin', 'catalogo:rubros:gestionar'),
|
('admin', 'catalogo:rubros:gestionar'),
|
||||||
|
-- V017 (PRD-001)
|
||||||
|
('admin', 'catalogo:tipos:gestionar'),
|
||||||
|
-- V018 (PRD-002)
|
||||||
|
('admin', 'catalogo:productos:gestionar'),
|
||||||
('cajero', 'ventas:contado:crear'),
|
('cajero', 'ventas:contado:crear'),
|
||||||
('cajero', 'ventas:contado:modificar'),
|
('cajero', 'ventas:contado:modificar'),
|
||||||
('cajero', 'ventas:contado:cobrar'),
|
('cajero', 'ventas:contado:cobrar'),
|
||||||
@@ -1011,4 +1024,102 @@ public sealed class SqlTestFixture : IAsyncLifetime
|
|||||||
await _connection.ExecuteAsync(createUqIndex);
|
await _connection.ExecuteAsync(createUqIndex);
|
||||||
await _connection.ExecuteAsync(createCoveringIndex);
|
await _connection.ExecuteAsync(createCoveringIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PRD-002 (V018): applies dbo.Product schema + temporal + filtered UQ + covering indexes
|
||||||
|
/// idempotentemente. Mirrors V018__create_product.sql.
|
||||||
|
/// Permiso 'catalogo:productos:gestionar' y asignación a admin se siembran
|
||||||
|
/// desde SeedPermisosCanonicalAsync / SeedRolPermisosCanonicalAsync (post-respawn).
|
||||||
|
/// </summary>
|
||||||
|
public async Task EnsureV018SchemaAsync()
|
||||||
|
{
|
||||||
|
const string createProduct = """
|
||||||
|
IF OBJECT_ID(N'dbo.Product', N'U') IS NULL
|
||||||
|
BEGIN
|
||||||
|
CREATE TABLE dbo.Product (
|
||||||
|
Id INT IDENTITY(1,1) NOT NULL CONSTRAINT PK_Product PRIMARY KEY,
|
||||||
|
Nombre NVARCHAR(300) COLLATE SQL_Latin1_General_CP1_CI_AI NOT NULL,
|
||||||
|
MedioId INT NOT NULL,
|
||||||
|
ProductTypeId INT NOT NULL,
|
||||||
|
RubroId INT NULL,
|
||||||
|
BasePrice DECIMAL(18,4) NOT NULL,
|
||||||
|
PriceDurationDays INT NULL,
|
||||||
|
IsActive BIT NOT NULL CONSTRAINT DF_Product_IsActive DEFAULT(1),
|
||||||
|
FechaCreacion DATETIME2(3) NOT NULL CONSTRAINT DF_Product_FechaCreacion DEFAULT(SYSUTCDATETIME()),
|
||||||
|
FechaModificacion DATETIME2(3) NULL,
|
||||||
|
CONSTRAINT FK_Product_Medio FOREIGN KEY (MedioId) REFERENCES dbo.Medio(Id) ON DELETE NO ACTION,
|
||||||
|
CONSTRAINT FK_Product_ProductType FOREIGN KEY (ProductTypeId) REFERENCES dbo.ProductType(Id) ON DELETE NO ACTION,
|
||||||
|
CONSTRAINT FK_Product_Rubro FOREIGN KEY (RubroId) REFERENCES dbo.Rubro(Id) ON DELETE NO ACTION,
|
||||||
|
CONSTRAINT CK_Product_BasePrice_NonNegative CHECK (BasePrice >= 0),
|
||||||
|
CONSTRAINT CK_Product_PriceDurationDays_Positive CHECK (PriceDurationDays IS NULL OR PriceDurationDays >= 1)
|
||||||
|
);
|
||||||
|
END
|
||||||
|
""";
|
||||||
|
|
||||||
|
const string addProductPeriod = """
|
||||||
|
IF COL_LENGTH('dbo.Product', 'ValidFrom') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE dbo.Product
|
||||||
|
ADD
|
||||||
|
ValidFrom DATETIME2(3) GENERATED ALWAYS AS ROW START HIDDEN NOT NULL
|
||||||
|
CONSTRAINT DF_Product_ValidFrom DEFAULT(SYSUTCDATETIME()),
|
||||||
|
ValidTo DATETIME2(3) GENERATED ALWAYS AS ROW END HIDDEN NOT NULL
|
||||||
|
CONSTRAINT DF_Product_ValidTo DEFAULT(CONVERT(DATETIME2(3), '9999-12-31 23:59:59.999')),
|
||||||
|
PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo);
|
||||||
|
END
|
||||||
|
""";
|
||||||
|
|
||||||
|
const string setProductVersioning = """
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM sys.tables WHERE object_id = OBJECT_ID('dbo.Product') AND temporal_type = 2)
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE dbo.Product
|
||||||
|
SET (SYSTEM_VERSIONING = ON (
|
||||||
|
HISTORY_TABLE = dbo.Product_History,
|
||||||
|
HISTORY_RETENTION_PERIOD = 10 YEARS
|
||||||
|
));
|
||||||
|
END
|
||||||
|
""";
|
||||||
|
|
||||||
|
const string createUqIndex = """
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'UQ_Product_MedioId_ProductTypeId_Nombre_Active' AND object_id = OBJECT_ID('dbo.Product'))
|
||||||
|
BEGIN
|
||||||
|
CREATE UNIQUE INDEX UQ_Product_MedioId_ProductTypeId_Nombre_Active
|
||||||
|
ON dbo.Product (MedioId, ProductTypeId, Nombre)
|
||||||
|
WHERE IsActive = 1;
|
||||||
|
END
|
||||||
|
""";
|
||||||
|
|
||||||
|
const string createProductTypeIdx = """
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'IX_Product_ProductTypeId_IsActive' AND object_id = OBJECT_ID('dbo.Product'))
|
||||||
|
BEGIN
|
||||||
|
CREATE INDEX IX_Product_ProductTypeId_IsActive
|
||||||
|
ON dbo.Product (ProductTypeId, IsActive);
|
||||||
|
END
|
||||||
|
""";
|
||||||
|
|
||||||
|
const string createMedioIdx = """
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'IX_Product_MedioId_IsActive' AND object_id = OBJECT_ID('dbo.Product'))
|
||||||
|
BEGIN
|
||||||
|
CREATE INDEX IX_Product_MedioId_IsActive
|
||||||
|
ON dbo.Product (MedioId, IsActive);
|
||||||
|
END
|
||||||
|
""";
|
||||||
|
|
||||||
|
const string createRubroIdx = """
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'IX_Product_RubroId_IsActive' AND object_id = OBJECT_ID('dbo.Product'))
|
||||||
|
BEGIN
|
||||||
|
CREATE INDEX IX_Product_RubroId_IsActive
|
||||||
|
ON dbo.Product (RubroId, IsActive)
|
||||||
|
WHERE RubroId IS NOT NULL;
|
||||||
|
END
|
||||||
|
""";
|
||||||
|
|
||||||
|
await _connection.ExecuteAsync(createProduct);
|
||||||
|
await _connection.ExecuteAsync(addProductPeriod);
|
||||||
|
await _connection.ExecuteAsync(setProductVersioning);
|
||||||
|
await _connection.ExecuteAsync(createUqIndex);
|
||||||
|
await _connection.ExecuteAsync(createProductTypeIdx);
|
||||||
|
await _connection.ExecuteAsync(createMedioIdx);
|
||||||
|
await _connection.ExecuteAsync(createRubroIdx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user