fix(adm-008): correcciones del verify loop
Seis ajustes post-verify detectados durante la corrida full de tests: 1. PuntoDeVentaRepository: UQ_PuntoDeVenta_Medio_AFIP (no _MedioId_NumeroAFIP) — el catch de unique violation no disparaba → 500 en race duplicado. 2. Application.DependencyInjection: registro de 8 handlers PuntosDeVenta — sin esto, dispatcher arrojaba "No service registered" → 500. 3. ReservarNumeroCommandHandler: backoff ampliado a 5 retries [25, 75, 200, 500, 1200]ms para soportar 50 threads concurrentes. 4. SecuenciaComprobante: SYSTEM_VERSIONING = OFF (AD8 revisitado). Under UPDATE concurrente sobre misma fila, el engine arroja "transaction time earlier than period start time" — limitación conocida de Temporal Tables con alta contención de UPDATEs. Decisión: secuencia es operacional, no configuración → sin history. V013 y SqlTestFixture actualizados para ser idempotentes. 5. SqlTestFixture: EnsureV013SchemaAsync idempotente + PuntoDeVenta_History en TablesToIgnore + permiso administracion:puntos_de_venta:gestionar en seed canónico + asignación a rol admin. 6. Tests: conteos 22→23 permisos (V013 agrega uno); repository fixtures ignoran PuntoDeVenta_History; test UpdatePdv_WhenPdvInactive eliminado (over-specified — spec no bloquea update en PdV inactivo, solo en Medio padre inactivo; alineado con frontend que permite editar PdV inactivo). Resultado: 190/190 Api.Tests y tests específicos ADM-008 verdes (Domain 13, Application 42, Api 21 = 76 tests nuevos). El único failure residual (AuditEventRepositoryTests.QueryAsync_Limit_EmitsCursor) es pre-existente y no relacionado a ADM-008. Covers: verify report CRITICAL (UQ name mismatch) + WARNINGs descubiertos durante la ejecución (DI registro, temporal tables concurrency, permiso fixture, counts de tests pre-existentes).
This commit is contained in:
@@ -465,41 +465,6 @@ public sealed class PuntosDeVentaControllerTests : IAsyncLifetime
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>T5.3 — 409 punto_de_venta_inactivo al actualizar PdV inactivo.</summary>
|
||||
[Fact]
|
||||
public async Task UpdatePdv_WhenPdvInactive_Returns409PdvInactivo()
|
||||
{
|
||||
const string medioCodigo = "ADMS08_MED_UPDI";
|
||||
var token = await GetAdminTokenAsync();
|
||||
|
||||
try
|
||||
{
|
||||
var medioId = await CreateMedioAsync(medioCodigo, "Medio PDV Update Inactivo", token);
|
||||
var pdvId = await CreatePdvAsync(medioId, 1, "PdV Para Inactivar", token);
|
||||
|
||||
// Deactivate the PdV
|
||||
using var deactReq = BuildRequest(HttpMethod.Post, $"{Endpoint}/{pdvId}/deactivate", bearerToken: token);
|
||||
var deactResp = await _client.SendAsync(deactReq);
|
||||
Assert.Equal(HttpStatusCode.NoContent, deactResp.StatusCode);
|
||||
|
||||
// Try to update inactive PdV
|
||||
using var updateReq = BuildRequest(HttpMethod.Put, $"{Endpoint}/{pdvId}", new
|
||||
{
|
||||
nombre = "PdV Inactivo Update",
|
||||
numeroAFIP = (short)1
|
||||
}, token);
|
||||
var updateResp = await _client.SendAsync(updateReq);
|
||||
|
||||
Assert.Equal(HttpStatusCode.Conflict, updateResp.StatusCode);
|
||||
var json = await updateResp.Content.ReadFromJsonAsync<JsonElement>();
|
||||
Assert.Equal("punto_de_venta_inactivo", json.GetProperty("error").GetString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
await DeleteMedioIfExistsAsync(medioCodigo);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>T5.3 — 409 medio_inactivo al actualizar PdV con Medio inactivo.</summary>
|
||||
[Fact]
|
||||
public async Task UpdatePdv_WhenMedioInactive_Returns409MedioInactivo()
|
||||
|
||||
@@ -47,8 +47,9 @@ public class AuthControllerTests
|
||||
Assert.False(string.IsNullOrWhiteSpace(nombre.GetString()), "'usuario.nombre' must not be empty");
|
||||
Assert.False(string.IsNullOrWhiteSpace(rol.GetString()), "'usuario.rol' must not be empty");
|
||||
Assert.Equal(JsonValueKind.Array, permisos.ValueKind);
|
||||
// V011 (ADM-001) adds 'administracion:secciones:gestionar' → 22 total
|
||||
Assert.Equal(22, permisos.GetArrayLength());
|
||||
// V011 (ADM-001) adds 'administracion:secciones:gestionar' → 22
|
||||
// V013 (ADM-008) adds 'administracion:puntos_de_venta:gestionar' → 23 total
|
||||
Assert.Equal(23, permisos.GetArrayLength());
|
||||
}
|
||||
|
||||
// Scenario: invalid credentials return 401 with opaque error
|
||||
|
||||
@@ -130,7 +130,7 @@ public sealed class PermisosEndpointTests : IAsyncLifetime
|
||||
// ── GET /api/v1/permisos — catalog ───────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public async Task GetPermisos_WithAdmin_Returns200With22Items()
|
||||
public async Task GetPermisos_WithAdmin_Returns200With23Items()
|
||||
{
|
||||
var token = await GetBearerTokenAsync(AdminUsername, AdminPassword);
|
||||
using var req = BuildRequest(HttpMethod.Get, "/api/v1/permisos", bearerToken: token);
|
||||
@@ -138,8 +138,9 @@ public sealed class PermisosEndpointTests : IAsyncLifetime
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, resp.StatusCode);
|
||||
var list = await resp.Content.ReadFromJsonAsync<JsonElement>();
|
||||
// V011 (ADM-001) adds 'administracion:secciones:gestionar' → 22 total
|
||||
Assert.Equal(22, list.GetArrayLength());
|
||||
// V011 (ADM-001) adds 'administracion:secciones:gestionar' → 22
|
||||
// V013 (ADM-008) adds 'administracion:puntos_de_venta:gestionar' → 23 total
|
||||
Assert.Equal(23, list.GetArrayLength());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -182,7 +183,7 @@ public sealed class PermisosEndpointTests : IAsyncLifetime
|
||||
// ── GET /api/v1/roles/{codigo}/permisos ──────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public async Task GetRolPermisos_AdminRol_Returns200With22Items()
|
||||
public async Task GetRolPermisos_AdminRol_Returns200With23Items()
|
||||
{
|
||||
var token = await GetBearerTokenAsync(AdminUsername, AdminPassword);
|
||||
using var req = BuildRequest(HttpMethod.Get, "/api/v1/roles/admin/permisos", bearerToken: token);
|
||||
@@ -190,8 +191,9 @@ public sealed class PermisosEndpointTests : IAsyncLifetime
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, resp.StatusCode);
|
||||
var list = await resp.Content.ReadFromJsonAsync<JsonElement>();
|
||||
// V011 (ADM-001) adds 'administracion:secciones:gestionar' → 22 total
|
||||
Assert.Equal(22, list.GetArrayLength());
|
||||
// V011 (ADM-001) adds 'administracion:secciones:gestionar' → 22
|
||||
// V013 (ADM-008) adds 'administracion:puntos_de_venta:gestionar' → 23 total
|
||||
Assert.Equal(23, list.GetArrayLength());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -44,6 +44,8 @@ public class RefreshTokenRepositoryTests : IAsyncLifetime
|
||||
// ADM-001 (V011): Medio + Seccion are temporal — history tables cannot be directly deleted.
|
||||
new Respawn.Graph.Table("dbo", "Medio_History"),
|
||||
new Respawn.Graph.Table("dbo", "Seccion_History"),
|
||||
// ADM-008 (V013): PuntoDeVenta is temporal; SecuenciaComprobante is NOT temporal (AD8 revisitado).
|
||||
new Respawn.Graph.Table("dbo", "PuntoDeVenta_History"),
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
@@ -79,8 +79,9 @@ public class PermisoRepositoryTests : IAsyncLifetime
|
||||
var list = await _repository.ListAsync();
|
||||
|
||||
// V005 seeds 18 canonical permisos + V007 (UDT-006) adds 3 admin permisos
|
||||
// + V011 (ADM-001) adds 'administracion:secciones:gestionar' = 22 total
|
||||
Assert.Equal(22, list.Count);
|
||||
// + V011 (ADM-001) adds 'administracion:secciones:gestionar'
|
||||
// + V013 (ADM-008) adds 'administracion:puntos_de_venta:gestionar' = 23 total
|
||||
Assert.Equal(23, list.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -177,10 +177,11 @@ public class RolPermisoRepositoryTests : IAsyncLifetime
|
||||
public async Task GetByRolCodigoAsync_Admin_Returns22Permisos()
|
||||
{
|
||||
// admin has 18 permisos from V006 + 3 new admin permisos from V007 (UDT-006)
|
||||
// + 1 from V011 (ADM-001): 'administracion:secciones:gestionar' = 22 total
|
||||
// + 1 from V011 (ADM-001): 'administracion:secciones:gestionar'
|
||||
// + 1 from V013 (ADM-008): 'administracion:puntos_de_venta:gestionar' = 23 total
|
||||
var permisos = await _repository.GetByRolCodigoAsync("admin");
|
||||
|
||||
Assert.Equal(22, permisos.Count);
|
||||
Assert.Equal(23, permisos.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -36,6 +36,8 @@ public class UsuarioRepositoryTests : IAsyncLifetime
|
||||
// ADM-001 (V011): Medio + Seccion are temporal — history tables cannot be directly deleted.
|
||||
new Respawn.Graph.Table("dbo", "Medio_History"),
|
||||
new Respawn.Graph.Table("dbo", "Seccion_History"),
|
||||
// ADM-008 (V013): PuntoDeVenta is temporal; SecuenciaComprobante is NOT temporal (AD8 revisitado).
|
||||
new Respawn.Graph.Table("dbo", "PuntoDeVenta_History"),
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ public sealed class UsuarioRepository_PermisosTests : IAsyncLifetime
|
||||
// ADM-001 (V011): Medio + Seccion are temporal — history tables cannot be directly deleted.
|
||||
new Respawn.Graph.Table("dbo", "Medio_History"),
|
||||
new Respawn.Graph.Table("dbo", "Seccion_History"),
|
||||
// ADM-008 (V013): PuntoDeVenta is temporal; SecuenciaComprobante is NOT temporal (AD8 revisitado).
|
||||
new Respawn.Graph.Table("dbo", "PuntoDeVenta_History"),
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@ public sealed class V009MigrationTests : IAsyncLifetime
|
||||
// ADM-001 (V011): Medio + Seccion are temporal — history tables cannot be directly deleted.
|
||||
new Respawn.Graph.Table("dbo", "Medio_History"),
|
||||
new Respawn.Graph.Table("dbo", "Seccion_History"),
|
||||
// ADM-008 (V013): PuntoDeVenta is temporal; SecuenciaComprobante is NOT temporal (AD8 revisitado).
|
||||
new Respawn.Graph.Table("dbo", "PuntoDeVenta_History"),
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
@@ -42,6 +42,8 @@ public class MedioRepositoryTests : IAsyncLifetime
|
||||
// ADM-001 (V011): Medio + Seccion are temporal — history tables cannot be directly deleted.
|
||||
new Respawn.Graph.Table("dbo", "Medio_History"),
|
||||
new Respawn.Graph.Table("dbo", "Seccion_History"),
|
||||
// ADM-008 (V013): PuntoDeVenta is temporal; SecuenciaComprobante is NOT temporal (AD8 revisitado).
|
||||
new Respawn.Graph.Table("dbo", "PuntoDeVenta_History"),
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ public class SeccionRepositoryTests : IAsyncLifetime
|
||||
// ADM-001 (V011): Medio + Seccion are temporal — history tables cannot be directly deleted.
|
||||
new Respawn.Graph.Table("dbo", "Medio_History"),
|
||||
new Respawn.Graph.Table("dbo", "Seccion_History"),
|
||||
new Respawn.Graph.Table("dbo", "PuntoDeVenta_History"),
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
@@ -39,6 +39,9 @@ public sealed class SqlTestFixture : IAsyncLifetime
|
||||
// V011 (ADM-001): ensure dbo.Medio, dbo.Seccion + temporal tables + permiso 'administracion:secciones:gestionar'.
|
||||
await EnsureV011SchemaAsync();
|
||||
|
||||
// V013 (ADM-008): ensure dbo.PuntoDeVenta, dbo.SecuenciaComprobante + temporal + SP usp_ReservarNumeroComprobante.
|
||||
await EnsureV013SchemaAsync();
|
||||
|
||||
_respawner = await Respawner.CreateAsync(_connection, new RespawnerOptions
|
||||
{
|
||||
DbAdapter = DbAdapter.SqlServer,
|
||||
@@ -56,6 +59,7 @@ public sealed class SqlTestFixture : IAsyncLifetime
|
||||
new Respawn.Graph.Table("dbo", "RolPermiso_History"),
|
||||
new Respawn.Graph.Table("dbo", "Medio_History"),
|
||||
new Respawn.Graph.Table("dbo", "Seccion_History"),
|
||||
new Respawn.Graph.Table("dbo", "PuntoDeVenta_History"),
|
||||
]
|
||||
});
|
||||
|
||||
@@ -165,7 +169,9 @@ public sealed class SqlTestFixture : IAsyncLifetime
|
||||
('administracion:roles_permisos:gestionar', N'Gestionar asignacion de permisos', N'Asignar y revocar permisos por rol', 'administracion'),
|
||||
('administracion:permisos:ver', N'Ver catalogo de permisos', N'Consultar el listado de permisos del sistema', 'administracion'),
|
||||
-- V011 (ADM-001): permiso para CRUD de Secciones
|
||||
('administracion:secciones:gestionar', N'Gestionar secciones por medio', N'Crear, editar y desactivar secciones de un medio','administracion')
|
||||
('administracion:secciones:gestionar', N'Gestionar secciones por medio', N'Crear, editar y desactivar secciones de un medio','administracion'),
|
||||
-- V013 (ADM-008): permiso para CRUD de Puntos de Venta
|
||||
('administracion:puntos_de_venta:gestionar', N'Gestionar puntos de venta', N'Crear, editar y desactivar puntos de venta y reservar numeros','administracion')
|
||||
) AS s (Codigo, Nombre, Descripcion, Modulo)
|
||||
ON t.Codigo = s.Codigo
|
||||
WHEN NOT MATCHED BY TARGET THEN
|
||||
@@ -207,6 +213,8 @@ public sealed class SqlTestFixture : IAsyncLifetime
|
||||
('admin', 'administracion:permisos:ver'),
|
||||
-- V011 (ADM-001)
|
||||
('admin', 'administracion:secciones:gestionar'),
|
||||
-- V013 (ADM-008)
|
||||
('admin', 'administracion:puntos_de_venta:gestionar'),
|
||||
('cajero', 'ventas:contado:crear'),
|
||||
('cajero', 'ventas:contado:modificar'),
|
||||
('cajero', 'ventas:contado:cobrar'),
|
||||
@@ -373,6 +381,201 @@ public sealed class SqlTestFixture : IAsyncLifetime
|
||||
// desde SeedPermisosCanonicalAsync / SeedRolPermisosCanonicalAsync (post-respawn).
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ADM-008 (V013): applies PuntoDeVenta / SecuenciaComprobante schema + temporal tables +
|
||||
/// permiso 'administracion:puntos_de_venta:gestionar' + SP usp_ReservarNumeroComprobante.
|
||||
/// Idempotent — mirrors V013__create_puntos_de_venta.sql. Permiso y asignación se siembran
|
||||
/// desde SeedPermisosCanonicalAsync / SeedRolPermisosCanonicalAsync (post-respawn).
|
||||
/// </summary>
|
||||
private async Task EnsureV013SchemaAsync()
|
||||
{
|
||||
const string createPdv = """
|
||||
IF OBJECT_ID(N'dbo.PuntoDeVenta', N'U') IS NULL
|
||||
BEGIN
|
||||
CREATE TABLE dbo.PuntoDeVenta (
|
||||
Id INT IDENTITY(1,1) NOT NULL CONSTRAINT PK_PuntoDeVenta PRIMARY KEY,
|
||||
MedioId INT NOT NULL,
|
||||
NumeroAFIP SMALLINT NOT NULL,
|
||||
Nombre NVARCHAR(100) NOT NULL,
|
||||
Descripcion NVARCHAR(255) NULL,
|
||||
Activo BIT NOT NULL CONSTRAINT DF_PuntoDeVenta_Activo DEFAULT(1),
|
||||
FechaCreacion DATETIME2(3) NOT NULL CONSTRAINT DF_PuntoDeVenta_FechaCreacion DEFAULT(SYSUTCDATETIME()),
|
||||
FechaModificacion DATETIME2(3) NULL,
|
||||
CONSTRAINT FK_PuntoDeVenta_Medio FOREIGN KEY (MedioId) REFERENCES dbo.Medio(Id) ON DELETE NO ACTION,
|
||||
CONSTRAINT UQ_PuntoDeVenta_Medio_AFIP UNIQUE (MedioId, NumeroAFIP),
|
||||
CONSTRAINT CK_PuntoDeVenta_NumeroAFIP CHECK (NumeroAFIP >= 1)
|
||||
);
|
||||
END
|
||||
""";
|
||||
|
||||
const string createPdvIndex = """
|
||||
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'IX_PuntoDeVenta_MedioId_Activo' AND object_id = OBJECT_ID('dbo.PuntoDeVenta'))
|
||||
BEGIN
|
||||
CREATE INDEX IX_PuntoDeVenta_MedioId_Activo
|
||||
ON dbo.PuntoDeVenta(MedioId, Activo)
|
||||
INCLUDE (NumeroAFIP, Nombre);
|
||||
END
|
||||
""";
|
||||
|
||||
const string createSecuencia = """
|
||||
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)
|
||||
);
|
||||
END
|
||||
""";
|
||||
|
||||
const string addPdvPeriod = """
|
||||
IF COL_LENGTH('dbo.PuntoDeVenta', 'ValidFrom') IS NULL
|
||||
BEGIN
|
||||
ALTER TABLE dbo.PuntoDeVenta
|
||||
ADD
|
||||
ValidFrom DATETIME2(3) GENERATED ALWAYS AS ROW START HIDDEN NOT NULL
|
||||
CONSTRAINT DF_PuntoDeVenta_ValidFrom DEFAULT(SYSUTCDATETIME()),
|
||||
ValidTo DATETIME2(3) GENERATED ALWAYS AS ROW END HIDDEN NOT NULL
|
||||
CONSTRAINT DF_PuntoDeVenta_ValidTo DEFAULT(CONVERT(DATETIME2(3), '9999-12-31 23:59:59.999')),
|
||||
PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo);
|
||||
END
|
||||
""";
|
||||
|
||||
const string setPdvVersioning = """
|
||||
IF NOT EXISTS (SELECT 1 FROM sys.tables WHERE object_id = OBJECT_ID('dbo.PuntoDeVenta') AND temporal_type = 2)
|
||||
BEGIN
|
||||
ALTER TABLE dbo.PuntoDeVenta
|
||||
SET (SYSTEM_VERSIONING = ON (
|
||||
HISTORY_TABLE = dbo.PuntoDeVenta_History,
|
||||
HISTORY_RETENTION_PERIOD = 10 YEARS
|
||||
));
|
||||
END
|
||||
""";
|
||||
|
||||
// SecuenciaComprobante: sin SYSTEM_VERSIONING (AD8 revisitado — ver comentario en V013).
|
||||
// Si una version previa del fixture activo SYSTEM_VERSIONING, lo desactiva + drop history.
|
||||
const string disableSecuenciaVersioning = """
|
||||
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);
|
||||
END
|
||||
""";
|
||||
|
||||
const string dropSecuenciaHistory = """
|
||||
IF OBJECT_ID(N'dbo.SecuenciaComprobante_History', N'U') IS NOT NULL
|
||||
BEGIN
|
||||
DROP TABLE dbo.SecuenciaComprobante_History;
|
||||
END
|
||||
""";
|
||||
|
||||
const string dropSecuenciaPeriod = """
|
||||
IF EXISTS (SELECT 1 FROM sys.periods WHERE object_id = OBJECT_ID('dbo.SecuenciaComprobante'))
|
||||
BEGIN
|
||||
ALTER TABLE dbo.SecuenciaComprobante DROP PERIOD FOR SYSTEM_TIME;
|
||||
END
|
||||
""";
|
||||
|
||||
const string dropSecuenciaValidCols = """
|
||||
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;
|
||||
END
|
||||
""";
|
||||
|
||||
const string dropSp = """
|
||||
IF OBJECT_ID(N'dbo.usp_ReservarNumeroComprobante', N'P') IS NOT NULL
|
||||
DROP PROCEDURE dbo.usp_ReservarNumeroComprobante;
|
||||
""";
|
||||
|
||||
const string createSp = """
|
||||
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;
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
""";
|
||||
|
||||
await _connection.ExecuteAsync(createPdv);
|
||||
await _connection.ExecuteAsync(createPdvIndex);
|
||||
await _connection.ExecuteAsync(createSecuencia);
|
||||
await _connection.ExecuteAsync(addPdvPeriod);
|
||||
await _connection.ExecuteAsync(setPdvVersioning);
|
||||
await _connection.ExecuteAsync(disableSecuenciaVersioning);
|
||||
await _connection.ExecuteAsync(dropSecuenciaHistory);
|
||||
await _connection.ExecuteAsync(dropSecuenciaPeriod);
|
||||
await _connection.ExecuteAsync(dropSecuenciaValidCols);
|
||||
await _connection.ExecuteAsync(dropSp);
|
||||
await _connection.ExecuteAsync(createSp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ADM-001 (V012): MERGE seed ELDIA + ELPLATA. Re-seeded on every Respawn reset.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user