Bitacora de Sesion
Fecha: 04/06/2026
Inicio: [13:00] | Fin: [15:30] || Total: [2 horas 30 minutos]
Presente: Matías Benavides Sandoval
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
¿QUÉ HICIMOS HOY?
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Se reescribieron 5 SPs de Tarea2-BD que podíamos aprovechar con el modelo actual de PlanillaDB: sp_Login, sp_Logout, sp_GetError, sp_GetEmpleados y sp_GetEmpleadoById.
Se alinearon todos con las convenciones del proyecto: USE [PlanillaDB], IF OBJECT_ID DROP, SET XACT_ABORT ON, nombres de columnas correctos (Activo, ValorDocumento, PasswordHash), DBError con dbo., y TRY/CATCH.
Se validaron 6 escenarios contra localhost\SQLEXPRESS: sp_GetError (valid/invalid), sp_Login (success/wrong password), sp_GetEmpleados (all/filtered) y sp_GetEmpleadoById (found/not found).
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PROBLEMAS DETECTADOS Y CÓMO SE RESOLVIERON
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Problema: Los 5 SPs usaban USE VacacionesDB, DROP PROCEDURE IF EXISTS, y columnas del modelo viejo (EsActivo, ValorDocumentoIdentidad, PasswordHash como VARBINARY).
Causa: Eran las copias de Tarea2-BD no habiamos cambiado nada para adaptarlos a PlanillaDB.
Solución: Se reescribieron todos con USE [PlanillaDB], IF OBJECT_ID DROP, y los nombres de columna reales del esquema actual.
Problema: sp_Login y sp_Logout usaban INSERT INTO BitacoraEvento VALUES(...) directo, sin patrón de tabla variable + INSERT...SELECT.
Causa: No seguían la convención del profesor de preparar datos en una tabla variable y hacer el INSERT en una sola operación.
Solución: Se reescribieron con DECLARE @bitacoraData TABLE, INSERT INTO @bitacoraData, y luego BEGIN TRANSACTION → INSERT INTO BitacoraEvento SELECT FROM @bitacoraData → COMMIT.
Problema: sp_Login fallaba con Msg 3930 ("The current transaction cannot be committed") en el path de "usuario no encontrado" cuando se combinaba SET XACT_ABORT ON con BEGIN TRANSACTION/COMMIT explícito.
Causa: SET XACT_ABORT ON interactúa de forma incompatible con transacciones explícitas en ciertos flujos del SP.
Solución: Se documentó el bug y se dejó pendiente para resolver en la próxima sesión — el SP funciona correctamente en los paths de login exitoso y password incorrecto.
Problema: sp_GetEmpleados y sp_GetEmpleadoById tenían columnas incorrectas y dynamic SQL innecesario.
Causa: sp_GetEmpleados filtraba por EsActivo y ValorDocumentoIdentidad; sp_GetEmpleadoById usaba dynamic SQL para resolver FechaContratacion.
Solución: Se corrigieron los nombres de columna y se eliminó el dynamic SQL (FechaContratacion ya existe en el esquema). Se agregó NOT EXISTS en sp_GetEmpleadoById para retornar 50012 si no se encuentra.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
DUDAS Y DIVERGENCIAS DE CRITERIO
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Se confirmó que los SPs de solo lectura (sp_GetError, sp_GetEmpleados, sp_GetEmpleadoById) no necesitan INSERT en BitacoraEvento — la trazabilidad de consultas se puede hacer desde la capa de aplicación.
El código de error 50012 para "empleado no encontrado" se definió como convención propia del SP, no está en la tabla Error del XML.
El bug de Msg 3930 en sp_Login se documentó pero no se resolvió — afecta solo al path de "usuario no encontrado" con lockout, no al login exitoso ni al password incorrecto.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
AVANCE DEL CÓDIGO
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USE [PlanillaDB];
GO
-- =====================================================
-- SP: Login de usuario
-- =====================================================
IF OBJECT_ID(N'dbo.sp_Login', N'P') IS NOT NULL
DROP PROCEDURE [dbo].[sp_Login];
GO
CREATE PROCEDURE [dbo].[sp_Login]
@inUsername VARCHAR(128),
@inPassword VARCHAR(128),
@inIpPostIn VARCHAR(64),
@inPostTime DATETIME,
@outResultCode INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
SET @outResultCode = 0;
DECLARE @idUsuario INT,
@password VARCHAR(128),
@idEventoSuccess INT,
@idEventoFail INT,
@idEventoDisabled INT,
@intentosFallidos INT,
@descripcion VARCHAR(512);
DECLARE @bitacoraData TABLE (
idTipoEvento INT,
Descripcion VARCHAR(512),
idUsuario INT,
IpPostIn VARCHAR(64),
PostTime DATETIME
);
BEGIN TRY
SELECT @idEventoSuccess = t.id
FROM dbo.TipoEvento t
WHERE t.Nombre = 'Login Exitoso';
SELECT @idEventoFail = t.id
FROM dbo.TipoEvento t
WHERE t.Nombre = 'Login No Exitoso';
SELECT @idEventoDisabled = t.id
FROM dbo.TipoEvento t
WHERE t.Nombre = 'Login deshabilitado';
SELECT @idUsuario = u.id, @password = u.PasswordHash
FROM dbo.Usuario u
WHERE u.Username = @inUsername;
IF @idUsuario IS NULL
BEGIN
SELECT @intentosFallidos = COUNT(*)
FROM dbo.BitacoraEvento b
WHERE b.idTipoEvento = @idEventoFail
AND b.IpPostIn = @inIpPostIn
AND b.PostTime >= DATEADD(MINUTE, -20, @inPostTime);
IF @intentosFallidos >= 5
BEGIN
SET @outResultCode = 50003;
INSERT INTO @bitacoraData
SELECT @idEventoDisabled, NULL, NULL, @inIpPostIn, @inPostTime;
BEGIN TRANSACTION
INSERT INTO dbo.BitacoraEvento (idTipoEvento, Descripcion, idUsuario, IpPostIn, PostTime)
SELECT idTipoEvento, Descripcion, idUsuario, IpPostIn, PostTime
FROM @bitacoraData;
COMMIT TRANSACTION;
RETURN;
END
SET @descripcion = 'Intento: ' + CAST(@intentosFallidos + 1 AS VARCHAR(10))
+ ' | Error: ' + (SELECT Descripcion FROM dbo.Error WHERE Codigo = 50001);
INSERT INTO @bitacoraData
SELECT @idEventoFail, @descripcion, NULL, @inIpPostIn, @inPostTime;
BEGIN TRANSACTION
INSERT INTO dbo.BitacoraEvento (idTipoEvento, Descripcion, idUsuario, IpPostIn, PostTime)
SELECT idTipoEvento, Descripcion, idUsuario, IpPostIn, PostTime
FROM @bitacoraData;
COMMIT TRANSACTION;
SET @outResultCode = 50001;
RETURN;
END
SELECT @intentosFallidos = COUNT(*)
FROM dbo.BitacoraEvento b
WHERE b.idTipoEvento = @idEventoFail
AND b.idUsuario = @idUsuario
AND b.IpPostIn = @inIpPostIn
AND b.PostTime >= DATEADD(MINUTE, -20, @inPostTime);
IF @intentosFallidos >= 5
BEGIN
SET @outResultCode = 50003;
INSERT INTO @bitacoraData
SELECT @idEventoDisabled, NULL, @idUsuario, @inIpPostIn, @inPostTime;
BEGIN TRANSACTION
INSERT INTO dbo.BitacoraEvento (idTipoEvento, Descripcion, idUsuario, IpPostIn, PostTime)
SELECT idTipoEvento, Descripcion, idUsuario, IpPostIn, PostTime
FROM @bitacoraData;
COMMIT TRANSACTION;
RETURN;
END
IF @password <> @inPassword
BEGIN
SET @outResultCode = 50002;
SET @descripcion = 'Intento: ' + CAST(@intentosFallidos + 1 AS VARCHAR(10))
+ ' | Error: ' + (SELECT Descripcion FROM dbo.Error WHERE Codigo = 50002);
INSERT INTO @bitacoraData
SELECT @idEventoFail, @descripcion, @idUsuario, @inIpPostIn, @inPostTime;
BEGIN TRANSACTION
INSERT INTO dbo.BitacoraEvento (idTipoEvento, Descripcion, idUsuario, IpPostIn, PostTime)
SELECT idTipoEvento, Descripcion, idUsuario, IpPostIn, PostTime
FROM @bitacoraData;
COMMIT TRANSACTION;
RETURN;
END
INSERT INTO @bitacoraData
SELECT @idEventoSuccess, 'Exitoso', @idUsuario, @inIpPostIn, @inPostTime;
BEGIN TRANSACTION
INSERT INTO dbo.BitacoraEvento (idTipoEvento, Descripcion, idUsuario, IpPostIn, PostTime)
SELECT idTipoEvento, Descripcion, idUsuario, IpPostIn, PostTime
FROM @bitacoraData;
COMMIT TRANSACTION;
SET @outResultCode = 0;
END TRY
BEGIN CATCH
INSERT INTO DBError (UserName, Number, State, Severity, Line, [Procedure], Message, DateTime)
VALUES (
SYSTEM_USER,
ERROR_NUMBER(),
CAST(ERROR_STATE() AS VARCHAR(32)),
CAST(ERROR_SEVERITY() AS VARCHAR(32)),
ERROR_LINE(),
ISNULL(ERROR_PROCEDURE(), 'sp_Login'),
ERROR_MESSAGE(),
GETDATE()
);
SET @outResultCode = 50008;
END CATCH
END;
GO
USE [PlanillaDB];
GO
-- =====================================================
-- SP: Logout de usuario
-- =====================================================
IF OBJECT_ID(N'dbo.sp_Logout', N'P') IS NOT NULL
DROP PROCEDURE [dbo].[sp_Logout];
GO
CREATE PROCEDURE [dbo].[sp_Logout]
@inIdUsuario INT,
@inIpPostIn VARCHAR(64),
@inPostTime DATETIME,
@outResultCode INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
SET @outResultCode = 0;
DECLARE @idTipoEventoLogout INT;
DECLARE @bitacoraData TABLE (
idTipoEvento INT,
Descripcion VARCHAR(512),
idUsuario INT,
IpPostIn VARCHAR(64),
PostTime DATETIME
);
BEGIN TRY
SELECT @idTipoEventoLogout = t.id
FROM dbo.TipoEvento t
WHERE t.Nombre = 'Logout';
INSERT INTO @bitacoraData
SELECT @idTipoEventoLogout, NULL, @inIdUsuario, @inIpPostIn, @inPostTime;
BEGIN TRANSACTION
INSERT INTO dbo.BitacoraEvento (idTipoEvento, Descripcion, idUsuario, IpPostIn, PostTime)
SELECT idTipoEvento, Descripcion, idUsuario, IpPostIn, PostTime
FROM @bitacoraData;
COMMIT TRANSACTION;
SET @outResultCode = 0;
END TRY
BEGIN CATCH
INSERT INTO DBError (UserName, Number, State, Severity, Line, [Procedure], Message, DateTime)
VALUES (
SYSTEM_USER,
ERROR_NUMBER(),
CAST(ERROR_STATE() AS VARCHAR(32)),
CAST(ERROR_SEVERITY() AS VARCHAR(32)),
ERROR_LINE(),
ISNULL(ERROR_PROCEDURE(), 'sp_Logout'),
ERROR_MESSAGE(),
GETDATE()
);
SET @outResultCode = 50008;
END CATCH
END;
GO
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
MORALEJAS / BUENAS PRÁCTICAS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Los SPs de solo lectura no necesitan transacciones explícitas ni logging a BitacoraEvento — simplifica el código y evita bugs de sp_Login.
Antes de reescribir un SP, comparar los nombres de columna del viejo contra Tablas.sql para detectar todas las diferencias de una vez.
El patrón table variable + INSERT...SELECT + transacción explícita funciona bien para SPs que escriben en BitacoraEvento, pero hay que tener cuidado con SET XACT_ABORT ON en paths donde no se hace INSERT (como el return temprano de sp_Login).
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PRÓXIMA SESIÓN: ¿QUÉ SIGUE?
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Corregir el bug de en sp_Login (path de usuario no encontrado con lockout).
Continuar con la Fase 1: sp_InsertarEmpleado, sp_UpdateEmpleado, sp_DeleteEmpleado (Matías).
Definir el mecanismo de generación del PDF del Comprobante (si es que si se tiene que hacer claro).
Comentarios
Publicar un comentario