From 40482caf7b90f6b784a44734d74ae6010b50d7de Mon Sep 17 00:00:00 2001 From: dmolinari Date: Fri, 17 Apr 2026 14:16:01 -0300 Subject: [PATCH] =?UTF-8?q?revert(db):=20eliminar=20SecuenciaComprobante?= =?UTF-8?q?=20+=20SP=20de=20V013=20=E2=80=94=20IMAC=20asigna=20numeros=20A?= =?UTF-8?q?FIP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SecuenciaComprobante, usp_ReservarNumeroComprobante y TipoComprobante no tienen propósito de negocio: IMAC/Infogestión asigna NumeroFactura+CAI externamente. V013 ahora solo gestiona PuntoDeVenta + temporal table + permiso AFIP. Sección 0 aplica drops idempotentes para limpiar SIGCM2_Test y reinstalaciones. --- database/migrations/V013_ROLLBACK.sql | 68 +----- .../V013__create_puntos_de_venta.sql | 215 ++++-------------- 2 files changed, 56 insertions(+), 227 deletions(-) diff --git a/database/migrations/V013_ROLLBACK.sql b/database/migrations/V013_ROLLBACK.sql index 8226e72..5da66c8 100644 --- a/database/migrations/V013_ROLLBACK.sql +++ b/database/migrations/V013_ROLLBACK.sql @@ -1,12 +1,15 @@ -- V013_ROLLBACK.sql -- Reversa de V013__create_puntos_de_venta.sql. -- --- ADVERTENCIA: ejecutar ELIMINA PuntoDeVenta, SecuenciaComprobante, su historia temporal, --- el permiso 'administracion:puntos_de_venta:gestionar', sus asignaciones y el SP. +-- ADVERTENCIA: ejecutar ELIMINA PuntoDeVenta, su historia temporal, +-- el permiso 'administracion:puntos_de_venta:gestionar' y sus asignaciones. -- -- Uso intended: ROLLBACK en entornos NO-productivos. -- Prerequisito: no deben existir FKs vivas apuntando a PuntoDeVenta (p.ej., comprobantes FAC-001). -- Si FAC-001 ya está aplicado, este rollback fallará — usar backup. +-- +-- NOTA: SecuenciaComprobante y SP usp_ReservarNumeroComprobante ya no forman parte +-- de V013 (eliminados en cirugía post-smoke Batch 9). Este rollback solo maneja PuntoDeVenta. SET QUOTED_IDENTIFIER ON; SET ANSI_NULLS ON; @@ -14,52 +17,7 @@ SET NOCOUNT ON; GO -- ═══════════════════════════════════════════════════════════════════════ --- 1. Drop SP --- ═══════════════════════════════════════════════════════════════════════ - -IF OBJECT_ID(N'dbo.usp_ReservarNumeroComprobante', N'P') IS NOT NULL -BEGIN - DROP PROCEDURE dbo.usp_ReservarNumeroComprobante; - PRINT 'SP dbo.usp_ReservarNumeroComprobante dropped.'; -END -GO - --- ═══════════════════════════════════════════════════════════════════════ --- 2. Apagar SYSTEM_VERSIONING + remover PERIOD — SecuenciaComprobante primero (FK a PdV) --- ═══════════════════════════════════════════════════════════════════════ - -IF EXISTS (SELECT 1 FROM sys.tables WHERE object_id = OBJECT_ID('dbo.SecuenciaComprobante') AND temporal_type = 2) -BEGIN - ALTER TABLE dbo.SecuenciaComprobante SET (SYSTEM_VERSIONING = OFF); - PRINT 'SecuenciaComprobante: SYSTEM_VERSIONING OFF.'; -END -GO - -IF EXISTS (SELECT 1 FROM sys.periods WHERE object_id = OBJECT_ID('dbo.SecuenciaComprobante')) -BEGIN - ALTER TABLE dbo.SecuenciaComprobante DROP PERIOD FOR SYSTEM_TIME; - PRINT 'SecuenciaComprobante: PERIOD FOR SYSTEM_TIME dropped.'; -END -GO - -IF COL_LENGTH('dbo.SecuenciaComprobante', 'ValidFrom') IS NOT NULL -BEGIN - ALTER TABLE dbo.SecuenciaComprobante DROP CONSTRAINT IF EXISTS DF_SecuenciaComprobante_ValidFrom; - ALTER TABLE dbo.SecuenciaComprobante DROP CONSTRAINT IF EXISTS DF_SecuenciaComprobante_ValidTo; - ALTER TABLE dbo.SecuenciaComprobante DROP COLUMN ValidFrom, ValidTo; - PRINT 'SecuenciaComprobante: ValidFrom/ValidTo dropped.'; -END -GO - -IF OBJECT_ID(N'dbo.SecuenciaComprobante_History', N'U') IS NOT NULL -BEGIN - DROP TABLE dbo.SecuenciaComprobante_History; - PRINT 'SecuenciaComprobante_History dropped.'; -END -GO - --- ═══════════════════════════════════════════════════════════════════════ --- 3. Apagar SYSTEM_VERSIONING + remover PERIOD — PuntoDeVenta +-- 1. Apagar SYSTEM_VERSIONING + remover PERIOD — PuntoDeVenta -- ═══════════════════════════════════════════════════════════════════════ IF EXISTS (SELECT 1 FROM sys.tables WHERE object_id = OBJECT_ID('dbo.PuntoDeVenta') AND temporal_type = 2) @@ -93,16 +51,9 @@ END GO -- ═══════════════════════════════════════════════════════════════════════ --- 4. Drop tablas (SecuenciaComprobante primero por FK) +-- 2. Drop tabla PuntoDeVenta -- ═══════════════════════════════════════════════════════════════════════ -IF OBJECT_ID(N'dbo.SecuenciaComprobante', N'U') IS NOT NULL -BEGIN - DROP TABLE dbo.SecuenciaComprobante; - PRINT 'Table dbo.SecuenciaComprobante dropped.'; -END -GO - IF OBJECT_ID(N'dbo.PuntoDeVenta', N'U') IS NOT NULL BEGIN DROP TABLE dbo.PuntoDeVenta; @@ -111,7 +62,7 @@ END GO -- ═══════════════════════════════════════════════════════════════════════ --- 5. Remover permiso 'administracion:puntos_de_venta:gestionar' + RolPermiso +-- 3. Remover permiso 'administracion:puntos_de_venta:gestionar' + RolPermiso -- ═══════════════════════════════════════════════════════════════════════ DELETE rp @@ -125,7 +76,6 @@ WHERE Codigo = 'administracion:puntos_de_venta:gestionar'; GO PRINT ''; -PRINT 'V013 rolled back. dbo.PuntoDeVenta, dbo.SecuenciaComprobante and their history removed.'; -PRINT 'SP dbo.usp_ReservarNumeroComprobante removed.'; +PRINT 'V013 rolled back. dbo.PuntoDeVenta and its history removed.'; PRINT 'Permiso administracion:puntos_de_venta:gestionar removed.'; GO diff --git a/database/migrations/V013__create_puntos_de_venta.sql b/database/migrations/V013__create_puntos_de_venta.sql index d0c8738..dd2bcbc 100644 --- a/database/migrations/V013__create_puntos_de_venta.sql +++ b/database/migrations/V013__create_puntos_de_venta.sql @@ -1,11 +1,16 @@ -- V013__create_puntos_de_venta.sql --- ADM-008 Puntos de Venta: DDL + SP de reserva atómica de número de comprobante. +-- ADM-008 Puntos de Venta: DDL para dbo.PuntoDeVenta + permiso AFIP. +-- +-- NOTA POST-SMOKE (Batch 9): SecuenciaComprobante, SP usp_ReservarNumeroComprobante +-- y TipoComprobante fueron eliminados. SIG-CM2.0 NO genera números AFIP — IMAC +-- (Plataforma Infogestión) los asigna externamente. Un worker futuro (INT-001) +-- polleará la vista de Infogestión para asociar NumeroOrdenInterno ↔ NumeroFacturaAFIP + CAI. +-- PuntoDeVenta.NumeroAFIP es config fija que se manda en el payload a IMAC. -- -- Cambios: -- 1. dbo.PuntoDeVenta (FK→Medio, UNIQUE(MedioId,NumeroAFIP), SYSTEM_VERSIONING ON, retention 10Y). --- 2. dbo.SecuenciaComprobante (FK→PuntoDeVenta, PK(PuntoDeVentaId,TipoComprobante), SYSTEM_VERSIONING ON, retention 10Y). --- 3. Permiso 'administracion:puntos-de-venta:gestionar' + asignación a rol 'admin'. --- 4. SP dbo.usp_ReservarNumeroComprobante (SERIALIZABLE, UPDATE+OUTPUT, THROW 50001/50002/50003). +-- 2. Drops idempotentes de artefactos de versión previa (SecuenciaComprobante + SP). +-- 3. Permiso 'administracion:puntos_de_venta:gestionar' + asignación a rol 'admin'. -- -- Patrón: V011 (Temporal Tables + Permiso MERGE + PAGE compression en history). -- Idempotente: seguro para re-ejecutar. @@ -18,7 +23,7 @@ -- solo permite [a-z0-9_:] — se usa guion_bajo para cumplir la constraint existente. -- El backend y frontend deben usar el código con guion_bajo. -- --- Covers: REQ-PDV-001, -003, -009, REQ-SEC-CMB-001, -002, -003, -004 +-- Covers: REQ-PDV-001, -003, -009 -- Source of truth: Obsidian/02-ARQUITECTURA-y-TECH-STACK/2.10 ADM-008 -- -- NOTA T1.3 — Seeds: NO se seedean PuntoDeVenta. @@ -30,6 +35,39 @@ SET ANSI_NULLS ON; SET NOCOUNT ON; GO +-- ═══════════════════════════════════════════════════════════════════════ +-- 0. Drops idempotentes de artefactos de versión previa +-- (SecuenciaComprobante + SP — eliminados en cirugía post-smoke Batch 9) +-- ═══════════════════════════════════════════════════════════════════════ + +IF OBJECT_ID(N'dbo.usp_ReservarNumeroComprobante', N'P') IS NOT NULL +BEGIN + DROP PROCEDURE dbo.usp_ReservarNumeroComprobante; + PRINT 'SP dbo.usp_ReservarNumeroComprobante dropped (cleanup).'; +END +GO + +IF EXISTS (SELECT 1 FROM sys.tables WHERE object_id = OBJECT_ID('dbo.SecuenciaComprobante') AND temporal_type = 2) +BEGIN + ALTER TABLE dbo.SecuenciaComprobante SET (SYSTEM_VERSIONING = OFF); + PRINT 'SecuenciaComprobante: SYSTEM_VERSIONING = OFF (cleanup).'; +END +GO + +IF OBJECT_ID(N'dbo.SecuenciaComprobante_History', N'U') IS NOT NULL +BEGIN + DROP TABLE dbo.SecuenciaComprobante_History; + PRINT 'SecuenciaComprobante_History dropped (cleanup).'; +END +GO + +IF OBJECT_ID(N'dbo.SecuenciaComprobante', N'U') IS NOT NULL +BEGIN + DROP TABLE dbo.SecuenciaComprobante; + PRINT 'Table dbo.SecuenciaComprobante dropped (cleanup).'; +END +GO + -- ═══════════════════════════════════════════════════════════════════════ -- 1. dbo.PuntoDeVenta -- ═══════════════════════════════════════════════════════════════════════ @@ -65,30 +103,7 @@ END GO -- ═══════════════════════════════════════════════════════════════════════ --- 2. dbo.SecuenciaComprobante --- ═══════════════════════════════════════════════════════════════════════ - -IF OBJECT_ID(N'dbo.SecuenciaComprobante', N'U') IS NULL -BEGIN - CREATE TABLE dbo.SecuenciaComprobante ( - PuntoDeVentaId INT NOT NULL, - TipoComprobante TINYINT NOT NULL, - UltimoNumero INT NOT NULL CONSTRAINT DF_SecuenciaComprobante_UltimoNumero DEFAULT(0), - FechaCreacion DATETIME2(3) NOT NULL CONSTRAINT DF_SecuenciaComprobante_FechaCreacion DEFAULT(SYSUTCDATETIME()), - FechaModificacion DATETIME2(3) NULL, - CONSTRAINT PK_SecuenciaComprobante PRIMARY KEY (PuntoDeVentaId, TipoComprobante), - CONSTRAINT FK_SecuenciaComprobante_PuntoDeVenta FOREIGN KEY (PuntoDeVentaId) REFERENCES dbo.PuntoDeVenta(Id) ON DELETE NO ACTION, - CONSTRAINT CK_SecuenciaComprobante_TipoComprobante CHECK (TipoComprobante BETWEEN 1 AND 6), - CONSTRAINT CK_SecuenciaComprobante_UltimoNumero CHECK (UltimoNumero >= 0) - ); - PRINT 'Table dbo.SecuenciaComprobante created.'; -END -ELSE - PRINT 'Table dbo.SecuenciaComprobante already exists — skip.'; -GO - --- ═══════════════════════════════════════════════════════════════════════ --- 3. SYSTEM_VERSIONING — PuntoDeVenta +-- 2. SYSTEM_VERSIONING — PuntoDeVenta -- ═══════════════════════════════════════════════════════════════════════ IF COL_LENGTH('dbo.PuntoDeVenta', 'ValidFrom') IS NULL @@ -130,55 +145,7 @@ END GO -- ═══════════════════════════════════════════════════════════════════════ --- 4. SecuenciaComprobante — SIN SYSTEM_VERSIONING (decisión AD8 revisitada) --- ═══════════════════════════════════════════════════════════════════════ --- Razón: bajo reservas concurrentes (reportado 50 threads paralelos) el engine --- arroja "Data modification failed on system-versioned table because transaction --- time was earlier than period start" — UPDATEs repetidos sobre la misma fila --- en transacciones SERIALIZABLE concurrentes violan la invariante temporal. --- Ya anticipado en el design (AD8): la reserva es operacional, no configuracion. --- La auditoria del numero vigente no tiene valor de negocio: el estado actual --- (UltimoNumero) es la unica informacion relevante; cada comprobante emitido --- deja su propio rastro en AvisosCdo/CtaCte (modulos FAC-*). --- --- Esta seccion es idempotente: si una version previa de la migracion dejo --- SYSTEM_VERSIONING = ON, lo desactiva y drop la history. En instalacion nueva --- no hace nada porque nunca se activo. - -IF EXISTS (SELECT 1 FROM sys.tables WHERE object_id = OBJECT_ID('dbo.SecuenciaComprobante') AND temporal_type = 2) -BEGIN - ALTER TABLE dbo.SecuenciaComprobante SET (SYSTEM_VERSIONING = OFF); - PRINT 'SecuenciaComprobante: SYSTEM_VERSIONING = OFF (revisited AD8).'; -END -GO - -IF OBJECT_ID(N'dbo.SecuenciaComprobante_History', N'U') IS NOT NULL -BEGIN - DROP TABLE dbo.SecuenciaComprobante_History; - PRINT 'SecuenciaComprobante_History: dropped.'; -END -GO - -IF EXISTS (SELECT 1 FROM sys.periods WHERE object_id = OBJECT_ID('dbo.SecuenciaComprobante')) -BEGIN - ALTER TABLE dbo.SecuenciaComprobante DROP PERIOD FOR SYSTEM_TIME; - PRINT 'SecuenciaComprobante: PERIOD FOR SYSTEM_TIME dropped.'; -END -GO - -IF COL_LENGTH('dbo.SecuenciaComprobante', 'ValidFrom') IS NOT NULL -BEGIN - IF EXISTS (SELECT 1 FROM sys.default_constraints WHERE name = 'DF_SecuenciaComprobante_ValidFrom' AND parent_object_id = OBJECT_ID('dbo.SecuenciaComprobante')) - ALTER TABLE dbo.SecuenciaComprobante DROP CONSTRAINT DF_SecuenciaComprobante_ValidFrom; - IF EXISTS (SELECT 1 FROM sys.default_constraints WHERE name = 'DF_SecuenciaComprobante_ValidTo' AND parent_object_id = OBJECT_ID('dbo.SecuenciaComprobante')) - ALTER TABLE dbo.SecuenciaComprobante DROP CONSTRAINT DF_SecuenciaComprobante_ValidTo; - ALTER TABLE dbo.SecuenciaComprobante DROP COLUMN ValidFrom, ValidTo; - PRINT 'SecuenciaComprobante: ValidFrom/ValidTo + default constraints dropped.'; -END -GO - --- ═══════════════════════════════════════════════════════════════════════ --- 5. Permiso: administracion:puntos-de-venta:gestionar +-- 3. Permiso: administracion:puntos_de_venta:gestionar -- ═══════════════════════════════════════════════════════════════════════ MERGE dbo.Permiso AS t @@ -204,97 +171,9 @@ WHEN NOT MATCHED BY TARGET THEN INSERT (RolId, PermisoId) VALUES (s.RolId, s.PermisoId); GO --- ═══════════════════════════════════════════════════════════════════════ --- 6. SP: dbo.usp_ReservarNumeroComprobante --- ═══════════════════════════════════════════════════════════════════════ --- Decisión AD1: SP con UPDATE+OUTPUT bajo SERIALIZABLE (lógica crítica en SP; --- permite validar PdV/Medio activos en DB; atómico sin race). --- Decisión AD2: patrón "UPDATE si existe, INSERT si @@ROWCOUNT=0" (lazy init; --- sin trigger; simple; no genera filas huérfanas). --- El retry ante deadlock (SqlException 1205) vive en el Application handler --- (ReservarNumeroCommandHandler), NO en este SP. Máx 3 intentos, 50/150/450ms. --- NO envolver la llamada en TransactionScope externo: el SP ya es atómico. --- Un TransactionScope externo con otra conexión escalaría a MSDTC innecesariamente. - -IF OBJECT_ID(N'dbo.usp_ReservarNumeroComprobante', N'P') IS NOT NULL - DROP PROCEDURE dbo.usp_ReservarNumeroComprobante; -GO - -CREATE PROCEDURE dbo.usp_ReservarNumeroComprobante - @PuntoDeVentaId INT, - @TipoComprobante TINYINT, - @NumeroReservado INT OUTPUT -AS -BEGIN - SET NOCOUNT ON; - SET XACT_ABORT ON; - SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; - - BEGIN TRAN; - - -- Validar existencia y estado de PdV + Medio padre en una sola lectura. - -- Con SERIALIZABLE, el lock de rango protege contra inserciones concurrentes - -- de la misma fila de SecuenciaComprobante entre el SELECT y el UPDATE/INSERT. - DECLARE @PdvActivo BIT; - DECLARE @MedioActivo BIT; - - SELECT - @PdvActivo = p.Activo, - @MedioActivo = m.Activo - FROM dbo.PuntoDeVenta p - JOIN dbo.Medio m ON m.Id = p.MedioId - WHERE p.Id = @PuntoDeVentaId; - - IF @PdvActivo IS NULL - BEGIN - ROLLBACK; - THROW 50003, 'punto_de_venta_not_found', 1; - END - - IF @PdvActivo = 0 - BEGIN - ROLLBACK; - THROW 50001, 'punto_de_venta_inactivo', 1; - END - - IF @MedioActivo = 0 - BEGIN - ROLLBACK; - THROW 50002, 'medio_inactivo', 1; - END - - -- Intentar actualizar la fila existente y capturar el nuevo número. - DECLARE @_out TABLE (n INT NOT NULL); - - UPDATE dbo.SecuenciaComprobante - SET - UltimoNumero = UltimoNumero + 1, - FechaModificacion = SYSUTCDATETIME() - OUTPUT inserted.UltimoNumero INTO @_out(n) - WHERE PuntoDeVentaId = @PuntoDeVentaId - AND TipoComprobante = @TipoComprobante; - - IF @@ROWCOUNT = 0 - BEGIN - -- Primera reserva para este par (PdvId, TipoComprobante): inicialización lazy. - INSERT INTO dbo.SecuenciaComprobante (PuntoDeVentaId, TipoComprobante, UltimoNumero) - VALUES (@PuntoDeVentaId, @TipoComprobante, 1); - SET @NumeroReservado = 1; - END - ELSE - BEGIN - SELECT @NumeroReservado = n FROM @_out; - END - - COMMIT; -END -GO - PRINT ''; PRINT 'V013 applied successfully.'; PRINT ' - dbo.PuntoDeVenta (temporal, retention 10y, PAGE compression)'; -PRINT ' - dbo.SecuenciaComprobante (temporal, retention 10y, PAGE compression)'; -PRINT ' - Permiso administracion:puntos-de-venta:gestionar (asignado a admin)'; -PRINT ' - SP dbo.usp_ReservarNumeroComprobante'; -PRINT 'Next: Batch 2 — Domain (PuntoDeVenta entity + exceptions + TipoComprobante enum).'; +PRINT ' - Permiso administracion:puntos_de_venta:gestionar (asignado a admin)'; +PRINT ' - Artefactos de version previa (SecuenciaComprobante + SP) eliminados si existian'; GO