/* Procedimientos para el modelo evento -> registros -> campos/relaciones. Base, plantillas y detalle plano instalados el 20/09/2026 con autorización del usuario. Requiere la migración de eventos y la columna Plantilla de Ajuste_Bitacora_Plantillas_20260920.sql, en una transacción externa. Para actualizar la base actual, ejecutar únicamente ese ajuste, que incluye los dos PA cambiados. Add conserva su resultado CveBitacora; EventoRegistrar devuelve CveBitacoraEvento. Los cuatro lectores anteriores conservan filtros, seguridad y forma de resultados. */ IF @@TRANCOUNT = 0 OR COL_LENGTH('dbo.Bit_Campos', 'Plantilla') IS NULL OR COL_LENGTH('dbo.Bit_Bitacora', 'LlaveRegistroTexto') IS NULL OR COL_LENGTH('dbo.Bit_Bitacora', 'DescripcionMovimiento') IS NOT NULL THROW 51000, 'Instale la migración de eventos en la transacción del instalador.', 1; GO CREATE OR ALTER PROCEDURE dbo.PA_Bit_BitacoraAdd @NombreTablaSQL varchar(128), @CveTipoMovimiento tinyint, @CveDefinicionAccion int = NULL, @CveUsuarioAutoriza int = NULL, @FechaAutorizacion datetime = NULL, @LlaveRegistro nvarchar(250), @DescripcionMovimiento nvarchar(1000), @RelacionesXml xml, @CamposXml xml = NULL, @DiscriminarCamposSinCambios bit = 1, @DireccionIP varchar(50) = NULL, @NombreEquipo varchar(150) = NULL, @Modulo varchar(150) = NULL, @CveBitacoraEvento bigint = NULL, @LlaveRegistroNumero bigint = NULL, @EsLlaveTexto bit = 0, @DevolverClave bit = 1, @RegistrarSinCampos bit = 0, @CveUsuarioEjecuta int, @ErrorMostrar varchar(max) OUTPUT, @ErrorTipo int OUTPUT, @Return bit OUTPUT AS BEGIN SET NOCOUNT ON; SELECT @ErrorMostrar = NULL, @ErrorTipo = 0, @Return = 0; BEGIN TRY IF @@TRANCOUNT = 0 BEGIN SET @ErrorMostrar = 'El registro de bitácora requiere la transacción del servicio.'; SET @ErrorTipo = 1; RETURN; END; DECLARE @EsCompatibilidad bit = CASE WHEN @CveBitacoraEvento IS NULL THEN 1 ELSE 0 END; DECLARE @CveTabla int; SELECT @CveTabla = T.CveTabla FROM dbo.Bit_Tablas AS T WHERE T.NombreTablaSQL = @NombreTablaSQL AND T.Activo = 1; IF @CveTabla IS NULL BEGIN INSERT INTO [dbo].[Bit_Tablas]([NombreTablaSQL], [NombreMostrar], [Descripcion], [NombreCampoLlaveSQL], [Activo], [FechaAlta]) VALUES (@NombreTablaSQL, REPLACE(@NombreTablaSQL, '_', ' '), REPLACE(@NombreTablaSQL, '_', ' '), NULL, 1, GETDATE()); SELECT @CveTabla = T.CveTabla FROM dbo.Bit_Tablas AS T WHERE T.NombreTablaSQL = @NombreTablaSQL AND T.Activo = 1; END; IF @CveTabla IS NULL BEGIN SET @ErrorMostrar = 'La tabla indicada no está configurada para bitácora.'+@NombreTablaSQL; SET @ErrorTipo = 2; RETURN; END; IF NOT EXISTS ( SELECT 1 FROM dbo.Bit_TiposMovimiento AS M WHERE M.CveTipoMovimiento = @CveTipoMovimiento ) BEGIN SET @ErrorMostrar = 'El tipo de movimiento de bitácora no es válido.'; SET @ErrorTipo = 2; RETURN; END; IF @CveDefinicionAccion IS NOT NULL AND NOT EXISTS ( SELECT 1 FROM dbo.Bit_DefinicionesAccion AS A WHERE A.CveDefinicionAccion = @CveDefinicionAccion AND A.Activo = 1 ) BEGIN SET @ErrorMostrar = 'La definición de la acción no corresponde al movimiento.'; SET @ErrorTipo = 2; RETURN; END; DECLARE @Relaciones TABLE ( Orden int NOT NULL, CveTipoRelacion tinyint NOT NULL, NombreTablaSQL varchar(128) NOT NULL, NombreCampoSQL varchar(128) NOT NULL, Valor nvarchar(250) NOT NULL, VisibleDesarrollador bit NOT NULL, DescripcionRelacion nvarchar(500) NULL ); INSERT INTO @Relaciones ( Orden, CveTipoRelacion, NombreTablaSQL, NombreCampoSQL, Valor, VisibleDesarrollador, DescripcionRelacion ) SELECT R.value('@Orden', 'int'), R.value('@CveTipoRelacion', 'tinyint'), R.value('@NombreTablaSQL', 'varchar(128)'), R.value('@NombreCampoSQL', 'varchar(128)'), R.value('@Valor', 'nvarchar(250)'), R.value('@VisibleDesarrollador', 'bit'), NULLIF(R.value('@DescripcionRelacion', 'nvarchar(500)'), '') FROM @RelacionesXml.nodes('/Relaciones/Relacion') AS X(R); IF NOT EXISTS (SELECT 1 FROM @Relaciones) BEGIN SET @ErrorMostrar = 'La bitácora debe incluir al menos una relación.'; SET @ErrorTipo = 2; RETURN; END; /*============================================================== AUTOCONFIGURAR TABLAS UTILIZADAS EN RELACIONES ==============================================================*/ INSERT INTO dbo.Bit_Tablas ( NombreTablaSQL, NombreMostrar, Descripcion, NombreCampoLlaveSQL, Activo, FechaAlta ) SELECT DISTINCT R.NombreTablaSQL, REPLACE(R.NombreTablaSQL, '_', ' '), REPLACE(R.NombreTablaSQL, '_', ' '), NULL, 1, GETDATE() FROM @Relaciones AS R INNER JOIN sys.schemas AS S ON S.name = 'dbo' INNER JOIN sys.tables AS ST ON ST.schema_id = S.schema_id AND ST.name = R.NombreTablaSQL WHERE NOT EXISTS ( SELECT 1 FROM dbo.Bit_Tablas AS T WHERE T.NombreTablaSQL = R.NombreTablaSQL ); /*============================================================== AUTOCONFIGURAR CAMPOS UTILIZADOS EN RELACIONES ==============================================================*/ INSERT INTO dbo.Bit_Campos ( CveTabla, NombreCampoSQL, NombreMostrar, TipoDatoSQL, MostrarEnBitacora, Activo, FechaAlta ) SELECT DISTINCT T.CveTabla, R.NombreCampoSQL, LTRIM ( REPLACE ( CASE WHEN LEFT(R.NombreCampoSQL, 3) = 'Cve' THEN SUBSTRING(R.NombreCampoSQL, 4, 125) ELSE R.NombreCampoSQL END, '_', ' ' ) ) AS NombreMostrar, TYPE_NAME(SC.system_type_id) AS TipoDatoSQL, 1, 1, GETDATE() FROM @Relaciones AS R INNER JOIN dbo.Bit_Tablas AS T ON T.NombreTablaSQL = R.NombreTablaSQL AND T.Activo = 1 INNER JOIN sys.schemas AS S ON S.name = 'dbo' INNER JOIN sys.tables AS ST ON ST.schema_id = S.schema_id AND ST.name = R.NombreTablaSQL INNER JOIN sys.columns AS SC ON SC.object_id = ST.object_id AND SC.name = R.NombreCampoSQL WHERE NOT EXISTS ( SELECT 1 FROM dbo.Bit_Campos AS BC WHERE BC.CveTabla = T.CveTabla AND BC.NombreCampoSQL = R.NombreCampoSQL ); DECLARE @RelacionesResueltas TABLE ( Orden int NOT NULL, CveTipoRelacion tinyint NOT NULL, CveTabla int NOT NULL, CveCampo int NOT NULL, NombreTablaSQL varchar(128) NOT NULL, NombreCampoSQL varchar(128) NOT NULL, TipoDatoSQL varchar(128) NOT NULL, Valor nvarchar(250) NOT NULL, VisibleDesarrollador bit NOT NULL, DescripcionRelacion nvarchar(500) NULL ); INSERT INTO @RelacionesResueltas ( Orden, CveTipoRelacion, CveTabla, CveCampo, NombreTablaSQL, NombreCampoSQL, TipoDatoSQL, Valor, VisibleDesarrollador, DescripcionRelacion ) SELECT R.Orden, R.CveTipoRelacion, T.CveTabla, C.CveCampo, R.NombreTablaSQL, R.NombreCampoSQL, C.TipoDatoSQL, R.Valor, R.VisibleDesarrollador, R.DescripcionRelacion FROM @Relaciones AS R INNER JOIN dbo.Bit_Tablas AS T ON T.NombreTablaSQL = R.NombreTablaSQL AND T.Activo = 1 INNER JOIN dbo.Bit_Campos AS C ON C.CveTabla = T.CveTabla AND C.NombreCampoSQL = R.NombreCampoSQL AND C.Activo = 1 INNER JOIN dbo.Bit_TiposRelacion AS TR ON TR.CveTipoRelacion = R.CveTipoRelacion AND TR.Activo = 1 WHERE C.TipoDatoSQL IS NOT NULL; IF (SELECT COUNT(1) FROM @Relaciones) <> (SELECT COUNT(1) FROM @RelacionesResueltas) BEGIN SET @ErrorMostrar = 'Una relación utiliza una tabla, campo o tipo que no está configurado en la bitácora.'; SET @ErrorTipo = 2; RETURN; END; IF EXISTS ( SELECT 1 FROM @RelacionesResueltas WHERE TipoDatoSQL IN ('tinyint', 'smallint', 'int', 'bigint') AND TRY_CONVERT(bigint, Valor) IS NULL ) BEGIN SET @ErrorMostrar = 'El valor de una relación numérica no es válido.'; SET @ErrorTipo = 2; RETURN; END; IF EXISTS ( SELECT 1 FROM @RelacionesResueltas WHERE VisibleDesarrollador = 1 ) AND NOT EXISTS ( SELECT 1 FROM @RelacionesResueltas WHERE NombreTablaSQL = 'Clientes' AND NombreCampoSQL = 'CveCliente' AND VisibleDesarrollador = 1 ) BEGIN SET @ErrorMostrar = 'Una bitácora visible para desarrolladores debe relacionarse con un cliente.'; SET @ErrorTipo = 2; RETURN; END; DECLARE @Campos TABLE ( Orden int NOT NULL, NombreCampoSQL varchar(128) NOT NULL, ValorAnteriorEsNulo bit NOT NULL, ValorAnterior nvarchar(max) NULL, ValorNuevoEsNulo bit NOT NULL, ValorNuevo nvarchar(max) NULL, ValorAnteriorMostrar nvarchar(max) NULL, ValorNuevoMostrar nvarchar(max) NULL ); IF @CamposXml IS NOT NULL BEGIN INSERT INTO @Campos ( Orden, NombreCampoSQL, ValorAnteriorEsNulo, ValorAnterior, ValorNuevoEsNulo, ValorNuevo, ValorAnteriorMostrar, ValorNuevoMostrar ) SELECT C.value('@Orden', 'int'), C.value('@NombreCampoSQL', 'varchar(128)'), C.value('@ValorAnteriorEsNulo', 'bit'), CASE WHEN C.value('@ValorAnteriorEsNulo', 'bit') = 1 THEN NULL ELSE C.value('@ValorAnterior', 'nvarchar(max)') END, C.value('@ValorNuevoEsNulo', 'bit'), CASE WHEN C.value('@ValorNuevoEsNulo', 'bit') = 1 THEN NULL ELSE C.value('@ValorNuevo', 'nvarchar(max)') END, CASE WHEN C.exist('@ValorAnteriorMostrar') = 1 THEN C.value('@ValorAnteriorMostrar', 'nvarchar(max)') END, CASE WHEN C.exist('@ValorNuevoMostrar') = 1 THEN C.value('@ValorNuevoMostrar', 'nvarchar(max)') END FROM @CamposXml.nodes('/Campos/Campo') AS X(C); END; IF @CveTipoMovimiento IN (1, 2) AND NOT EXISTS (SELECT 1 FROM @Campos) AND @RegistrarSinCampos = 0 BEGIN SET @ErrorMostrar = 'Las altas y modificaciones deben incluir los campos auditados.'; SET @ErrorTipo = 2; RETURN; END; /*============================================================== AUTOCONFIGURAR CAMPOS AUDITADOS DE LA TABLA PRINCIPAL ==============================================================*/ INSERT INTO dbo.Bit_Campos ( CveTabla, NombreCampoSQL, NombreMostrar, TipoDatoSQL, MostrarEnBitacora, Activo, FechaAlta ) SELECT DISTINCT @CveTabla, F.NombreCampoSQL, LTRIM ( REPLACE ( CASE WHEN LEFT(F.NombreCampoSQL, 3) = 'Cve' THEN SUBSTRING(F.NombreCampoSQL, 4, 125) ELSE F.NombreCampoSQL END, '_', ' ' ) ) AS NombreMostrar, TYPE_NAME(SC.system_type_id) AS TipoDatoSQL, 1, 1, GETDATE() FROM @Campos AS F INNER JOIN sys.schemas AS S ON S.name = 'dbo' INNER JOIN sys.tables AS ST ON ST.schema_id = S.schema_id AND ST.name = @NombreTablaSQL INNER JOIN sys.columns AS SC ON SC.object_id = ST.object_id AND SC.name = F.NombreCampoSQL WHERE NOT EXISTS ( SELECT 1 FROM dbo.Bit_Campos AS BC WHERE BC.CveTabla = @CveTabla AND BC.NombreCampoSQL = F.NombreCampoSQL ); IF EXISTS ( SELECT 1 FROM @Campos AS F LEFT JOIN dbo.Bit_Campos AS C ON C.CveTabla = @CveTabla AND C.NombreCampoSQL = F.NombreCampoSQL AND C.Activo = 1 WHERE C.CveCampo IS NULL ) BEGIN SET @ErrorMostrar = 'Uno de los campos enviados no está configurado para la tabla auditada.'; SET @ErrorTipo = 2; RETURN; END; DECLARE @ValoresPlantilla TABLE ( CveBitacora bigint NOT NULL, CveCampo int NOT NULL, NombreCampoSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, AliasReferencia varchar(128) COLLATE Modern_Spanish_CS_AS NULL, TipoDatoSQL varchar(128) NULL, MostrarEnBitacora bit NOT NULL, ValorAnterior nvarchar(max) COLLATE Modern_Spanish_CS_AS NULL, ValorNuevo nvarchar(max) COLLATE Modern_Spanish_CS_AS NULL, ValorAnteriorMostrar nvarchar(max) COLLATE Modern_Spanish_CS_AS NULL, ValorNuevoMostrar nvarchar(max) COLLATE Modern_Spanish_CS_AS NULL, Orden int NOT NULL ); DECLARE @RegistrosPlantilla TABLE ( CveBitacora bigint NOT NULL PRIMARY KEY, CveTabla int NOT NULL, CveTipoMovimiento tinyint NOT NULL ); DECLARE @ResultadosPlantilla TABLE ( CveBitacora bigint NOT NULL, CveCampo int NOT NULL, ValorAnterior nvarchar(max) COLLATE Modern_Spanish_CS_AS NULL, ValorNuevo nvarchar(max) COLLATE Modern_Spanish_CS_AS NULL, Orden int NOT NULL ); DECLARE @CamposSustituidos TABLE ( CveBitacora bigint NOT NULL, CveCampo int NOT NULL, PRIMARY KEY (CveBitacora, CveCampo) ); DECLARE @EsLecturaHistorica bit = 0; INSERT INTO @RegistrosPlantilla(CveBitacora, CveTabla, CveTipoMovimiento) VALUES (0, @CveTabla, @CveTipoMovimiento); INSERT INTO @ValoresPlantilla ( CveBitacora, CveCampo, NombreCampoSQL, AliasReferencia, TipoDatoSQL, MostrarEnBitacora, ValorAnterior, ValorNuevo, ValorAnteriorMostrar, ValorNuevoMostrar, Orden ) SELECT 0, C.CveCampo, C.NombreCampoSQL, M.NombreCampoSQL, C.TipoDatoSQL, C.MostrarEnBitacora, F.ValorAnterior, F.ValorNuevo, F.ValorAnteriorMostrar, F.ValorNuevoMostrar, F.Orden FROM @Campos AS F INNER JOIN dbo.Bit_Campos AS C ON C.CveTabla = @CveTabla AND C.NombreCampoSQL = F.NombreCampoSQL AND C.Activo = 1 LEFT JOIN dbo.Bit_Campos AS M ON M.CveCampo = C.CveCampoMostrarReferencia AND M.CveTabla = C.CveTablaReferencia WHERE C.Plantilla IS NULL; -- Resolver referencias faltantes al registrar, conservando las imágenes enviadas. DECLARE @CampoReferencia int; DECLARE @TablaReferencia sysname; DECLARE @LlaveReferencia sysname; DECLARE @MostrarReferencia sysname; DECLARE @AnteriorReferencia nvarchar(max); DECLARE @NuevoReferencia nvarchar(max); DECLARE @AnteriorResuelto nvarchar(max); DECLARE @NuevoResuelto nvarchar(max); DECLARE @ConsultaReferencia nvarchar(max); DECLARE CursorReferenciasRegistro CURSOR LOCAL FAST_FORWARD FOR SELECT V.CveCampo, T.NombreTablaSQL, T.NombreCampoLlaveSQL, M.NombreCampoSQL, V.ValorAnterior, V.ValorNuevo FROM @ValoresPlantilla AS V INNER JOIN dbo.Bit_Campos AS C ON C.CveCampo = V.CveCampo INNER JOIN dbo.Bit_Tablas AS T ON T.CveTabla = C.CveTablaReferencia AND T.Activo = 1 INNER JOIN dbo.Bit_Campos AS M ON M.CveCampo = C.CveCampoMostrarReferencia AND M.CveTabla = T.CveTabla AND M.Activo = 1 AND M.MostrarEnBitacora = 1 INNER JOIN sys.tables AS ST ON ST.name COLLATE Modern_Spanish_CS_AS = T.NombreTablaSQL AND ST.schema_id = SCHEMA_ID('dbo') INNER JOIN sys.columns AS CL ON CL.object_id = ST.object_id AND CL.name COLLATE Modern_Spanish_CS_AS = T.NombreCampoLlaveSQL INNER JOIN sys.columns AS CM ON CM.object_id = ST.object_id AND CM.name COLLATE Modern_Spanish_CS_AS = M.NombreCampoSQL WHERE (V.ValorAnterior IS NOT NULL AND V.ValorAnteriorMostrar IS NULL) OR (V.ValorNuevo IS NOT NULL AND V.ValorNuevoMostrar IS NULL); OPEN CursorReferenciasRegistro; FETCH NEXT FROM CursorReferenciasRegistro INTO @CampoReferencia, @TablaReferencia, @LlaveReferencia, @MostrarReferencia, @AnteriorReferencia, @NuevoReferencia; WHILE @@FETCH_STATUS = 0 BEGIN SELECT @AnteriorResuelto = NULL, @NuevoResuelto = NULL; SET @ConsultaReferencia = N' SELECT @AnteriorMostrar = CONVERT(nvarchar(max), ' + QUOTENAME(@MostrarReferencia) + N') FROM dbo.' + QUOTENAME(@TablaReferencia) + N' WHERE CONVERT(nvarchar(250), ' + QUOTENAME(@LlaveReferencia) + N') = @Anterior; SELECT @NuevoMostrar = CONVERT(nvarchar(max), ' + QUOTENAME(@MostrarReferencia) + N') FROM dbo.' + QUOTENAME(@TablaReferencia) + N' WHERE CONVERT(nvarchar(250), ' + QUOTENAME(@LlaveReferencia) + N') = @Nuevo;'; EXEC sys.sp_executesql @ConsultaReferencia, N'@Anterior nvarchar(max), @Nuevo nvarchar(max), @AnteriorMostrar nvarchar(max) OUTPUT, @NuevoMostrar nvarchar(max) OUTPUT', @Anterior = @AnteriorReferencia, @Nuevo = @NuevoReferencia, @AnteriorMostrar = @AnteriorResuelto OUTPUT, @NuevoMostrar = @NuevoResuelto OUTPUT; UPDATE @ValoresPlantilla SET ValorAnteriorMostrar = COALESCE(ValorAnteriorMostrar, @AnteriorResuelto), ValorNuevoMostrar = COALESCE(ValorNuevoMostrar, @NuevoResuelto) WHERE CveCampo = @CampoReferencia; FETCH NEXT FROM CursorReferenciasRegistro INTO @CampoReferencia, @TablaReferencia, @LlaveReferencia, @MostrarReferencia, @AnteriorReferencia, @NuevoReferencia; END; CLOSE CursorReferenciasRegistro; DEALLOCATE CursorReferenciasRegistro; UPDATE @ValoresPlantilla SET ValorAnteriorMostrar = COALESCE(ValorAnteriorMostrar, CASE WHEN ValorAnterior IS NULL THEN NULL WHEN TipoDatoSQL = 'bit' AND ValorAnterior IN (N'0', N'1') THEN CASE WHEN ValorAnterior = N'1' THEN N'Sí' ELSE N'No' END WHEN TipoDatoSQL = 'time' AND TRY_CONVERT(time, ValorAnterior) IS NOT NULL THEN LEFT(CONVERT(varchar(16), TRY_CONVERT(time, ValorAnterior), 108), 5) END), ValorNuevoMostrar = COALESCE(ValorNuevoMostrar, CASE WHEN ValorNuevo IS NULL THEN NULL WHEN TipoDatoSQL = 'bit' AND ValorNuevo IN (N'0', N'1') THEN CASE WHEN ValorNuevo = N'1' THEN N'Sí' ELSE N'No' END WHEN TipoDatoSQL = 'time' AND TRY_CONVERT(time, ValorNuevo) IS NOT NULL THEN LEFT(CONVERT(varchar(16), TRY_CONVERT(time, ValorNuevo), 108), 5) END); -- Sustitución literal de tokens; nunca se ejecuta el contenido de una plantilla. DECLARE @RegistroPlantilla bigint; DECLARE @TablaPlantilla int; DECLARE @CampoPlantilla int; DECLARE @TextoPlantilla nvarchar(1000); DECLARE @MovimientoPlantilla tinyint; DECLARE @Pendiente nvarchar(max); DECLARE @AnteriorPlantilla nvarchar(max); DECLARE @NuevoPlantilla nvarchar(max); DECLARE @InicioToken int; DECLARE @FinToken int; DECLARE @Token nvarchar(128); DECLARE @CampoToken int; DECLARE @Coincidencias int; DECLARE @AnteriorToken nvarchar(max); DECLARE @NuevoToken nvarchar(max); DECLARE @Completa bit; DECLARE @CamposUsados TABLE (CveCampo int NOT NULL PRIMARY KEY); DECLARE CursorPlantillas CURSOR LOCAL FAST_FORWARD FOR SELECT R.CveBitacora, R.CveTabla, P.CveCampo, P.Plantilla, R.CveTipoMovimiento FROM @RegistrosPlantilla AS R INNER JOIN dbo.Bit_Campos AS P ON P.CveTabla = R.CveTabla WHERE P.Activo = 1 AND P.MostrarEnBitacora = 1 AND P.Plantilla IS NOT NULL AND NOT EXISTS ( SELECT 1 FROM @ValoresPlantilla AS V WHERE V.CveBitacora = R.CveBitacora AND V.CveCampo = P.CveCampo ) ORDER BY R.CveBitacora, P.CveCampo; OPEN CursorPlantillas; FETCH NEXT FROM CursorPlantillas INTO @RegistroPlantilla, @TablaPlantilla, @CampoPlantilla, @TextoPlantilla, @MovimientoPlantilla; WHILE @@FETCH_STATUS = 0 BEGIN DELETE FROM @CamposUsados; SELECT @Pendiente = @TextoPlantilla, @AnteriorPlantilla = N'', @NuevoPlantilla = N'', @Completa = 1; WHILE LEN(@Pendiente) > 0 BEGIN SET @InicioToken = CHARINDEX(N'{', @Pendiente); IF @InicioToken = 0 BEGIN IF CHARINDEX(N'}', @Pendiente) > 0 SET @Completa = 0; SET @AnteriorPlantilla += @Pendiente; SET @NuevoPlantilla += @Pendiente; BREAK; END; SET @FinToken = CHARINDEX(N'}', @Pendiente, @InicioToken + 1); IF @FinToken = 0 OR @FinToken - @InicioToken NOT BETWEEN 2 AND 129 OR CHARINDEX(N'}', LEFT(@Pendiente, @InicioToken - 1)) > 0 BEGIN SET @Completa = 0; BREAK; END; SET @Token = SUBSTRING(@Pendiente, @InicioToken + 1, @FinToken - @InicioToken - 1); SET @CampoToken = NULL; SELECT @Coincidencias = COUNT(*), @CampoToken = MIN(CveCampo) FROM @ValoresPlantilla WHERE CveBitacora = @RegistroPlantilla AND NombreCampoSQL = @Token; IF @Coincidencias = 0 BEGIN SELECT @Coincidencias = COUNT(*), @CampoToken = MIN(CveCampo) FROM @ValoresPlantilla WHERE CveBitacora = @RegistroPlantilla AND AliasReferencia = @Token; END; IF @Coincidencias <> 1 OR EXISTS ( SELECT 1 FROM dbo.Bit_Campos WHERE CveCampo = @CampoToken AND Plantilla IS NOT NULL ) BEGIN SET @Completa = 0; BREAK; END; -- Una clave de catálogo sin texto histórico no permite reconstruir su nombre anterior. IF EXISTS ( SELECT 1 FROM @ValoresPlantilla WHERE CveBitacora = @RegistroPlantilla AND CveCampo = @CampoToken AND AliasReferencia IS NOT NULL AND ( (ValorAnterior IS NOT NULL AND ValorAnteriorMostrar IS NULL) OR (ValorNuevo IS NOT NULL AND ValorNuevoMostrar IS NULL) ) ) BEGIN SET @Completa = 0; BREAK; END; SELECT @AnteriorToken = COALESCE(ValorAnteriorMostrar, ValorAnterior, N''), @NuevoToken = COALESCE(ValorNuevoMostrar, ValorNuevo, N'') FROM @ValoresPlantilla WHERE CveBitacora = @RegistroPlantilla AND CveCampo = @CampoToken; SET @AnteriorPlantilla += LEFT(@Pendiente, @InicioToken - 1) + @AnteriorToken; SET @NuevoPlantilla += LEFT(@Pendiente, @InicioToken - 1) + @NuevoToken; SET @Pendiente = SUBSTRING(@Pendiente, @FinToken + 1, LEN(@Pendiente)); IF NOT EXISTS (SELECT 1 FROM @CamposUsados WHERE CveCampo = @CampoToken) INSERT INTO @CamposUsados(CveCampo) VALUES (@CampoToken); END; IF NOT EXISTS (SELECT 1 FROM @CamposUsados) SET @Completa = 0; IF @Completa = 1 BEGIN INSERT INTO @ResultadosPlantilla (CveBitacora, CveCampo, ValorAnterior, ValorNuevo, Orden) SELECT @RegistroPlantilla, @CampoPlantilla, CASE WHEN @MovimientoPlantilla = 1 THEN NULL ELSE @AnteriorPlantilla END, CASE WHEN @MovimientoPlantilla = 3 THEN NULL ELSE @NuevoPlantilla END, MIN(V.Orden) FROM @ValoresPlantilla AS V INNER JOIN @CamposUsados AS U ON U.CveCampo = V.CveCampo WHERE V.CveBitacora = @RegistroPlantilla; INSERT INTO @CamposSustituidos(CveBitacora, CveCampo) SELECT @RegistroPlantilla, U.CveCampo FROM @CamposUsados AS U INNER JOIN @ValoresPlantilla AS V ON V.CveBitacora = @RegistroPlantilla AND V.CveCampo = U.CveCampo WHERE V.MostrarEnBitacora = 0 AND NOT EXISTS ( SELECT 1 FROM @CamposSustituidos AS S WHERE S.CveBitacora = @RegistroPlantilla AND S.CveCampo = U.CveCampo ); END ELSE IF @EsLecturaHistorica = 0 BEGIN CLOSE CursorPlantillas; DEALLOCATE CursorPlantillas; SET @ErrorMostrar = 'No se pudo formar la plantilla de bitácora: revise sus campos y valores mostrables.'; SET @ErrorTipo = 2; RETURN; END; FETCH NEXT FROM CursorPlantillas INTO @RegistroPlantilla, @TablaPlantilla, @CampoPlantilla, @TextoPlantilla, @MovimientoPlantilla; END; CLOSE CursorPlantillas; DEALLOCATE CursorPlantillas; IF @CveBitacoraEvento IS NULL BEGIN INSERT INTO dbo.Bit_BitacoraEventos ( CveDefinicionAccion, CveUsuarioEjecuta, CveUsuarioAutoriza, FechaAutorizacion, DescripcionMovimiento, DireccionIP, NombreEquipo, Modulo ) VALUES ( @CveDefinicionAccion, @CveUsuarioEjecuta, @CveUsuarioAutoriza, @FechaAutorizacion, @DescripcionMovimiento, @DireccionIP, @NombreEquipo, @Modulo ); SET @CveBitacoraEvento = CONVERT(bigint, SCOPE_IDENTITY()); END; INSERT INTO dbo.Bit_Bitacora ( CveBitacoraEvento, CveTabla, CveTipoMovimiento, LlaveRegistro, LlaveRegistroTexto ) VALUES ( @CveBitacoraEvento, @CveTabla, @CveTipoMovimiento, CASE WHEN @EsCompatibilidad = 1 THEN TRY_CONVERT(bigint, @LlaveRegistro) WHEN @EsLlaveTexto = 0 THEN @LlaveRegistroNumero END, CASE WHEN @EsCompatibilidad = 1 OR @EsLlaveTexto = 1 THEN @LlaveRegistro END ); DECLARE @CveBitacora bigint = CONVERT(bigint, SCOPE_IDENTITY()); INSERT INTO dbo.Bit_BitacoraRelaciones ( CveBitacora, CveTipoRelacion, CveTablaRelacionada, LlaveRegistroRelacionada, CveCampoRelacion, ValorRelacionNumero, ValorRelacionTexto, DescripcionRelacion, VisibleDesarrollador ) SELECT @CveBitacora, R.CveTipoRelacion, R.CveTabla, R.Valor, R.CveCampo, CASE WHEN R.TipoDatoSQL IN ('tinyint', 'smallint', 'int', 'bigint') THEN TRY_CONVERT(bigint, R.Valor) ELSE NULL END, CASE WHEN R.TipoDatoSQL IN ('tinyint', 'smallint', 'int', 'bigint') THEN NULL ELSE R.Valor END, R.DescripcionRelacion, R.VisibleDesarrollador FROM @RelacionesResueltas AS R; ;WITH CamposGuardar AS ( SELECT CveCampo, ValorAnterior, ValorNuevo, ValorAnteriorMostrar, ValorNuevoMostrar FROM @ValoresPlantilla WHERE MostrarEnBitacora = 1 UNION ALL SELECT CveCampo, ValorAnterior, ValorNuevo, ValorAnterior, ValorNuevo FROM @ResultadosPlantilla ) INSERT INTO dbo.Bit_BitacoraCampos ( CveBitacora, CveCampo, ValorAnterior, ValorNuevo, ValorAnteriorMostrar, ValorNuevoMostrar ) SELECT @CveBitacora, F.CveCampo, F.ValorAnterior, F.ValorNuevo, F.ValorAnteriorMostrar, F.ValorNuevoMostrar FROM CamposGuardar AS F WHERE @DiscriminarCamposSinCambios = 0 OR (F.ValorAnterior IS NULL AND F.ValorNuevo IS NOT NULL) OR (F.ValorAnterior IS NOT NULL AND F.ValorNuevo IS NULL) OR DATALENGTH(F.ValorAnterior) <> DATALENGTH(F.ValorNuevo) OR F.ValorAnterior COLLATE Latin1_General_100_BIN2 <> F.ValorNuevo COLLATE Latin1_General_100_BIN2 OR (F.ValorAnteriorMostrar IS NULL AND F.ValorNuevoMostrar IS NOT NULL) OR (F.ValorAnteriorMostrar IS NOT NULL AND F.ValorNuevoMostrar IS NULL) OR DATALENGTH(F.ValorAnteriorMostrar) <> DATALENGTH(F.ValorNuevoMostrar) OR F.ValorAnteriorMostrar COLLATE Latin1_General_100_BIN2 <> F.ValorNuevoMostrar COLLATE Latin1_General_100_BIN2; IF @DevolverClave = 1 SELECT @CveBitacora AS CveBitacora; SET @Return = 1; END TRY BEGIN CATCH SET @ErrorMostrar = 'Ocurrió un error al registrar la bitácora; favor de contactar a Sistemas.'; SET @ErrorTipo = 1; SET @Return = 0; DECLARE @ErrorTecnico nvarchar(2048) = CONCAT( 'Número: ', ERROR_NUMBER(), '; Procedimiento: ', COALESCE(ERROR_PROCEDURE(), 'PA_Bit_BitacoraAdd'), '; Línea: ', ERROR_LINE(), '; Detalle: ', ERROR_MESSAGE() ); RAISERROR(N'%s', 10, 1, @ErrorTecnico); END CATCH; END; GO CREATE OR ALTER PROCEDURE dbo.PA_Bit_BitacoraEventoRegistrar @CodigoAccion varchar(150), @DescripcionMovimiento nvarchar(1000), @CveUsuarioAutoriza int = NULL, @FechaAutorizacion datetime2(3) = NULL, @DireccionIP varchar(50) = NULL, @NombreEquipo varchar(150) = NULL, @Modulo varchar(150) = NULL, @RegistrosXml xml, @CveUsuarioEjecuta int, @ErrorMostrar varchar(max) OUTPUT, @ErrorTipo int OUTPUT, @Return bit OUTPUT AS BEGIN SET NOCOUNT ON; SELECT @ErrorMostrar = NULL, @ErrorTipo = 0, @Return = 0; BEGIN TRY IF @@TRANCOUNT = 0 BEGIN SET @ErrorMostrar = 'La operación requiere una transacción del servicio.'; SET @ErrorTipo = 1; RETURN; END; DECLARE @CveDefinicionAccion int; SELECT @CveDefinicionAccion = CveDefinicionAccion FROM dbo.Bit_DefinicionesAccion WHERE CodigoAccion = @CodigoAccion AND Activo = 1; IF @CveDefinicionAccion IS NULL OR @RegistrosXml IS NULL OR @RegistrosXml.exist('/Registros/Registro') = 0 BEGIN SET @ErrorMostrar = 'La acción no está configurada o no contiene registros.'; SET @ErrorTipo = 2; RETURN; END; DECLARE @Registros TABLE ( Orden int NOT NULL PRIMARY KEY, Datos xml NOT NULL ); INSERT INTO @Registros(Orden, Datos) SELECT R.value('@Orden', 'int'), R.query('.') FROM @RegistrosXml.nodes('/Registros/Registro') AS X(R); INSERT INTO dbo.Bit_BitacoraEventos ( CveDefinicionAccion, CveUsuarioEjecuta, CveUsuarioAutoriza, FechaAutorizacion, DescripcionMovimiento, DireccionIP, NombreEquipo, Modulo ) VALUES ( @CveDefinicionAccion, @CveUsuarioEjecuta, @CveUsuarioAutoriza, @FechaAutorizacion, @DescripcionMovimiento, @DireccionIP, @NombreEquipo, @Modulo ); DECLARE @CveBitacoraEvento bigint = CONVERT(bigint, SCOPE_IDENTITY()); DECLARE @Orden int = 0; DECLARE @Datos xml; DECLARE @Tabla varchar(128); DECLARE @Movimiento tinyint; DECLARE @LlaveNumero bigint; DECLARE @LlaveTexto nvarchar(250); DECLARE @EsLlaveTexto bit; DECLARE @Discriminar bit; DECLARE @RegistrarSinCampos bit; DECLARE @Campos xml; DECLARE @Relaciones xml; WHILE EXISTS (SELECT 1 FROM @Registros WHERE Orden > @Orden) BEGIN SELECT TOP (1) @Orden = Orden, @Datos = Datos FROM @Registros WHERE Orden > @Orden ORDER BY Orden; SELECT @Tabla = @Datos.value('(/Registro/@NombreTablaSQL)[1]', 'varchar(128)'), @Movimiento = @Datos.value('(/Registro/@CveTipoMovimiento)[1]', 'tinyint'), @EsLlaveTexto = @Datos.exist('/Registro/@LlaveRegistroTexto'), @LlaveNumero = CASE WHEN @Datos.exist('/Registro/@LlaveRegistro') = 1 THEN @Datos.value('(/Registro/@LlaveRegistro)[1]', 'bigint') END, @LlaveTexto = CASE WHEN @Datos.exist('/Registro/@LlaveRegistroTexto') = 1 THEN @Datos.value('(/Registro/@LlaveRegistroTexto)[1]', 'nvarchar(250)') END, @RegistrarSinCampos = CASE WHEN @Datos.exist('/Registro/@RegistrarSinCampos') = 1 THEN @Datos.value('(/Registro/@RegistrarSinCampos)[1]', 'bit') ELSE 0 END, @Discriminar = @Datos.value('(/Registro/@DiscriminarCamposSinCambios)[1]', 'bit'), @Campos = @Datos.query('/Registro/Campos'), @Relaciones = @Datos.query('/Registro/Relaciones'); IF (@EsLlaveTexto = 0 AND @LlaveNumero IS NULL) OR (@EsLlaveTexto = 1 AND (@LlaveNumero IS NOT NULL OR NULLIF(@LlaveTexto, N'') IS NULL)) BEGIN SET @ErrorMostrar = 'La clave del registro de bitácora no es válida.'; SET @ErrorTipo = 2; SET @Return = 0; RETURN; END; EXEC dbo.PA_Bit_BitacoraAdd @NombreTablaSQL = @Tabla, @CveTipoMovimiento = @Movimiento, @CveDefinicionAccion = @CveDefinicionAccion, @LlaveRegistro = @LlaveTexto, @LlaveRegistroNumero = @LlaveNumero, @EsLlaveTexto = @EsLlaveTexto, @CveBitacoraEvento = @CveBitacoraEvento, @DescripcionMovimiento = @DescripcionMovimiento, @CamposXml = @Campos, @RelacionesXml = @Relaciones, @DiscriminarCamposSinCambios = @Discriminar, @DevolverClave = 0, @RegistrarSinCampos = @RegistrarSinCampos, @CveUsuarioEjecuta = @CveUsuarioEjecuta, @ErrorMostrar = @ErrorMostrar OUTPUT, @ErrorTipo = @ErrorTipo OUTPUT, @Return = @Return OUTPUT; IF @Return = 0 RETURN; END; SELECT @CveBitacoraEvento AS CveBitacoraEvento; SET @Return = 1; END TRY BEGIN CATCH SET @ErrorMostrar = 'No fue posible completar la operación de bitácora. Contacte a Sistemas.'; SET @ErrorTipo = 1; SET @Return = 0; DECLARE @ErrorTecnico nvarchar(2048) = CONCAT( 'Número: ', ERROR_NUMBER(), '; Procedimiento: ', COALESCE(ERROR_PROCEDURE(), 'PA_Bit_BitacoraEventoRegistrar'), '; Línea: ', ERROR_LINE(), '; Detalle: ', ERROR_MESSAGE() ); RAISERROR(N'%s', 10, 1, @ErrorTecnico); END CATCH; END; GO CREATE OR ALTER PROCEDURE dbo.PA_Bit_BitacoraEventoListar @RelacionesXml xml = NULL, @RelacionesMostrarXml xml = NULL, @RegistrosOmitidos int, @RegistrosPorPagina int, @CveUsuarioEjecuta int, @ErrorMostrar varchar(max) OUTPUT, @ErrorTipo int OUTPUT, @Return bit OUTPUT AS BEGIN SET NOCOUNT ON; SELECT @ErrorMostrar = NULL, @ErrorTipo = 0, @Return = 0; BEGIN TRY IF @RegistrosOmitidos < 0 OR @RegistrosPorPagina NOT BETWEEN 1 AND 100 BEGIN SET @ErrorMostrar = 'La paginación no es válida.'; SET @ErrorTipo = 2; RETURN; END; DECLARE @EsAdministrador bit = 0; DECLARE @EsDesarrollador bit = 0; SELECT @EsAdministrador = CASE WHEN R.NombreRol IN ('SuperUsuario', 'Administrador') THEN 1 ELSE 0 END, @EsDesarrollador = CASE WHEN R.NombreRol = 'Empresa' THEN 1 ELSE 0 END FROM dbo.Sis_Usuarios AS U INNER JOIN dbo.Sis_Roles AS R ON R.CveRol = U.CveRol WHERE U.CveUsuario = @CveUsuarioEjecuta AND U.Activo = 1 AND R.Activo = 1; DECLARE @Filtros TABLE ( Orden int NOT NULL PRIMARY KEY, NombreTablaSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, NombreCampoSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, Valor nvarchar(250) COLLATE Modern_Spanish_CS_AS NOT NULL, SoloExistencia bit NOT NULL ); IF @RelacionesXml IS NOT NULL BEGIN INSERT INTO @Filtros(Orden, NombreTablaSQL, NombreCampoSQL, Valor, SoloExistencia) SELECT R.value('@Orden', 'int'), R.value('@Tabla', 'varchar(128)'), R.value('@Campo', 'varchar(128)'), R.value('@Valor', 'nvarchar(250)'), COALESCE(R.value('@SoloExistencia', 'bit'), 0) FROM @RelacionesXml.nodes('/Relaciones/Relacion') AS X(R); END; DECLARE @FiltrosResueltos TABLE ( Orden int NOT NULL PRIMARY KEY, CveTabla int NOT NULL, CveCampo int NOT NULL, TipoDatoSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, Valor nvarchar(250) COLLATE Modern_Spanish_CS_AS NOT NULL, SoloExistencia bit NOT NULL, EsEntero bit NOT NULL ); INSERT INTO @FiltrosResueltos ( Orden, CveTabla, CveCampo, TipoDatoSQL, Valor, SoloExistencia, EsEntero ) SELECT F.Orden, T.CveTabla, C.CveCampo, C.TipoDatoSQL, F.Valor, F.SoloExistencia, CASE WHEN C.TipoDatoSQL IN ( 'tinyint', 'smallint', 'int', 'bigint' ) THEN 1 ELSE 0 END FROM @Filtros AS F INNER JOIN dbo.Bit_Tablas AS T ON T.NombreTablaSQL = F.NombreTablaSQL AND T.Activo = 1 INNER JOIN dbo.Bit_Campos AS C ON C.CveTabla = T.CveTabla AND C.NombreCampoSQL = F.NombreCampoSQL AND C.Activo = 1 INNER JOIN sys.schemas AS SF ON SF.name = 'dbo' INNER JOIN sys.tables AS TF ON TF.schema_id = SF.schema_id AND TF.name COLLATE Modern_Spanish_CS_AS = T.NombreTablaSQL INNER JOIN sys.columns AS CF ON CF.object_id = TF.object_id AND CF.name COLLATE Modern_Spanish_CS_AS = C.NombreCampoSQL WHERE C.TipoDatoSQL IS NOT NULL; IF (SELECT COUNT(1) FROM @Filtros) <> (SELECT COUNT(1) FROM @FiltrosResueltos) BEGIN SET @ErrorMostrar = 'Una relación utiliza una tabla o campo que no está configurado en la bitácora.'; SET @ErrorTipo = 2; RETURN; END; IF EXISTS ( SELECT 1 FROM @FiltrosResueltos WHERE SoloExistencia = 0 AND EsEntero = 1 AND TRY_CONVERT(bigint, Valor) IS NULL ) BEGIN SET @ErrorMostrar = 'El valor de una relación numérica no es válido.'; SET @ErrorTipo = 2; RETURN; END; CREATE TABLE #RegistrosAutorizados ( CveBitacora bigint NOT NULL PRIMARY KEY ); INSERT INTO #RegistrosAutorizados SELECT B.CveBitacora FROM dbo.Bit_Bitacora AS B WHERE ( @EsAdministrador = 1 OR ( @EsDesarrollador = 1 AND EXISTS ( SELECT 1 FROM dbo.Bit_BitacoraRelaciones AS BRCliente INNER JOIN dbo.Bit_Tablas AS TCliente ON TCliente.CveTabla = BRCliente.CveTablaRelacionada INNER JOIN dbo.Bit_Campos AS CCliente ON CCliente.CveCampo = BRCliente.CveCampoRelacion AND CCliente.CveTabla = TCliente.CveTabla INNER JOIN dbo.Sis_UsuariosClientes AS UC ON UC.CveCliente = BRCliente.ValorRelacionNumero AND UC.CveUsuario = @CveUsuarioEjecuta AND UC.Activo = 1 WHERE BRCliente.CveBitacora = B.CveBitacora AND BRCliente.VisibleDesarrollador = 1 AND TCliente.NombreTablaSQL = 'Clientes' AND CCliente.NombreCampoSQL = 'CveCliente' ) ) ); CREATE TABLE #EventosCoincidentes ( CveBitacoraEvento bigint NOT NULL PRIMARY KEY, FechaMovimiento datetime2(3) NOT NULL ); INSERT INTO #EventosCoincidentes SELECT DISTINCT B.CveBitacoraEvento, E.FechaMovimiento FROM dbo.Bit_Bitacora AS B INNER JOIN #RegistrosAutorizados AS V ON V.CveBitacora = B.CveBitacora INNER JOIN dbo.Bit_BitacoraEventos AS E ON E.CveBitacoraEvento = B.CveBitacoraEvento WHERE 1 = 1 AND ( @EsDesarrollador = 0 OR NOT EXISTS ( SELECT 1 FROM @FiltrosResueltos AS FCliente INNER JOIN dbo.Bit_Tablas AS TClienteFiltro ON TClienteFiltro.CveTabla = FCliente.CveTabla INNER JOIN dbo.Bit_Campos AS CClienteFiltro ON CClienteFiltro.CveCampo = FCliente.CveCampo AND CClienteFiltro.CveTabla = TClienteFiltro.CveTabla WHERE TClienteFiltro.NombreTablaSQL = 'Clientes' AND CClienteFiltro.NombreCampoSQL = 'CveCliente' AND FCliente.SoloExistencia = 0 AND NOT EXISTS ( SELECT 1 FROM dbo.Sis_UsuariosClientes AS UCClienteFiltro WHERE UCClienteFiltro.CveUsuario = @CveUsuarioEjecuta AND UCClienteFiltro.CveCliente = TRY_CONVERT(bigint, FCliente.Valor) AND UCClienteFiltro.Activo = 1 ) ) ) AND NOT EXISTS ( SELECT 1 FROM @FiltrosResueltos AS F WHERE NOT EXISTS ( SELECT 1 FROM dbo.Bit_BitacoraRelaciones AS BR WHERE BR.CveBitacora = B.CveBitacora AND BR.CveTablaRelacionada = F.CveTabla AND BR.CveCampoRelacion = F.CveCampo AND ( @EsAdministrador = 1 OR BR.VisibleDesarrollador = 1 ) AND ( F.SoloExistencia = 1 OR ( ( F.EsEntero = 1 AND BR.ValorRelacionNumero = TRY_CONVERT(bigint, F.Valor) ) OR ( F.EsEntero = 0 AND BR.ValorRelacionTexto = F.Valor COLLATE Modern_Spanish_CS_AS ) ) ) ) ); SELECT COUNT(*) AS TotalRegistros FROM #EventosCoincidentes; SELECT E.CveBitacoraEvento, E.CveDefinicionAccion, A.CodigoAccion, COALESCE(A.NombreAccion, N'Evento histórico') AS NombreAccion, E.CveUsuarioEjecuta, COALESCE(NULLIF(U.NombreUsuario, ''), CONCAT('(', E.CveUsuarioEjecuta, ') Eliminado')) AS NombreUsuarioEjecuta, E.CveUsuarioAutoriza, E.FechaAutorizacion, CASE WHEN EXISTS ( SELECT 1 FROM dbo.Bit_Bitacora AS R WHERE R.CveBitacoraEvento = E.CveBitacoraEvento AND NOT EXISTS (SELECT 1 FROM #RegistrosAutorizados AS V WHERE V.CveBitacora = R.CveBitacora) ) THEN N'Operación con registros de acceso restringido.' ELSE E.DescripcionMovimiento END AS DescripcionMovimiento, E.FechaMovimiento, E.DireccionIP, E.NombreEquipo, E.Modulo, E.EsHistorico FROM dbo.Bit_BitacoraEventos AS E LEFT JOIN dbo.Bit_DefinicionesAccion AS A ON A.CveDefinicionAccion = E.CveDefinicionAccion LEFT JOIN dbo.Sis_Usuarios AS U ON U.CveUsuario = E.CveUsuarioEjecuta INNER JOIN #EventosCoincidentes AS C ON C.CveBitacoraEvento = E.CveBitacoraEvento ORDER BY C.FechaMovimiento DESC, C.CveBitacoraEvento DESC OFFSET @RegistrosOmitidos ROWS FETCH NEXT @RegistrosPorPagina ROWS ONLY; SET @Return = 1; END TRY BEGIN CATCH SET @ErrorMostrar = 'No fue posible completar la operación de bitácora. Contacte a Sistemas.'; SET @ErrorTipo = 1; SET @Return = 0; DECLARE @ErrorTecnico nvarchar(2048) = CONCAT( 'Número: ', ERROR_NUMBER(), '; Procedimiento: ', COALESCE(ERROR_PROCEDURE(), 'PA_Bit_BitacoraEventoListar'), '; Línea: ', ERROR_LINE(), '; Detalle: ', ERROR_MESSAGE() ); RAISERROR(N'%s', 10, 1, @ErrorTecnico); END CATCH; END; GO CREATE OR ALTER PROCEDURE dbo.PA_Bit_BitacoraEventoObtener @CveBitacoraEvento bigint, @CveUsuarioEjecuta int, @ErrorMostrar varchar(max) OUTPUT, @ErrorTipo int OUTPUT, @Return bit OUTPUT AS BEGIN SET NOCOUNT ON; SELECT @ErrorMostrar = NULL, @ErrorTipo = 0, @Return = 0; BEGIN TRY DECLARE @EsAdministrador bit = 0; DECLARE @EsDesarrollador bit = 0; SELECT @EsAdministrador = CASE WHEN R.NombreRol IN ('SuperUsuario', 'Administrador') THEN 1 ELSE 0 END, @EsDesarrollador = CASE WHEN R.NombreRol = 'Empresa' THEN 1 ELSE 0 END FROM dbo.Sis_Usuarios AS U INNER JOIN dbo.Sis_Roles AS R ON R.CveRol = U.CveRol WHERE U.CveUsuario = @CveUsuarioEjecuta AND U.Activo = 1 AND R.Activo = 1; DECLARE @ValoresPlantilla TABLE ( CveBitacora bigint NOT NULL, CveCampo int NOT NULL, NombreCampoSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, AliasReferencia varchar(128) COLLATE Modern_Spanish_CS_AS NULL, TipoDatoSQL varchar(128) NULL, MostrarEnBitacora bit NOT NULL, ValorAnterior nvarchar(max) COLLATE Modern_Spanish_CS_AS NULL, ValorNuevo nvarchar(max) COLLATE Modern_Spanish_CS_AS NULL, ValorAnteriorMostrar nvarchar(max) COLLATE Modern_Spanish_CS_AS NULL, ValorNuevoMostrar nvarchar(max) COLLATE Modern_Spanish_CS_AS NULL, Orden int NOT NULL ); DECLARE @RegistrosPlantilla TABLE ( CveBitacora bigint NOT NULL PRIMARY KEY, CveTabla int NOT NULL, CveTipoMovimiento tinyint NOT NULL ); DECLARE @ResultadosPlantilla TABLE ( CveBitacora bigint NOT NULL, CveCampo int NOT NULL, ValorAnterior nvarchar(max) COLLATE Modern_Spanish_CS_AS NULL, ValorNuevo nvarchar(max) COLLATE Modern_Spanish_CS_AS NULL, Orden int NOT NULL ); DECLARE @CamposSustituidos TABLE ( CveBitacora bigint NOT NULL, CveCampo int NOT NULL, PRIMARY KEY (CveBitacora, CveCampo) ); DECLARE @EsLecturaHistorica bit = 1; INSERT INTO @RegistrosPlantilla(CveBitacora, CveTabla, CveTipoMovimiento) SELECT B.CveBitacora, B.CveTabla, B.CveTipoMovimiento FROM dbo.Bit_Bitacora AS B WHERE ( @EsAdministrador = 1 OR ( @EsDesarrollador = 1 AND EXISTS ( SELECT 1 FROM dbo.Bit_BitacoraRelaciones AS BRCliente INNER JOIN dbo.Bit_Tablas AS TCliente ON TCliente.CveTabla = BRCliente.CveTablaRelacionada INNER JOIN dbo.Bit_Campos AS CCliente ON CCliente.CveCampo = BRCliente.CveCampoRelacion AND CCliente.CveTabla = TCliente.CveTabla INNER JOIN dbo.Sis_UsuariosClientes AS UC ON UC.CveCliente = BRCliente.ValorRelacionNumero AND UC.CveUsuario = @CveUsuarioEjecuta AND UC.Activo = 1 WHERE BRCliente.CveBitacora = B.CveBitacora AND BRCliente.VisibleDesarrollador = 1 AND TCliente.NombreTablaSQL = 'Clientes' AND CCliente.NombreCampoSQL = 'CveCliente' ) ) ) AND B.CveBitacoraEvento = @CveBitacoraEvento; IF NOT EXISTS (SELECT 1 FROM @RegistrosPlantilla) BEGIN SET @ErrorMostrar = 'No se encontró el evento de bitácora solicitado.'; SET @ErrorTipo = 2; RETURN; END; -- Leer únicamente los campos del evento autorizado; sin encabezados ni relaciones de salida. INSERT INTO @ValoresPlantilla ( CveBitacora, CveCampo, NombreCampoSQL, AliasReferencia, TipoDatoSQL, MostrarEnBitacora, ValorAnterior, ValorNuevo, ValorAnteriorMostrar, ValorNuevoMostrar, Orden ) SELECT R.CveBitacora, C.CveCampo, C.NombreCampoSQL, M.NombreCampoSQL, C.TipoDatoSQL, C.MostrarEnBitacora, BC.ValorAnterior, BC.ValorNuevo, BC.ValorAnteriorMostrar, BC.ValorNuevoMostrar, C.CveCampo FROM @RegistrosPlantilla AS R INNER JOIN dbo.Bit_BitacoraCampos AS BC ON BC.CveBitacora = R.CveBitacora INNER JOIN dbo.Bit_Campos AS C ON C.CveCampo = BC.CveCampo LEFT JOIN dbo.Bit_Campos AS M ON M.CveCampo = C.CveCampoMostrarReferencia AND M.CveTabla = C.CveTablaReferencia OPTION (LOOP JOIN, MAXDOP 1); UPDATE @ValoresPlantilla SET ValorAnteriorMostrar = COALESCE(ValorAnteriorMostrar, CASE WHEN ValorAnterior IS NULL THEN NULL WHEN TipoDatoSQL = 'bit' AND ValorAnterior IN (N'0', N'1') THEN CASE WHEN ValorAnterior = N'1' THEN N'Sí' ELSE N'No' END WHEN TipoDatoSQL = 'time' AND TRY_CONVERT(time, ValorAnterior) IS NOT NULL THEN LEFT(CONVERT(varchar(16), TRY_CONVERT(time, ValorAnterior), 108), 5) END), ValorNuevoMostrar = COALESCE(ValorNuevoMostrar, CASE WHEN ValorNuevo IS NULL THEN NULL WHEN TipoDatoSQL = 'bit' AND ValorNuevo IN (N'0', N'1') THEN CASE WHEN ValorNuevo = N'1' THEN N'Sí' ELSE N'No' END WHEN TipoDatoSQL = 'time' AND TRY_CONVERT(time, ValorNuevo) IS NOT NULL THEN LEFT(CONVERT(varchar(16), TRY_CONVERT(time, ValorNuevo), 108), 5) END); -- Sustitución literal de tokens; nunca se ejecuta el contenido de una plantilla. DECLARE @RegistroPlantilla bigint; DECLARE @TablaPlantilla int; DECLARE @CampoPlantilla int; DECLARE @TextoPlantilla nvarchar(1000); DECLARE @MovimientoPlantilla tinyint; DECLARE @Pendiente nvarchar(max); DECLARE @AnteriorPlantilla nvarchar(max); DECLARE @NuevoPlantilla nvarchar(max); DECLARE @InicioToken int; DECLARE @FinToken int; DECLARE @Token nvarchar(128); DECLARE @CampoToken int; DECLARE @Coincidencias int; DECLARE @AnteriorToken nvarchar(max); DECLARE @NuevoToken nvarchar(max); DECLARE @Completa bit; DECLARE @CamposUsados TABLE (CveCampo int NOT NULL PRIMARY KEY); DECLARE CursorPlantillas CURSOR LOCAL FAST_FORWARD FOR SELECT R.CveBitacora, R.CveTabla, P.CveCampo, P.Plantilla, R.CveTipoMovimiento FROM @RegistrosPlantilla AS R INNER JOIN dbo.Bit_Campos AS P ON P.CveTabla = R.CveTabla WHERE P.Activo = 1 AND P.MostrarEnBitacora = 1 AND P.Plantilla IS NOT NULL AND NOT EXISTS ( SELECT 1 FROM @ValoresPlantilla AS V WHERE V.CveBitacora = R.CveBitacora AND V.CveCampo = P.CveCampo ) ORDER BY R.CveBitacora, P.CveCampo; OPEN CursorPlantillas; FETCH NEXT FROM CursorPlantillas INTO @RegistroPlantilla, @TablaPlantilla, @CampoPlantilla, @TextoPlantilla, @MovimientoPlantilla; WHILE @@FETCH_STATUS = 0 BEGIN DELETE FROM @CamposUsados; SELECT @Pendiente = @TextoPlantilla, @AnteriorPlantilla = N'', @NuevoPlantilla = N'', @Completa = 1; WHILE LEN(@Pendiente) > 0 BEGIN SET @InicioToken = CHARINDEX(N'{', @Pendiente); IF @InicioToken = 0 BEGIN IF CHARINDEX(N'}', @Pendiente) > 0 SET @Completa = 0; SET @AnteriorPlantilla += @Pendiente; SET @NuevoPlantilla += @Pendiente; BREAK; END; SET @FinToken = CHARINDEX(N'}', @Pendiente, @InicioToken + 1); IF @FinToken = 0 OR @FinToken - @InicioToken NOT BETWEEN 2 AND 129 OR CHARINDEX(N'}', LEFT(@Pendiente, @InicioToken - 1)) > 0 BEGIN SET @Completa = 0; BREAK; END; SET @Token = SUBSTRING(@Pendiente, @InicioToken + 1, @FinToken - @InicioToken - 1); SET @CampoToken = NULL; SELECT @Coincidencias = COUNT(*), @CampoToken = MIN(CveCampo) FROM @ValoresPlantilla WHERE CveBitacora = @RegistroPlantilla AND NombreCampoSQL = @Token; IF @Coincidencias = 0 BEGIN SELECT @Coincidencias = COUNT(*), @CampoToken = MIN(CveCampo) FROM @ValoresPlantilla WHERE CveBitacora = @RegistroPlantilla AND AliasReferencia = @Token; END; IF @Coincidencias <> 1 OR EXISTS ( SELECT 1 FROM dbo.Bit_Campos WHERE CveCampo = @CampoToken AND Plantilla IS NOT NULL ) BEGIN SET @Completa = 0; BREAK; END; -- Una clave de catálogo sin texto histórico no permite reconstruir su nombre anterior. IF EXISTS ( SELECT 1 FROM @ValoresPlantilla WHERE CveBitacora = @RegistroPlantilla AND CveCampo = @CampoToken AND AliasReferencia IS NOT NULL AND ( (ValorAnterior IS NOT NULL AND ValorAnteriorMostrar IS NULL) OR (ValorNuevo IS NOT NULL AND ValorNuevoMostrar IS NULL) ) ) BEGIN SET @Completa = 0; BREAK; END; SELECT @AnteriorToken = COALESCE(ValorAnteriorMostrar, ValorAnterior, N''), @NuevoToken = COALESCE(ValorNuevoMostrar, ValorNuevo, N'') FROM @ValoresPlantilla WHERE CveBitacora = @RegistroPlantilla AND CveCampo = @CampoToken; SET @AnteriorPlantilla += LEFT(@Pendiente, @InicioToken - 1) + @AnteriorToken; SET @NuevoPlantilla += LEFT(@Pendiente, @InicioToken - 1) + @NuevoToken; SET @Pendiente = SUBSTRING(@Pendiente, @FinToken + 1, LEN(@Pendiente)); IF NOT EXISTS (SELECT 1 FROM @CamposUsados WHERE CveCampo = @CampoToken) INSERT INTO @CamposUsados(CveCampo) VALUES (@CampoToken); END; IF NOT EXISTS (SELECT 1 FROM @CamposUsados) SET @Completa = 0; IF @Completa = 1 BEGIN INSERT INTO @ResultadosPlantilla (CveBitacora, CveCampo, ValorAnterior, ValorNuevo, Orden) SELECT @RegistroPlantilla, @CampoPlantilla, CASE WHEN @MovimientoPlantilla = 1 THEN NULL ELSE @AnteriorPlantilla END, CASE WHEN @MovimientoPlantilla = 3 THEN NULL ELSE @NuevoPlantilla END, MIN(V.Orden) FROM @ValoresPlantilla AS V INNER JOIN @CamposUsados AS U ON U.CveCampo = V.CveCampo WHERE V.CveBitacora = @RegistroPlantilla; INSERT INTO @CamposSustituidos(CveBitacora, CveCampo) SELECT @RegistroPlantilla, U.CveCampo FROM @CamposUsados AS U INNER JOIN @ValoresPlantilla AS V ON V.CveBitacora = @RegistroPlantilla AND V.CveCampo = U.CveCampo WHERE V.MostrarEnBitacora = 0 AND NOT EXISTS ( SELECT 1 FROM @CamposSustituidos AS S WHERE S.CveBitacora = @RegistroPlantilla AND S.CveCampo = U.CveCampo ); END ELSE IF @EsLecturaHistorica = 0 BEGIN CLOSE CursorPlantillas; DEALLOCATE CursorPlantillas; SET @ErrorMostrar = 'No se pudo formar la plantilla de bitácora: revise sus campos y valores mostrables.'; SET @ErrorTipo = 2; RETURN; END; FETCH NEXT FROM CursorPlantillas INTO @RegistroPlantilla, @TablaPlantilla, @CampoPlantilla, @TextoPlantilla, @MovimientoPlantilla; END; CLOSE CursorPlantillas; DEALLOCATE CursorPlantillas; ;WITH Detalle AS ( SELECT V.CveBitacora, V.CveCampo, V.Orden, COALESCE(V.ValorAnteriorMostrar, V.ValorAnterior) AS ValorAnteriorMostrar, COALESCE(V.ValorNuevoMostrar, V.ValorNuevo) AS ValorNuevoMostrar FROM @ValoresPlantilla AS V WHERE (@EsAdministrador = 1 OR V.MostrarEnBitacora = 1) AND NOT EXISTS ( SELECT 1 FROM @CamposSustituidos AS S WHERE S.CveBitacora = V.CveBitacora AND S.CveCampo = V.CveCampo ) UNION ALL SELECT CveBitacora, CveCampo, Orden, ValorAnterior, ValorNuevo FROM @ResultadosPlantilla ) SELECT D.CveBitacora, CONVERT(int, ROW_NUMBER() OVER (ORDER BY D.CveBitacora, D.Orden, D.CveCampo)) AS Orden, CASE WHEN @EsAdministrador = 1 THEN COALESCE(B.LlaveRegistroTexto, CONVERT(nvarchar(250), B.LlaveRegistro)) ELSE NULL END AS LlaveRegistro, C.NombreMostrar, D.ValorAnteriorMostrar, D.ValorNuevoMostrar FROM Detalle AS D INNER JOIN dbo.Bit_Bitacora AS B ON B.CveBitacora = D.CveBitacora INNER JOIN dbo.Bit_Campos AS C ON C.CveCampo = D.CveCampo ORDER BY D.CveBitacora, D.Orden, D.CveCampo OPTION (LOOP JOIN, MAXDOP 1, RECOMPILE); SET @Return = 1; END TRY BEGIN CATCH SET @ErrorMostrar = 'No fue posible consultar el detalle de la bitácora. Contacte a Sistemas.'; SET @ErrorTipo = 1; SET @Return = 0; DECLARE @ErrorTecnico nvarchar(2048) = CONCAT( 'Número: ', ERROR_NUMBER(), '; Procedimiento: ', COALESCE(ERROR_PROCEDURE(), 'PA_Bit_BitacoraEventoObtener'), '; Línea: ', ERROR_LINE(), '; Detalle: ', ERROR_MESSAGE() ); RAISERROR(N'%s', 10, 1, @ErrorTecnico); END CATCH; END; GO CREATE OR ALTER PROCEDURE dbo.PA_Bit_BitacoraList @NombreTablaSQL varchar(128) = NULL, @LlaveRegistro nvarchar(250) = NULL, @CveUsuarioFiltro int = NULL, @FechaInicio datetime = NULL, @FechaFin datetime = NULL, @Modulo varchar(150) = NULL, @CveUsuarioEjecuta int, @ErrorMostrar varchar(max) OUTPUT, @ErrorTipo int OUTPUT, @Return bit OUTPUT AS BEGIN SET NOCOUNT ON; SELECT @ErrorMostrar = NULL, @ErrorTipo = 0, @Return = 0; BEGIN TRY DECLARE @EsAdministrador bit = 0; DECLARE @EsDesarrollador bit = 0; SELECT @EsAdministrador = CASE WHEN R.NombreRol IN ('SuperUsuario', 'Administrador') THEN 1 ELSE 0 END, @EsDesarrollador = CASE WHEN R.NombreRol = 'Empresa' THEN 1 ELSE 0 END FROM dbo.Sis_Usuarios AS U INNER JOIN dbo.Sis_Roles AS R ON R.CveRol = U.CveRol WHERE U.CveUsuario = @CveUsuarioEjecuta AND U.Activo = 1 AND R.Activo = 1; SELECT B.CveBitacora, B.CveTabla, T.NombreTablaSQL, T.NombreMostrar AS NombreTabla, B.CveTipoMovimiento, M.Nombre AS TipoMovimiento, A.NombreAccion, B.CveUsuarioEjecuta, B.CveUsuarioAutoriza, B.FechaAutorizacion, B.LlaveRegistro, B.DescripcionMovimiento, B.FechaMovimiento, B.DireccionIP, B.NombreEquipo, B.Modulo FROM ( SELECT R.CveBitacora, R.CveTabla, R.CveTipoMovimiento, COALESCE(R.LlaveRegistroTexto, CONVERT(nvarchar(250), R.LlaveRegistro)) AS LlaveRegistro, E.CveDefinicionAccion, E.CveUsuarioEjecuta, E.CveUsuarioAutoriza, E.FechaAutorizacion, E.FechaMovimiento, E.DireccionIP, E.NombreEquipo, E.Modulo, CASE WHEN @EsAdministrador = 1 OR NOT EXISTS ( SELECT 1 FROM dbo.Bit_Bitacora AS R2 WHERE R2.CveBitacoraEvento = E.CveBitacoraEvento AND NOT (( @EsAdministrador = 1 OR ( @EsDesarrollador = 1 AND EXISTS ( SELECT 1 FROM dbo.Bit_BitacoraRelaciones AS BRCliente INNER JOIN dbo.Bit_Tablas AS TCliente ON TCliente.CveTabla = BRCliente.CveTablaRelacionada INNER JOIN dbo.Bit_Campos AS CCliente ON CCliente.CveCampo = BRCliente.CveCampoRelacion AND CCliente.CveTabla = TCliente.CveTabla INNER JOIN dbo.Sis_UsuariosClientes AS UC ON UC.CveCliente = BRCliente.ValorRelacionNumero AND UC.CveUsuario = @CveUsuarioEjecuta AND UC.Activo = 1 WHERE BRCliente.CveBitacora = R2.CveBitacora AND BRCliente.VisibleDesarrollador = 1 AND TCliente.NombreTablaSQL = 'Clientes' AND CCliente.NombreCampoSQL = 'CveCliente' ) ) )) ) THEN E.DescripcionMovimiento ELSE N'Operación con registros de acceso restringido.' END AS DescripcionMovimiento FROM dbo.Bit_Bitacora AS R INNER JOIN dbo.Bit_BitacoraEventos AS E ON E.CveBitacoraEvento = R.CveBitacoraEvento ) AS B INNER JOIN dbo.Bit_Tablas AS T ON T.CveTabla = B.CveTabla INNER JOIN dbo.Bit_TiposMovimiento AS M ON M.CveTipoMovimiento = B.CveTipoMovimiento LEFT JOIN dbo.Bit_DefinicionesAccion AS A ON A.CveDefinicionAccion = B.CveDefinicionAccion WHERE (@NombreTablaSQL IS NULL OR T.NombreTablaSQL = @NombreTablaSQL COLLATE Modern_Spanish_CS_AS) AND (@LlaveRegistro IS NULL OR B.LlaveRegistro = @LlaveRegistro COLLATE Modern_Spanish_CS_AS) AND (@CveUsuarioFiltro IS NULL OR B.CveUsuarioEjecuta = @CveUsuarioFiltro) AND (@FechaInicio IS NULL OR B.FechaMovimiento >= @FechaInicio) AND ( @FechaFin IS NULL OR B.FechaMovimiento < DATEADD(day, 1, CONVERT(date, @FechaFin)) ) AND (@Modulo IS NULL OR B.Modulo = @Modulo COLLATE Modern_Spanish_CS_AS) AND ( @EsAdministrador = 1 OR ( @EsDesarrollador = 1 AND EXISTS ( SELECT 1 FROM dbo.Bit_BitacoraRelaciones AS BR INNER JOIN dbo.Bit_Tablas AS RT ON RT.CveTabla = BR.CveTablaRelacionada INNER JOIN dbo.Bit_Campos AS RC ON RC.CveCampo = BR.CveCampoRelacion AND RC.CveTabla = RT.CveTabla INNER JOIN dbo.Sis_UsuariosClientes AS UC ON UC.CveCliente = BR.ValorRelacionNumero AND UC.CveUsuario = @CveUsuarioEjecuta AND UC.Activo = 1 WHERE BR.CveBitacora = B.CveBitacora AND BR.VisibleDesarrollador = 1 AND RT.NombreTablaSQL = 'Clientes' AND RC.NombreCampoSQL = 'CveCliente' ) ) ) ORDER BY B.FechaMovimiento DESC, B.CveBitacora DESC; SET @Return = 1; END TRY BEGIN CATCH SET @ErrorMostrar = 'Ocurrió un error al consultar la bitácora; favor de contactar a Sistemas.'; SET @ErrorTipo = 1; SET @Return = 0; DECLARE @ErrorTecnico nvarchar(2048) = CONCAT( 'Número: ', ERROR_NUMBER(), '; Procedimiento: ', COALESCE(ERROR_PROCEDURE(), 'PA_Bit_BitacoraList'), '; Línea: ', ERROR_LINE(), '; Detalle: ', ERROR_MESSAGE() ); RAISERROR(N'%s', 10, 1, @ErrorTecnico); END CATCH; END; GO CREATE OR ALTER PROCEDURE dbo.PA_Bit_BitacoraListByRelations @RelacionesXml xml = NULL, @RelacionesMostrarXml xml = NULL, @RegistrosOmitidos int, @RegistrosPorPagina int, @CveUsuarioEjecuta int, @ErrorMostrar varchar(max) OUTPUT, @ErrorTipo int OUTPUT, @Return bit OUTPUT AS BEGIN SET NOCOUNT ON; SELECT @ErrorMostrar = NULL, @ErrorTipo = 0, @Return = 0; BEGIN TRY IF @RegistrosOmitidos < 0 OR @RegistrosPorPagina <= 0 BEGIN SET @ErrorMostrar = 'Los datos de paginación no son válidos.'; SET @ErrorTipo = 2; RETURN; END; DECLARE @EsAdministrador bit = 0; DECLARE @EsDesarrollador bit = 0; SELECT @EsAdministrador = CASE WHEN R.NombreRol IN ('SuperUsuario', 'Administrador') THEN 1 ELSE 0 END, @EsDesarrollador = CASE WHEN R.NombreRol = 'Empresa' THEN 1 ELSE 0 END FROM dbo.Sis_Usuarios AS U INNER JOIN dbo.Sis_Roles AS R ON R.CveRol = U.CveRol WHERE U.CveUsuario = @CveUsuarioEjecuta AND U.Activo = 1 AND R.Activo = 1; DECLARE @Filtros TABLE ( Orden int NOT NULL PRIMARY KEY, NombreTablaSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, NombreCampoSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, Valor nvarchar(250) COLLATE Modern_Spanish_CS_AS NOT NULL, SoloExistencia bit NOT NULL ); IF @RelacionesXml IS NOT NULL BEGIN INSERT INTO @Filtros ( Orden, NombreTablaSQL, NombreCampoSQL, Valor, SoloExistencia ) SELECT R.value('@Orden', 'int'), R.value('@Tabla', 'varchar(128)'), R.value('@Campo', 'varchar(128)'), R.value('@Valor', 'nvarchar(250)'), CASE WHEN R.exist('@SoloExistencia') = 1 THEN R.value('@SoloExistencia', 'bit') ELSE 0 END FROM @RelacionesXml.nodes('/Relaciones/Relacion') AS X(R); END; DECLARE @FiltrosResueltos TABLE ( Orden int NOT NULL PRIMARY KEY, CveTabla int NOT NULL, CveCampo int NOT NULL, TipoDatoSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, Valor nvarchar(250) COLLATE Modern_Spanish_CS_AS NOT NULL, SoloExistencia bit NOT NULL, EsEntero bit NOT NULL ); INSERT INTO @FiltrosResueltos ( Orden, CveTabla, CveCampo, TipoDatoSQL, Valor, SoloExistencia, EsEntero ) SELECT F.Orden, T.CveTabla, C.CveCampo, C.TipoDatoSQL, F.Valor, F.SoloExistencia, CASE WHEN C.TipoDatoSQL IN ( 'tinyint', 'smallint', 'int', 'bigint' ) THEN 1 ELSE 0 END FROM @Filtros AS F INNER JOIN dbo.Bit_Tablas AS T ON T.NombreTablaSQL = F.NombreTablaSQL AND T.Activo = 1 INNER JOIN dbo.Bit_Campos AS C ON C.CveTabla = T.CveTabla AND C.NombreCampoSQL = F.NombreCampoSQL AND C.Activo = 1 INNER JOIN sys.schemas AS SF ON SF.name = 'dbo' INNER JOIN sys.tables AS TF ON TF.schema_id = SF.schema_id AND TF.name COLLATE Modern_Spanish_CS_AS = T.NombreTablaSQL INNER JOIN sys.columns AS CF ON CF.object_id = TF.object_id AND CF.name COLLATE Modern_Spanish_CS_AS = C.NombreCampoSQL WHERE C.TipoDatoSQL IS NOT NULL; IF (SELECT COUNT(1) FROM @Filtros) <> (SELECT COUNT(1) FROM @FiltrosResueltos) BEGIN SET @ErrorMostrar = 'Una relación utiliza una tabla o campo que no está configurado en la bitácora.'; SET @ErrorTipo = 2; RETURN; END; IF EXISTS ( SELECT 1 FROM @FiltrosResueltos WHERE SoloExistencia = 0 AND EsEntero = 1 AND TRY_CONVERT(bigint, Valor) IS NULL ) BEGIN SET @ErrorMostrar = 'El valor de una relación numérica no es válido.'; SET @ErrorTipo = 2; RETURN; END; DECLARE @RelacionesMostrar TABLE ( Orden int NOT NULL, NombreTablaSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, NombreCampoSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL ); IF @RelacionesMostrarXml IS NOT NULL BEGIN INSERT INTO @RelacionesMostrar ( Orden, NombreTablaSQL, NombreCampoSQL ) SELECT R.value('@Orden', 'int'), R.value('@Tabla', 'varchar(128)'), R.value('@Campo', 'varchar(128)') FROM @RelacionesMostrarXml.nodes('/RelacionesMostrar/Relacion') AS X(R); END; IF (SELECT COUNT(1) FROM @RelacionesMostrar) > 10 OR EXISTS ( SELECT 1 FROM @RelacionesMostrar WHERE Orden <= 0 ) OR EXISTS ( SELECT Orden FROM @RelacionesMostrar GROUP BY Orden HAVING COUNT(1) > 1 ) OR EXISTS ( SELECT NombreTablaSQL, NombreCampoSQL FROM @RelacionesMostrar GROUP BY NombreTablaSQL, NombreCampoSQL HAVING COUNT(1) > 1 ) BEGIN SET @ErrorMostrar = 'La configuración de relaciones a mostrar no es válida.'; SET @ErrorTipo = 2; RETURN; END; DECLARE @RelacionesMostrarResueltas TABLE ( Orden int NOT NULL PRIMARY KEY, CveTabla int NOT NULL, NombreTablaSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, NombreTabla varchar(150) COLLATE Modern_Spanish_CS_AS NOT NULL, CveCampoLlave int NOT NULL, NombreCampoLlaveSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, CveCampoMostrar int NOT NULL, NombreCampoSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, NombreCampo varchar(150) COLLATE Modern_Spanish_CS_AS NOT NULL ); INSERT INTO @RelacionesMostrarResueltas ( Orden, CveTabla, NombreTablaSQL, NombreTabla, CveCampoLlave, NombreCampoLlaveSQL, CveCampoMostrar, NombreCampoSQL, NombreCampo ) SELECT R.Orden, T.CveTabla, T.NombreTablaSQL, T.NombreMostrar, CL.CveCampo, CL.NombreCampoSQL, CM.CveCampo, CM.NombreCampoSQL, CM.NombreMostrar FROM @RelacionesMostrar AS R INNER JOIN dbo.Bit_Tablas AS T ON T.NombreTablaSQL = R.NombreTablaSQL AND T.Activo = 1 AND T.NombreCampoLlaveSQL IS NOT NULL INNER JOIN dbo.Bit_Campos AS CL ON CL.CveTabla = T.CveTabla AND CL.NombreCampoSQL = T.NombreCampoLlaveSQL AND CL.Activo = 1 INNER JOIN dbo.Bit_Campos AS CM ON CM.CveTabla = T.CveTabla AND CM.NombreCampoSQL = R.NombreCampoSQL AND CM.MostrarEnBitacora = 1 AND CM.Activo = 1 INNER JOIN sys.schemas AS SC ON SC.name = 'dbo' INNER JOIN sys.tables AS ST ON ST.schema_id = SC.schema_id AND ST.name COLLATE Modern_Spanish_CS_AS = T.NombreTablaSQL INNER JOIN sys.columns AS SCL ON SCL.object_id = ST.object_id AND SCL.name COLLATE Modern_Spanish_CS_AS = CL.NombreCampoSQL INNER JOIN sys.columns AS SCM ON SCM.object_id = ST.object_id AND SCM.name COLLATE Modern_Spanish_CS_AS = CM.NombreCampoSQL; IF (SELECT COUNT(1) FROM @RelacionesMostrar) <> (SELECT COUNT(1) FROM @RelacionesMostrarResueltas) BEGIN SET @ErrorMostrar = 'Una relación a mostrar utiliza una tabla o campo no permitido.'; SET @ErrorTipo = 2; RETURN; END; CREATE TABLE #BitacorasCoincidentes ( CveBitacora bigint NOT NULL PRIMARY KEY, FechaMovimiento datetime NOT NULL ); INSERT INTO #BitacorasCoincidentes ( CveBitacora, FechaMovimiento ) SELECT B.CveBitacora, B.FechaMovimiento FROM ( SELECT R.CveBitacora, R.CveTabla, R.CveTipoMovimiento, COALESCE(R.LlaveRegistroTexto, CONVERT(nvarchar(250), R.LlaveRegistro)) AS LlaveRegistro, E.CveDefinicionAccion, E.CveUsuarioEjecuta, E.CveUsuarioAutoriza, E.FechaAutorizacion, E.FechaMovimiento, E.DireccionIP, E.NombreEquipo, E.Modulo, CASE WHEN @EsAdministrador = 1 OR NOT EXISTS ( SELECT 1 FROM dbo.Bit_Bitacora AS R2 WHERE R2.CveBitacoraEvento = E.CveBitacoraEvento AND NOT (( @EsAdministrador = 1 OR ( @EsDesarrollador = 1 AND EXISTS ( SELECT 1 FROM dbo.Bit_BitacoraRelaciones AS BRCliente INNER JOIN dbo.Bit_Tablas AS TCliente ON TCliente.CveTabla = BRCliente.CveTablaRelacionada INNER JOIN dbo.Bit_Campos AS CCliente ON CCliente.CveCampo = BRCliente.CveCampoRelacion AND CCliente.CveTabla = TCliente.CveTabla INNER JOIN dbo.Sis_UsuariosClientes AS UC ON UC.CveCliente = BRCliente.ValorRelacionNumero AND UC.CveUsuario = @CveUsuarioEjecuta AND UC.Activo = 1 WHERE BRCliente.CveBitacora = R2.CveBitacora AND BRCliente.VisibleDesarrollador = 1 AND TCliente.NombreTablaSQL = 'Clientes' AND CCliente.NombreCampoSQL = 'CveCliente' ) ) )) ) THEN E.DescripcionMovimiento ELSE N'Operación con registros de acceso restringido.' END AS DescripcionMovimiento FROM dbo.Bit_Bitacora AS R INNER JOIN dbo.Bit_BitacoraEventos AS E ON E.CveBitacoraEvento = R.CveBitacoraEvento ) AS B WHERE ( @EsAdministrador = 1 OR ( @EsDesarrollador = 1 AND EXISTS ( SELECT 1 FROM dbo.Bit_BitacoraRelaciones AS BRCliente INNER JOIN dbo.Bit_Tablas AS TCliente ON TCliente.CveTabla = BRCliente.CveTablaRelacionada INNER JOIN dbo.Bit_Campos AS CCliente ON CCliente.CveCampo = BRCliente.CveCampoRelacion AND CCliente.CveTabla = TCliente.CveTabla INNER JOIN dbo.Sis_UsuariosClientes AS UC ON UC.CveCliente = BRCliente.ValorRelacionNumero AND UC.CveUsuario = @CveUsuarioEjecuta AND UC.Activo = 1 WHERE BRCliente.CveBitacora = B.CveBitacora AND BRCliente.VisibleDesarrollador = 1 AND TCliente.NombreTablaSQL = 'Clientes' AND CCliente.NombreCampoSQL = 'CveCliente' ) ) ) AND ( @EsDesarrollador = 0 OR NOT EXISTS ( SELECT 1 FROM @FiltrosResueltos AS FCliente INNER JOIN dbo.Bit_Tablas AS TClienteFiltro ON TClienteFiltro.CveTabla = FCliente.CveTabla INNER JOIN dbo.Bit_Campos AS CClienteFiltro ON CClienteFiltro.CveCampo = FCliente.CveCampo AND CClienteFiltro.CveTabla = TClienteFiltro.CveTabla WHERE TClienteFiltro.NombreTablaSQL = 'Clientes' AND CClienteFiltro.NombreCampoSQL = 'CveCliente' AND FCliente.SoloExistencia = 0 AND NOT EXISTS ( SELECT 1 FROM dbo.Sis_UsuariosClientes AS UCClienteFiltro WHERE UCClienteFiltro.CveUsuario = @CveUsuarioEjecuta AND UCClienteFiltro.CveCliente = TRY_CONVERT(bigint, FCliente.Valor) AND UCClienteFiltro.Activo = 1 ) ) ) AND NOT EXISTS ( SELECT 1 FROM @FiltrosResueltos AS F WHERE NOT EXISTS ( SELECT 1 FROM dbo.Bit_BitacoraRelaciones AS BR WHERE BR.CveBitacora = B.CveBitacora AND BR.CveTablaRelacionada = F.CveTabla AND BR.CveCampoRelacion = F.CveCampo AND ( @EsAdministrador = 1 OR BR.VisibleDesarrollador = 1 ) AND ( F.SoloExistencia = 1 OR ( ( F.EsEntero = 1 AND BR.ValorRelacionNumero = TRY_CONVERT(bigint, F.Valor) ) OR ( F.EsEntero = 0 AND BR.ValorRelacionTexto = F.Valor COLLATE Modern_Spanish_CS_AS ) ) ) ) ); DECLARE @TotalRegistros int; SELECT @TotalRegistros = COUNT(DISTINCT CveBitacora) FROM #BitacorasCoincidentes; CREATE TABLE #BitacorasPagina ( CveBitacora bigint NOT NULL PRIMARY KEY, Orden int NOT NULL ); INSERT INTO #BitacorasPagina ( CveBitacora, Orden ) SELECT C.CveBitacora, ROW_NUMBER() OVER ( ORDER BY C.FechaMovimiento DESC, C.CveBitacora DESC ) FROM #BitacorasCoincidentes AS C ORDER BY C.FechaMovimiento DESC, C.CveBitacora DESC OFFSET @RegistrosOmitidos ROWS FETCH NEXT @RegistrosPorPagina ROWS ONLY; SELECT @TotalRegistros AS TotalRegistros; SELECT B.CveBitacora, B.CveTabla, T.NombreTablaSQL, T.NombreMostrar AS NombreTabla, B.CveTipoMovimiento, M.Nombre AS TipoMovimiento, B.CveDefinicionAccion, A.NombreAccion, A.DescripcionAccion, B.CveUsuarioEjecuta, COALESCE( NULLIF(U.NombreUsuario, ''), CONCAT('(', B.CveUsuarioEjecuta, ') Eliminado') ) AS NombreUsuarioEjecuta, B.CveUsuarioAutoriza, B.FechaAutorizacion, B.LlaveRegistro, B.DescripcionMovimiento, B.FechaMovimiento, B.DireccionIP, B.NombreEquipo, B.Modulo FROM #BitacorasPagina AS P INNER JOIN ( SELECT R.CveBitacora, R.CveTabla, R.CveTipoMovimiento, COALESCE(R.LlaveRegistroTexto, CONVERT(nvarchar(250), R.LlaveRegistro)) AS LlaveRegistro, E.CveDefinicionAccion, E.CveUsuarioEjecuta, E.CveUsuarioAutoriza, E.FechaAutorizacion, E.FechaMovimiento, E.DireccionIP, E.NombreEquipo, E.Modulo, CASE WHEN @EsAdministrador = 1 OR NOT EXISTS ( SELECT 1 FROM dbo.Bit_Bitacora AS R2 WHERE R2.CveBitacoraEvento = E.CveBitacoraEvento AND NOT (( @EsAdministrador = 1 OR ( @EsDesarrollador = 1 AND EXISTS ( SELECT 1 FROM dbo.Bit_BitacoraRelaciones AS BRCliente INNER JOIN dbo.Bit_Tablas AS TCliente ON TCliente.CveTabla = BRCliente.CveTablaRelacionada INNER JOIN dbo.Bit_Campos AS CCliente ON CCliente.CveCampo = BRCliente.CveCampoRelacion AND CCliente.CveTabla = TCliente.CveTabla INNER JOIN dbo.Sis_UsuariosClientes AS UC ON UC.CveCliente = BRCliente.ValorRelacionNumero AND UC.CveUsuario = @CveUsuarioEjecuta AND UC.Activo = 1 WHERE BRCliente.CveBitacora = R2.CveBitacora AND BRCliente.VisibleDesarrollador = 1 AND TCliente.NombreTablaSQL = 'Clientes' AND CCliente.NombreCampoSQL = 'CveCliente' ) ) )) ) THEN E.DescripcionMovimiento ELSE N'Operación con registros de acceso restringido.' END AS DescripcionMovimiento FROM dbo.Bit_Bitacora AS R INNER JOIN dbo.Bit_BitacoraEventos AS E ON E.CveBitacoraEvento = R.CveBitacoraEvento ) AS B ON B.CveBitacora = P.CveBitacora INNER JOIN dbo.Bit_Tablas AS T ON T.CveTabla = B.CveTabla INNER JOIN dbo.Bit_TiposMovimiento AS M ON M.CveTipoMovimiento = B.CveTipoMovimiento LEFT JOIN dbo.Bit_DefinicionesAccion AS A ON A.CveDefinicionAccion = B.CveDefinicionAccion LEFT JOIN dbo.Sis_Usuarios AS U ON U.CveUsuario = B.CveUsuarioEjecuta ORDER BY P.Orden; CREATE TABLE #CamposPagina ( OrdenPagina int NOT NULL, CveBitacoraCampo bigint NOT NULL PRIMARY KEY, CveBitacora bigint NOT NULL, CveCampo int NOT NULL, NombreCampoSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, NombreMostrar varchar(150) COLLATE Modern_Spanish_CS_AS NOT NULL, ValorAnterior nvarchar(max) COLLATE Modern_Spanish_CS_AS NULL, ValorNuevo nvarchar(max) COLLATE Modern_Spanish_CS_AS NULL, ValorAnteriorMostrar nvarchar(max) COLLATE Modern_Spanish_CS_AS NULL, ValorNuevoMostrar nvarchar(max) COLLATE Modern_Spanish_CS_AS NULL ); INSERT INTO #CamposPagina ( OrdenPagina, CveBitacoraCampo, CveBitacora, CveCampo, NombreCampoSQL, NombreMostrar, ValorAnterior, ValorNuevo, ValorAnteriorMostrar, ValorNuevoMostrar ) SELECT P.Orden, BC.CveBitacoraCampo, BC.CveBitacora, BC.CveCampo, C.NombreCampoSQL, C.NombreMostrar, BC.ValorAnterior, BC.ValorNuevo, BC.ValorAnterior, BC.ValorNuevo FROM #BitacorasPagina AS P INNER JOIN dbo.Bit_BitacoraCampos AS BC ON BC.CveBitacora = P.CveBitacora INNER JOIN dbo.Bit_Campos AS C ON C.CveCampo = BC.CveCampo WHERE @EsAdministrador = 1 OR C.MostrarEnBitacora = 1; DECLARE @CveCampoOrigenReferencia int; DECLARE @NombreTablaReferencia varchar(128); DECLARE @NombreCampoLlaveReferencia varchar(128); DECLARE @NombreCampoMostrarReferencia varchar(128); DECLARE @SqlCampoReferencia nvarchar(max); -- Los identificadores del SQL dinámico salen únicamente de metadatos -- activos y se verifican contra sys.tables/sys.columns antes de usarse. DECLARE CursorCamposReferencia CURSOR LOCAL FAST_FORWARD FOR SELECT DISTINCT CO.CveCampo, TR.NombreTablaSQL, CL.NombreCampoSQL, CM.NombreCampoSQL FROM #CamposPagina AS CP INNER JOIN dbo.Bit_Campos AS CO ON CO.CveCampo = CP.CveCampo AND CO.Activo = 1 INNER JOIN dbo.Bit_Tablas AS TR ON TR.CveTabla = CO.CveTablaReferencia AND TR.Activo = 1 AND TR.NombreCampoLlaveSQL IS NOT NULL INNER JOIN dbo.Bit_Campos AS CL ON CL.CveTabla = TR.CveTabla AND CL.NombreCampoSQL = TR.NombreCampoLlaveSQL AND CL.Activo = 1 INNER JOIN dbo.Bit_Campos AS CM ON CM.CveCampo = CO.CveCampoMostrarReferencia AND CM.CveTabla = TR.CveTabla AND CM.MostrarEnBitacora = 1 AND CM.Activo = 1 INNER JOIN sys.schemas AS SC ON SC.name = 'dbo' INNER JOIN sys.tables AS ST ON ST.schema_id = SC.schema_id AND ST.name COLLATE Modern_Spanish_CS_AS = TR.NombreTablaSQL INNER JOIN sys.columns AS SCL ON SCL.object_id = ST.object_id AND SCL.name COLLATE Modern_Spanish_CS_AS = CL.NombreCampoSQL INNER JOIN sys.columns AS SCM ON SCM.object_id = ST.object_id AND SCM.name COLLATE Modern_Spanish_CS_AS = CM.NombreCampoSQL; OPEN CursorCamposReferencia; FETCH NEXT FROM CursorCamposReferencia INTO @CveCampoOrigenReferencia, @NombreTablaReferencia, @NombreCampoLlaveReferencia, @NombreCampoMostrarReferencia; WHILE @@FETCH_STATUS = 0 BEGIN SET @SqlCampoReferencia = N' UPDATE CP SET CP.ValorAnteriorMostrar = CASE WHEN CP.ValorAnterior IS NULL THEN NULL WHEN VA.' + QUOTENAME(@NombreCampoLlaveReferencia) + N' IS NULL THEN CONCAT(N''('', CP.ValorAnterior, N'') Eliminada'') WHEN NULLIF(CONVERT(nvarchar(max), VA.' + QUOTENAME(@NombreCampoMostrarReferencia) + N'), N'''') IS NULL THEN CONCAT(N''('', CP.ValorAnterior, N'')'') ELSE CONVERT(nvarchar(max), VA.' + QUOTENAME(@NombreCampoMostrarReferencia) + N') END, CP.ValorNuevoMostrar = CASE WHEN CP.ValorNuevo IS NULL THEN NULL WHEN VN.' + QUOTENAME(@NombreCampoLlaveReferencia) + N' IS NULL THEN CONCAT(N''('', CP.ValorNuevo, N'') Eliminada'') WHEN NULLIF(CONVERT(nvarchar(max), VN.' + QUOTENAME(@NombreCampoMostrarReferencia) + N'), N'''') IS NULL THEN CONCAT(N''('', CP.ValorNuevo, N'')'') ELSE CONVERT(nvarchar(max), VN.' + QUOTENAME(@NombreCampoMostrarReferencia) + N') END FROM #CamposPagina AS CP LEFT JOIN dbo.' + QUOTENAME(@NombreTablaReferencia) + N' AS VA ON CONVERT(nvarchar(250), VA.' + QUOTENAME(@NombreCampoLlaveReferencia) + N') COLLATE Modern_Spanish_CS_AS = CP.ValorAnterior COLLATE Modern_Spanish_CS_AS LEFT JOIN dbo.' + QUOTENAME(@NombreTablaReferencia) + N' AS VN ON CONVERT(nvarchar(250), VN.' + QUOTENAME(@NombreCampoLlaveReferencia) + N') COLLATE Modern_Spanish_CS_AS = CP.ValorNuevo COLLATE Modern_Spanish_CS_AS WHERE CP.CveCampo = @CveCampoOrigen;'; EXEC sys.sp_executesql @SqlCampoReferencia, N'@CveCampoOrigen int', @CveCampoOrigen = @CveCampoOrigenReferencia; FETCH NEXT FROM CursorCamposReferencia INTO @CveCampoOrigenReferencia, @NombreTablaReferencia, @NombreCampoLlaveReferencia, @NombreCampoMostrarReferencia; END; CLOSE CursorCamposReferencia; DEALLOCATE CursorCamposReferencia; SELECT CveBitacoraCampo, CveBitacora, CveCampo, NombreCampoSQL, NombreMostrar, ValorAnterior, ValorNuevo, ValorAnteriorMostrar, ValorNuevoMostrar FROM #CamposPagina ORDER BY OrdenPagina, CveBitacoraCampo; SELECT BR.CveBitacoraRelacion, BR.CveBitacora, BR.CveTipoRelacion, TR.Nombre AS TipoRelacion, BR.CveTablaRelacionada, T.NombreTablaSQL, BR.CveCampoRelacion, C.NombreCampoSQL, BR.LlaveRegistroRelacionada, BR.ValorRelacionNumero, BR.ValorRelacionTexto, BR.VisibleDesarrollador, BR.DescripcionRelacion FROM #BitacorasPagina AS P INNER JOIN dbo.Bit_BitacoraRelaciones AS BR ON BR.CveBitacora = P.CveBitacora INNER JOIN dbo.Bit_TiposRelacion AS TR ON TR.CveTipoRelacion = BR.CveTipoRelacion INNER JOIN dbo.Bit_Tablas AS T ON T.CveTabla = BR.CveTablaRelacionada LEFT JOIN dbo.Bit_Campos AS C ON C.CveCampo = BR.CveCampoRelacion AND C.CveTabla = T.CveTabla WHERE @EsAdministrador = 1 OR BR.VisibleDesarrollador = 1 ORDER BY P.Orden, BR.CveBitacoraRelacion; CREATE TABLE #RelacionesVisuales ( CveBitacora bigint NOT NULL, Orden int NOT NULL, NombreTablaSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, NombreTabla varchar(150) COLLATE Modern_Spanish_CS_AS NOT NULL, NombreCampoSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, NombreCampo varchar(150) COLLATE Modern_Spanish_CS_AS NOT NULL, ValorClave nvarchar(250) COLLATE Modern_Spanish_CS_AS NOT NULL, ValorMostrar nvarchar(max) COLLATE Modern_Spanish_CS_AS NOT NULL ); DECLARE @OrdenRelacionMostrar int; DECLARE @CveTablaRelacionMostrar int; DECLARE @NombreTablaSQLRelacionMostrar varchar(128); DECLARE @NombreTablaRelacionMostrar varchar(150); DECLARE @CveCampoLlaveRelacionMostrar int; DECLARE @NombreCampoLlaveRelacionMostrar varchar(128); DECLARE @NombreCampoSQLRelacionMostrar varchar(128); DECLARE @NombreCampoRelacionMostrar varchar(150); DECLARE @SqlRelacionMostrar nvarchar(max); -- Tabla, llave y campo visible ya fueron resueltos contra la allowlist -- de Bit_Tablas/Bit_Campos y se delimitan nuevamente con QUOTENAME. DECLARE CursorRelacionesMostrar CURSOR LOCAL FAST_FORWARD FOR SELECT Orden, CveTabla, NombreTablaSQL, NombreTabla, CveCampoLlave, NombreCampoLlaveSQL, NombreCampoSQL, NombreCampo FROM @RelacionesMostrarResueltas ORDER BY Orden; OPEN CursorRelacionesMostrar; FETCH NEXT FROM CursorRelacionesMostrar INTO @OrdenRelacionMostrar, @CveTablaRelacionMostrar, @NombreTablaSQLRelacionMostrar, @NombreTablaRelacionMostrar, @CveCampoLlaveRelacionMostrar, @NombreCampoLlaveRelacionMostrar, @NombreCampoSQLRelacionMostrar, @NombreCampoRelacionMostrar; WHILE @@FETCH_STATUS = 0 BEGIN SET @SqlRelacionMostrar = N' INSERT INTO #RelacionesVisuales ( CveBitacora, Orden, NombreTablaSQL, NombreTabla, NombreCampoSQL, NombreCampo, ValorClave, ValorMostrar ) SELECT BR.CveBitacora, @Orden, @NombreTablaSQL, @NombreTabla, @NombreCampoSQL, @NombreCampo, BR.LlaveRegistroRelacionada, CASE WHEN V.' + QUOTENAME(@NombreCampoLlaveRelacionMostrar) + N' IS NULL THEN CONCAT(N''('', BR.LlaveRegistroRelacionada, N'') Eliminada'') WHEN NULLIF(CONVERT(nvarchar(max), V.' + QUOTENAME(@NombreCampoSQLRelacionMostrar) + N'), N'''') IS NULL THEN CONCAT(N''('', BR.LlaveRegistroRelacionada, N'')'') ELSE CONVERT(nvarchar(max), V.' + QUOTENAME(@NombreCampoSQLRelacionMostrar) + N') END FROM #BitacorasPagina AS P INNER JOIN dbo.Bit_BitacoraRelaciones AS BR ON BR.CveBitacora = P.CveBitacora INNER JOIN dbo.Bit_Bitacora AS B ON B.CveBitacora = P.CveBitacora LEFT JOIN dbo.' + QUOTENAME(@NombreTablaSQLRelacionMostrar) + N' AS V ON CONVERT(nvarchar(250), V.' + QUOTENAME(@NombreCampoLlaveRelacionMostrar) + N') COLLATE Modern_Spanish_CS_AS = BR.LlaveRegistroRelacionada COLLATE Modern_Spanish_CS_AS WHERE BR.CveTablaRelacionada = @CveTabla AND BR.CveCampoRelacion = @CveCampoLlave AND NOT ( BR.CveTipoRelacion = 1 AND BR.CveTablaRelacionada = B.CveTabla ) AND ( @EsAdministrador = 1 OR BR.VisibleDesarrollador = 1 );'; EXEC sys.sp_executesql @SqlRelacionMostrar, N'@Orden int, @NombreTablaSQL varchar(128), @NombreTabla varchar(150), @NombreCampoSQL varchar(128), @NombreCampo varchar(150), @CveTabla int, @CveCampoLlave int, @EsAdministrador bit', @Orden = @OrdenRelacionMostrar, @NombreTablaSQL = @NombreTablaSQLRelacionMostrar, @NombreTabla = @NombreTablaRelacionMostrar, @NombreCampoSQL = @NombreCampoSQLRelacionMostrar, @NombreCampo = @NombreCampoRelacionMostrar, @CveTabla = @CveTablaRelacionMostrar, @CveCampoLlave = @CveCampoLlaveRelacionMostrar, @EsAdministrador = @EsAdministrador; FETCH NEXT FROM CursorRelacionesMostrar INTO @OrdenRelacionMostrar, @CveTablaRelacionMostrar, @NombreTablaSQLRelacionMostrar, @NombreTablaRelacionMostrar, @CveCampoLlaveRelacionMostrar, @NombreCampoLlaveRelacionMostrar, @NombreCampoSQLRelacionMostrar, @NombreCampoRelacionMostrar; END; CLOSE CursorRelacionesMostrar; DEALLOCATE CursorRelacionesMostrar; SELECT RV.CveBitacora, RV.Orden, RV.NombreTablaSQL, RV.NombreTabla, RV.NombreCampoSQL, RV.NombreCampo, RV.ValorClave, RV.ValorMostrar FROM #RelacionesVisuales AS RV INNER JOIN #BitacorasPagina AS P ON P.CveBitacora = RV.CveBitacora ORDER BY P.Orden, RV.Orden; SET @Return = 1; END TRY BEGIN CATCH SET @ErrorMostrar = 'Ocurrió un error al consultar la bitácora; favor de contactar a Sistemas.'; SET @ErrorTipo = 1; SET @Return = 0; DECLARE @ErrorTecnico nvarchar(2048) = CONCAT( 'Número: ', ERROR_NUMBER(), '; Procedimiento: ', COALESCE( ERROR_PROCEDURE(), 'PA_Bit_BitacoraListByRelations' ), '; Línea: ', ERROR_LINE(), '; Detalle: ', ERROR_MESSAGE() ); RAISERROR(N'%s', 10, 1, @ErrorTecnico); END CATCH; END; GO CREATE OR ALTER PROCEDURE dbo.PA_Bit_BitacoraResumenListByRelations @RelacionesXml xml = NULL, @RelacionesMostrarXml xml = NULL, @RegistrosOmitidos int, @RegistrosPorPagina int, @CveUsuarioEjecuta int, @ErrorMostrar varchar(max) OUTPUT, @ErrorTipo int OUTPUT, @Return bit OUTPUT AS BEGIN SET NOCOUNT ON; SELECT @ErrorMostrar = NULL, @ErrorTipo = 0, @Return = 0; BEGIN TRY IF @RegistrosOmitidos < 0 OR @RegistrosPorPagina <= 0 BEGIN SET @ErrorMostrar = 'Los datos de paginación no son válidos.'; SET @ErrorTipo = 2; RETURN; END; DECLARE @EsAdministrador bit = 0; DECLARE @EsDesarrollador bit = 0; SELECT @EsAdministrador = CASE WHEN R.NombreRol IN ('SuperUsuario', 'Administrador') THEN 1 ELSE 0 END, @EsDesarrollador = CASE WHEN R.NombreRol = 'Empresa' THEN 1 ELSE 0 END FROM dbo.Sis_Usuarios AS U INNER JOIN dbo.Sis_Roles AS R ON R.CveRol = U.CveRol WHERE U.CveUsuario = @CveUsuarioEjecuta AND U.Activo = 1 AND R.Activo = 1; DECLARE @Filtros TABLE ( Orden int NOT NULL PRIMARY KEY, NombreTablaSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, NombreCampoSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, Valor nvarchar(250) COLLATE Modern_Spanish_CS_AS NOT NULL, SoloExistencia bit NOT NULL ); IF @RelacionesXml IS NOT NULL BEGIN DECLARE @IdRelaciones int; EXEC sys.sp_xml_preparedocument @IdRelaciones OUTPUT, @RelacionesXml; INSERT INTO @Filtros ( Orden, NombreTablaSQL, NombreCampoSQL, Valor, SoloExistencia ) SELECT Orden, Tabla, Campo, Valor, ISNULL(SoloExistencia, 0) FROM OPENXML( @IdRelaciones, '/Relaciones/Relacion', 1 ) WITH ( Orden int '@Orden', Tabla varchar(128) '@Tabla', Campo varchar(128) '@Campo', Valor nvarchar(250) '@Valor', SoloExistencia bit '@SoloExistencia' ); EXEC sys.sp_xml_removedocument @IdRelaciones; END; DECLARE @FiltrosResueltos TABLE ( Orden int NOT NULL PRIMARY KEY, CveTabla int NOT NULL, CveCampo int NOT NULL, TipoDatoSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, Valor nvarchar(250) COLLATE Modern_Spanish_CS_AS NOT NULL, SoloExistencia bit NOT NULL, EsEntero bit NOT NULL ); INSERT INTO @FiltrosResueltos ( Orden, CveTabla, CveCampo, TipoDatoSQL, Valor, SoloExistencia, EsEntero ) SELECT F.Orden, T.CveTabla, C.CveCampo, C.TipoDatoSQL, F.Valor, F.SoloExistencia, CASE WHEN C.TipoDatoSQL IN ( 'tinyint', 'smallint', 'int', 'bigint' ) THEN 1 ELSE 0 END FROM @Filtros AS F INNER JOIN dbo.Bit_Tablas AS T ON T.NombreTablaSQL = F.NombreTablaSQL AND T.Activo = 1 INNER JOIN dbo.Bit_Campos AS C ON C.CveTabla = T.CveTabla AND C.NombreCampoSQL = F.NombreCampoSQL AND C.Activo = 1 INNER JOIN sys.schemas AS SF ON SF.name = 'dbo' INNER JOIN sys.tables AS TF ON TF.schema_id = SF.schema_id AND TF.name COLLATE Modern_Spanish_CS_AS = T.NombreTablaSQL INNER JOIN sys.columns AS CF ON CF.object_id = TF.object_id AND CF.name COLLATE Modern_Spanish_CS_AS = C.NombreCampoSQL WHERE C.TipoDatoSQL IS NOT NULL; IF (SELECT COUNT(1) FROM @Filtros) <> (SELECT COUNT(1) FROM @FiltrosResueltos) BEGIN SET @ErrorMostrar = 'Una relación utiliza una tabla o campo que no está configurado en la bitácora.'; SET @ErrorTipo = 2; RETURN; END; IF EXISTS ( SELECT 1 FROM @FiltrosResueltos WHERE SoloExistencia = 0 AND EsEntero = 1 AND TRY_CONVERT(bigint, Valor) IS NULL ) BEGIN SET @ErrorMostrar = 'El valor de una relación numérica no es válido.'; SET @ErrorTipo = 2; RETURN; END; DECLARE @RelacionesMostrar TABLE ( Orden int NOT NULL, NombreTablaSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, NombreCampoSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL ); IF @RelacionesMostrarXml IS NOT NULL BEGIN INSERT INTO @RelacionesMostrar ( Orden, NombreTablaSQL, NombreCampoSQL ) SELECT R.value('@Orden', 'int'), R.value('@Tabla', 'varchar(128)'), R.value('@Campo', 'varchar(128)') FROM @RelacionesMostrarXml.nodes('/RelacionesMostrar/Relacion') AS X(R); END; IF (SELECT COUNT(1) FROM @RelacionesMostrar) > 10 OR EXISTS ( SELECT 1 FROM @RelacionesMostrar WHERE Orden <= 0 ) OR EXISTS ( SELECT Orden FROM @RelacionesMostrar GROUP BY Orden HAVING COUNT(1) > 1 ) OR EXISTS ( SELECT NombreTablaSQL, NombreCampoSQL FROM @RelacionesMostrar GROUP BY NombreTablaSQL, NombreCampoSQL HAVING COUNT(1) > 1 ) BEGIN SET @ErrorMostrar = 'La configuración de relaciones a mostrar no es válida.'; SET @ErrorTipo = 2; RETURN; END; DECLARE @RelacionesMostrarResueltas TABLE ( Orden int NOT NULL PRIMARY KEY, CveTabla int NOT NULL, NombreTablaSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, NombreTabla varchar(150) COLLATE Modern_Spanish_CS_AS NOT NULL, CveCampoLlave int NOT NULL, NombreCampoLlaveSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, CveCampoMostrar int NOT NULL, NombreCampoSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, NombreCampo varchar(150) COLLATE Modern_Spanish_CS_AS NOT NULL ); INSERT INTO @RelacionesMostrarResueltas ( Orden, CveTabla, NombreTablaSQL, NombreTabla, CveCampoLlave, NombreCampoLlaveSQL, CveCampoMostrar, NombreCampoSQL, NombreCampo ) SELECT R.Orden, T.CveTabla, T.NombreTablaSQL, T.NombreMostrar, CL.CveCampo, CL.NombreCampoSQL, CM.CveCampo, CM.NombreCampoSQL, CM.NombreMostrar FROM @RelacionesMostrar AS R INNER JOIN dbo.Bit_Tablas AS T ON T.NombreTablaSQL = R.NombreTablaSQL AND T.Activo = 1 AND T.NombreCampoLlaveSQL IS NOT NULL INNER JOIN dbo.Bit_Campos AS CL ON CL.CveTabla = T.CveTabla AND CL.NombreCampoSQL = T.NombreCampoLlaveSQL AND CL.Activo = 1 INNER JOIN dbo.Bit_Campos AS CM ON CM.CveTabla = T.CveTabla AND CM.NombreCampoSQL = R.NombreCampoSQL AND CM.MostrarEnBitacora = 1 AND CM.Activo = 1 INNER JOIN sys.schemas AS SC ON SC.name = 'dbo' INNER JOIN sys.tables AS ST ON ST.schema_id = SC.schema_id AND ST.name COLLATE Modern_Spanish_CS_AS = T.NombreTablaSQL INNER JOIN sys.columns AS SCL ON SCL.object_id = ST.object_id AND SCL.name COLLATE Modern_Spanish_CS_AS = CL.NombreCampoSQL INNER JOIN sys.columns AS SCM ON SCM.object_id = ST.object_id AND SCM.name COLLATE Modern_Spanish_CS_AS = CM.NombreCampoSQL; IF (SELECT COUNT(1) FROM @RelacionesMostrar) <> (SELECT COUNT(1) FROM @RelacionesMostrarResueltas) BEGIN SET @ErrorMostrar = 'Una relación a mostrar utiliza una tabla o campo no permitido.'; SET @ErrorTipo = 2; RETURN; END; CREATE TABLE #BitacorasCoincidentes ( CveBitacora bigint NOT NULL PRIMARY KEY, FechaMovimiento datetime NOT NULL ); INSERT INTO #BitacorasCoincidentes ( CveBitacora, FechaMovimiento ) SELECT B.CveBitacora, B.FechaMovimiento FROM ( SELECT R.CveBitacora, R.CveTabla, R.CveTipoMovimiento, COALESCE(R.LlaveRegistroTexto, CONVERT(nvarchar(250), R.LlaveRegistro)) AS LlaveRegistro, E.CveDefinicionAccion, E.CveUsuarioEjecuta, E.CveUsuarioAutoriza, E.FechaAutorizacion, E.FechaMovimiento, E.DireccionIP, E.NombreEquipo, E.Modulo, CASE WHEN @EsAdministrador = 1 OR NOT EXISTS ( SELECT 1 FROM dbo.Bit_Bitacora AS R2 WHERE R2.CveBitacoraEvento = E.CveBitacoraEvento AND NOT (( @EsAdministrador = 1 OR ( @EsDesarrollador = 1 AND EXISTS ( SELECT 1 FROM dbo.Bit_BitacoraRelaciones AS BRCliente INNER JOIN dbo.Bit_Tablas AS TCliente ON TCliente.CveTabla = BRCliente.CveTablaRelacionada INNER JOIN dbo.Bit_Campos AS CCliente ON CCliente.CveCampo = BRCliente.CveCampoRelacion AND CCliente.CveTabla = TCliente.CveTabla INNER JOIN dbo.Sis_UsuariosClientes AS UC ON UC.CveCliente = BRCliente.ValorRelacionNumero AND UC.CveUsuario = @CveUsuarioEjecuta AND UC.Activo = 1 WHERE BRCliente.CveBitacora = R2.CveBitacora AND BRCliente.VisibleDesarrollador = 1 AND TCliente.NombreTablaSQL = 'Clientes' AND CCliente.NombreCampoSQL = 'CveCliente' ) ) )) ) THEN E.DescripcionMovimiento ELSE N'Operación con registros de acceso restringido.' END AS DescripcionMovimiento FROM dbo.Bit_Bitacora AS R INNER JOIN dbo.Bit_BitacoraEventos AS E ON E.CveBitacoraEvento = R.CveBitacoraEvento ) AS B WHERE ( @EsAdministrador = 1 OR ( @EsDesarrollador = 1 AND EXISTS ( SELECT 1 FROM dbo.Bit_BitacoraRelaciones AS BRCliente INNER JOIN dbo.Bit_Tablas AS TCliente ON TCliente.CveTabla = BRCliente.CveTablaRelacionada INNER JOIN dbo.Bit_Campos AS CCliente ON CCliente.CveCampo = BRCliente.CveCampoRelacion AND CCliente.CveTabla = TCliente.CveTabla INNER JOIN dbo.Sis_UsuariosClientes AS UC ON UC.CveCliente = BRCliente.ValorRelacionNumero AND UC.CveUsuario = @CveUsuarioEjecuta AND UC.Activo = 1 WHERE BRCliente.CveBitacora = B.CveBitacora AND BRCliente.VisibleDesarrollador = 1 AND TCliente.NombreTablaSQL = 'Clientes' AND CCliente.NombreCampoSQL = 'CveCliente' ) ) ) AND ( @EsDesarrollador = 0 OR NOT EXISTS ( SELECT 1 FROM @FiltrosResueltos AS FCliente INNER JOIN dbo.Bit_Tablas AS TClienteFiltro ON TClienteFiltro.CveTabla = FCliente.CveTabla INNER JOIN dbo.Bit_Campos AS CClienteFiltro ON CClienteFiltro.CveCampo = FCliente.CveCampo AND CClienteFiltro.CveTabla = TClienteFiltro.CveTabla WHERE TClienteFiltro.NombreTablaSQL = 'Clientes' AND CClienteFiltro.NombreCampoSQL = 'CveCliente' AND FCliente.SoloExistencia = 0 AND NOT EXISTS ( SELECT 1 FROM dbo.Sis_UsuariosClientes AS UCClienteFiltro WHERE UCClienteFiltro.CveUsuario = @CveUsuarioEjecuta AND UCClienteFiltro.CveCliente = TRY_CONVERT(bigint, FCliente.Valor) AND UCClienteFiltro.Activo = 1 ) ) ) AND NOT EXISTS ( SELECT 1 FROM @FiltrosResueltos AS F WHERE NOT EXISTS ( SELECT 1 FROM dbo.Bit_BitacoraRelaciones AS BR WHERE BR.CveBitacora = B.CveBitacora AND BR.CveTablaRelacionada = F.CveTabla AND BR.CveCampoRelacion = F.CveCampo AND ( @EsAdministrador = 1 OR BR.VisibleDesarrollador = 1 ) AND ( F.SoloExistencia = 1 OR ( ( F.EsEntero = 1 AND BR.ValorRelacionNumero = TRY_CONVERT(bigint, F.Valor) ) OR ( F.EsEntero = 0 AND BR.ValorRelacionTexto = F.Valor COLLATE Modern_Spanish_CS_AS ) ) ) ) ); DECLARE @TotalRegistros int; SELECT @TotalRegistros = COUNT(DISTINCT CveBitacora) FROM #BitacorasCoincidentes; CREATE TABLE #BitacorasPagina ( CveBitacora bigint NOT NULL PRIMARY KEY, Orden int NOT NULL ); INSERT INTO #BitacorasPagina ( CveBitacora, Orden ) SELECT C.CveBitacora, ROW_NUMBER() OVER ( ORDER BY C.FechaMovimiento DESC, C.CveBitacora DESC ) FROM #BitacorasCoincidentes AS C ORDER BY C.FechaMovimiento DESC, C.CveBitacora DESC OFFSET @RegistrosOmitidos ROWS FETCH NEXT @RegistrosPorPagina ROWS ONLY; SELECT @TotalRegistros AS TotalRegistros; SELECT B.CveBitacora, B.CveTabla, T.NombreTablaSQL, T.NombreMostrar AS NombreTabla, B.CveTipoMovimiento, M.Nombre AS TipoMovimiento, B.CveDefinicionAccion, A.NombreAccion, A.DescripcionAccion, B.CveUsuarioEjecuta, COALESCE( NULLIF(U.NombreUsuario, ''), CONCAT('(', B.CveUsuarioEjecuta, ') Eliminado') ) AS NombreUsuarioEjecuta, B.CveUsuarioAutoriza, B.FechaAutorizacion, B.LlaveRegistro, B.DescripcionMovimiento, B.FechaMovimiento, B.DireccionIP, B.NombreEquipo, B.Modulo FROM #BitacorasPagina AS P INNER JOIN ( SELECT R.CveBitacora, R.CveTabla, R.CveTipoMovimiento, COALESCE(R.LlaveRegistroTexto, CONVERT(nvarchar(250), R.LlaveRegistro)) AS LlaveRegistro, E.CveDefinicionAccion, E.CveUsuarioEjecuta, E.CveUsuarioAutoriza, E.FechaAutorizacion, E.FechaMovimiento, E.DireccionIP, E.NombreEquipo, E.Modulo, CASE WHEN @EsAdministrador = 1 OR NOT EXISTS ( SELECT 1 FROM dbo.Bit_Bitacora AS R2 WHERE R2.CveBitacoraEvento = E.CveBitacoraEvento AND NOT (( @EsAdministrador = 1 OR ( @EsDesarrollador = 1 AND EXISTS ( SELECT 1 FROM dbo.Bit_BitacoraRelaciones AS BRCliente INNER JOIN dbo.Bit_Tablas AS TCliente ON TCliente.CveTabla = BRCliente.CveTablaRelacionada INNER JOIN dbo.Bit_Campos AS CCliente ON CCliente.CveCampo = BRCliente.CveCampoRelacion AND CCliente.CveTabla = TCliente.CveTabla INNER JOIN dbo.Sis_UsuariosClientes AS UC ON UC.CveCliente = BRCliente.ValorRelacionNumero AND UC.CveUsuario = @CveUsuarioEjecuta AND UC.Activo = 1 WHERE BRCliente.CveBitacora = R2.CveBitacora AND BRCliente.VisibleDesarrollador = 1 AND TCliente.NombreTablaSQL = 'Clientes' AND CCliente.NombreCampoSQL = 'CveCliente' ) ) )) ) THEN E.DescripcionMovimiento ELSE N'Operación con registros de acceso restringido.' END AS DescripcionMovimiento FROM dbo.Bit_Bitacora AS R INNER JOIN dbo.Bit_BitacoraEventos AS E ON E.CveBitacoraEvento = R.CveBitacoraEvento ) AS B ON B.CveBitacora = P.CveBitacora INNER JOIN dbo.Bit_Tablas AS T ON T.CveTabla = B.CveTabla INNER JOIN dbo.Bit_TiposMovimiento AS M ON M.CveTipoMovimiento = B.CveTipoMovimiento LEFT JOIN dbo.Bit_DefinicionesAccion AS A ON A.CveDefinicionAccion = B.CveDefinicionAccion LEFT JOIN dbo.Sis_Usuarios AS U ON U.CveUsuario = B.CveUsuarioEjecuta ORDER BY P.Orden; CREATE TABLE #RelacionesVisuales ( CveBitacora bigint NOT NULL, Orden int NOT NULL, NombreTablaSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, NombreTabla varchar(150) COLLATE Modern_Spanish_CS_AS NOT NULL, NombreCampoSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, NombreCampo varchar(150) COLLATE Modern_Spanish_CS_AS NOT NULL, ValorClave nvarchar(250) COLLATE Modern_Spanish_CS_AS NOT NULL, ValorMostrar nvarchar(max) COLLATE Modern_Spanish_CS_AS NOT NULL ); DECLARE @OrdenRelacionMostrar int; DECLARE @CveTablaRelacionMostrar int; DECLARE @NombreTablaSQLRelacionMostrar varchar(128); DECLARE @NombreTablaRelacionMostrar varchar(150); DECLARE @CveCampoLlaveRelacionMostrar int; DECLARE @NombreCampoLlaveRelacionMostrar varchar(128); DECLARE @NombreCampoSQLRelacionMostrar varchar(128); DECLARE @NombreCampoRelacionMostrar varchar(150); DECLARE @SqlRelacionMostrar nvarchar(max); -- Tabla, llave y campo visible ya fueron resueltos contra la allowlist -- de Bit_Tablas/Bit_Campos y se delimitan nuevamente con QUOTENAME. DECLARE CursorRelacionesMostrar CURSOR LOCAL FAST_FORWARD FOR SELECT Orden, CveTabla, NombreTablaSQL, NombreTabla, CveCampoLlave, NombreCampoLlaveSQL, NombreCampoSQL, NombreCampo FROM @RelacionesMostrarResueltas ORDER BY Orden; OPEN CursorRelacionesMostrar; FETCH NEXT FROM CursorRelacionesMostrar INTO @OrdenRelacionMostrar, @CveTablaRelacionMostrar, @NombreTablaSQLRelacionMostrar, @NombreTablaRelacionMostrar, @CveCampoLlaveRelacionMostrar, @NombreCampoLlaveRelacionMostrar, @NombreCampoSQLRelacionMostrar, @NombreCampoRelacionMostrar; WHILE @@FETCH_STATUS = 0 BEGIN SET @SqlRelacionMostrar = N' INSERT INTO #RelacionesVisuales ( CveBitacora, Orden, NombreTablaSQL, NombreTabla, NombreCampoSQL, NombreCampo, ValorClave, ValorMostrar ) SELECT BR.CveBitacora, @Orden, @NombreTablaSQL, @NombreTabla, @NombreCampoSQL, @NombreCampo, BR.LlaveRegistroRelacionada, CASE WHEN V.' + QUOTENAME(@NombreCampoLlaveRelacionMostrar) + N' IS NULL THEN CONCAT(N''('', BR.LlaveRegistroRelacionada, N'') Eliminada'') WHEN NULLIF(CONVERT(nvarchar(max), V.' + QUOTENAME(@NombreCampoSQLRelacionMostrar) + N'), N'''') IS NULL THEN CONCAT(N''('', BR.LlaveRegistroRelacionada, N'')'') ELSE CONVERT(nvarchar(max), V.' + QUOTENAME(@NombreCampoSQLRelacionMostrar) + N') END FROM #BitacorasPagina AS P INNER JOIN dbo.Bit_BitacoraRelaciones AS BR ON BR.CveBitacora = P.CveBitacora INNER JOIN dbo.Bit_Bitacora AS B ON B.CveBitacora = P.CveBitacora LEFT JOIN dbo.' + QUOTENAME(@NombreTablaSQLRelacionMostrar) + N' AS V ON CONVERT(nvarchar(250), V.' + QUOTENAME(@NombreCampoLlaveRelacionMostrar) + N') COLLATE Modern_Spanish_CS_AS = BR.LlaveRegistroRelacionada COLLATE Modern_Spanish_CS_AS WHERE BR.CveTablaRelacionada = @CveTabla AND BR.CveCampoRelacion = @CveCampoLlave AND NOT ( BR.CveTipoRelacion = 1 AND BR.CveTablaRelacionada = B.CveTabla ) AND ( @EsAdministrador = 1 OR BR.VisibleDesarrollador = 1 );'; EXEC sys.sp_executesql @SqlRelacionMostrar, N'@Orden int, @NombreTablaSQL varchar(128), @NombreTabla varchar(150), @NombreCampoSQL varchar(128), @NombreCampo varchar(150), @CveTabla int, @CveCampoLlave int, @EsAdministrador bit', @Orden = @OrdenRelacionMostrar, @NombreTablaSQL = @NombreTablaSQLRelacionMostrar, @NombreTabla = @NombreTablaRelacionMostrar, @NombreCampoSQL = @NombreCampoSQLRelacionMostrar, @NombreCampo = @NombreCampoRelacionMostrar, @CveTabla = @CveTablaRelacionMostrar, @CveCampoLlave = @CveCampoLlaveRelacionMostrar, @EsAdministrador = @EsAdministrador; FETCH NEXT FROM CursorRelacionesMostrar INTO @OrdenRelacionMostrar, @CveTablaRelacionMostrar, @NombreTablaSQLRelacionMostrar, @NombreTablaRelacionMostrar, @CveCampoLlaveRelacionMostrar, @NombreCampoLlaveRelacionMostrar, @NombreCampoSQLRelacionMostrar, @NombreCampoRelacionMostrar; END; CLOSE CursorRelacionesMostrar; DEALLOCATE CursorRelacionesMostrar; SELECT RV.CveBitacora, RV.Orden, RV.NombreTablaSQL, RV.NombreTabla, RV.NombreCampoSQL, RV.NombreCampo, RV.ValorClave, RV.ValorMostrar FROM #RelacionesVisuales AS RV INNER JOIN #BitacorasPagina AS P ON P.CveBitacora = RV.CveBitacora ORDER BY P.Orden, RV.Orden; SET @Return = 1; END TRY BEGIN CATCH SET @ErrorMostrar = 'Ocurrió un error al consultar la bitácora; favor de contactar a Sistemas.'; SET @ErrorTipo = 1; SET @Return = 0; DECLARE @ErrorTecnico nvarchar(2048) = CONCAT( 'Número: ', ERROR_NUMBER(), '; Procedimiento: ', COALESCE( ERROR_PROCEDURE(), 'PA_Bit_BitacoraListByRelations' ), '; Línea: ', ERROR_LINE(), '; Detalle: ', ERROR_MESSAGE() ); RAISERROR(N'%s', 10, 1, @ErrorTecnico); END CATCH; END; GO CREATE OR ALTER PROCEDURE dbo.PA_Bit_BitacoraDetalleGet @CveBitacora bigint, @CveUsuarioEjecuta int, @ErrorMostrar varchar(max) OUTPUT, @ErrorTipo int OUTPUT, @Return bit OUTPUT AS BEGIN SET NOCOUNT ON; SELECT @ErrorMostrar = NULL, @ErrorTipo = 0, @Return = 0; BEGIN TRY IF @CveBitacora <= 0 BEGIN SET @ErrorMostrar = 'La clave del movimiento de bitácora no es válida.'; SET @ErrorTipo = 2; RETURN; END; DECLARE @EsAdministrador bit = 0; DECLARE @EsDesarrollador bit = 0; SELECT @EsAdministrador = CASE WHEN R.NombreRol IN ( 'SuperUsuario', 'Administrador' ) THEN 1 ELSE 0 END, @EsDesarrollador = CASE WHEN R.NombreRol = 'Empresa' THEN 1 ELSE 0 END FROM dbo.Sis_Usuarios AS U INNER JOIN dbo.Sis_Roles AS R ON R.CveRol = U.CveRol WHERE U.CveUsuario = @CveUsuarioEjecuta AND U.Activo = 1 AND R.Activo = 1; IF NOT EXISTS ( SELECT 1 FROM ( SELECT R.CveBitacora, R.CveTabla, R.CveTipoMovimiento, COALESCE(R.LlaveRegistroTexto, CONVERT(nvarchar(250), R.LlaveRegistro)) AS LlaveRegistro, E.CveDefinicionAccion, E.CveUsuarioEjecuta, E.CveUsuarioAutoriza, E.FechaAutorizacion, E.FechaMovimiento, E.DireccionIP, E.NombreEquipo, E.Modulo, CASE WHEN @EsAdministrador = 1 OR NOT EXISTS ( SELECT 1 FROM dbo.Bit_Bitacora AS R2 WHERE R2.CveBitacoraEvento = E.CveBitacoraEvento AND NOT (( @EsAdministrador = 1 OR ( @EsDesarrollador = 1 AND EXISTS ( SELECT 1 FROM dbo.Bit_BitacoraRelaciones AS BRCliente INNER JOIN dbo.Bit_Tablas AS TCliente ON TCliente.CveTabla = BRCliente.CveTablaRelacionada INNER JOIN dbo.Bit_Campos AS CCliente ON CCliente.CveCampo = BRCliente.CveCampoRelacion AND CCliente.CveTabla = TCliente.CveTabla INNER JOIN dbo.Sis_UsuariosClientes AS UC ON UC.CveCliente = BRCliente.ValorRelacionNumero AND UC.CveUsuario = @CveUsuarioEjecuta AND UC.Activo = 1 WHERE BRCliente.CveBitacora = R2.CveBitacora AND BRCliente.VisibleDesarrollador = 1 AND TCliente.NombreTablaSQL = 'Clientes' AND CCliente.NombreCampoSQL = 'CveCliente' ) ) )) ) THEN E.DescripcionMovimiento ELSE N'Operación con registros de acceso restringido.' END AS DescripcionMovimiento FROM dbo.Bit_Bitacora AS R INNER JOIN dbo.Bit_BitacoraEventos AS E ON E.CveBitacoraEvento = R.CveBitacoraEvento ) AS B WHERE B.CveBitacora = @CveBitacora AND ( @EsAdministrador = 1 OR ( @EsDesarrollador = 1 AND EXISTS ( SELECT 1 FROM dbo.Bit_BitacoraRelaciones AS BRCliente INNER JOIN dbo.Bit_Tablas AS TCliente ON TCliente.CveTabla = BRCliente.CveTablaRelacionada INNER JOIN dbo.Bit_Campos AS CCliente ON CCliente.CveCampo = BRCliente.CveCampoRelacion AND CCliente.CveTabla = TCliente.CveTabla INNER JOIN dbo.Sis_UsuariosClientes AS UC ON UC.CveCliente = BRCliente.ValorRelacionNumero AND UC.CveUsuario = @CveUsuarioEjecuta AND UC.Activo = 1 WHERE BRCliente.CveBitacora = B.CveBitacora AND BRCliente.VisibleDesarrollador = 1 AND TCliente.NombreTablaSQL = 'Clientes' AND CCliente.NombreCampoSQL = 'CveCliente' ) ) ) ) BEGIN SET @ErrorMostrar = 'No se encontró el movimiento de bitácora solicitado.'; SET @ErrorTipo = 2; RETURN; END; SELECT B.CveBitacora, B.CveTabla, T.NombreTablaSQL, T.NombreMostrar AS NombreTabla, B.CveTipoMovimiento, M.Nombre AS TipoMovimiento, B.CveDefinicionAccion, A.NombreAccion, A.DescripcionAccion, B.CveUsuarioEjecuta, COALESCE( NULLIF(U.NombreUsuario, ''), CONCAT('(', B.CveUsuarioEjecuta, ') Eliminado') ) AS NombreUsuarioEjecuta, B.CveUsuarioAutoriza, B.FechaAutorizacion, B.LlaveRegistro, B.DescripcionMovimiento, B.FechaMovimiento, B.DireccionIP, B.NombreEquipo, B.Modulo FROM ( SELECT R.CveBitacora, R.CveTabla, R.CveTipoMovimiento, COALESCE(R.LlaveRegistroTexto, CONVERT(nvarchar(250), R.LlaveRegistro)) AS LlaveRegistro, E.CveDefinicionAccion, E.CveUsuarioEjecuta, E.CveUsuarioAutoriza, E.FechaAutorizacion, E.FechaMovimiento, E.DireccionIP, E.NombreEquipo, E.Modulo, CASE WHEN @EsAdministrador = 1 OR NOT EXISTS ( SELECT 1 FROM dbo.Bit_Bitacora AS R2 WHERE R2.CveBitacoraEvento = E.CveBitacoraEvento AND NOT (( @EsAdministrador = 1 OR ( @EsDesarrollador = 1 AND EXISTS ( SELECT 1 FROM dbo.Bit_BitacoraRelaciones AS BRCliente INNER JOIN dbo.Bit_Tablas AS TCliente ON TCliente.CveTabla = BRCliente.CveTablaRelacionada INNER JOIN dbo.Bit_Campos AS CCliente ON CCliente.CveCampo = BRCliente.CveCampoRelacion AND CCliente.CveTabla = TCliente.CveTabla INNER JOIN dbo.Sis_UsuariosClientes AS UC ON UC.CveCliente = BRCliente.ValorRelacionNumero AND UC.CveUsuario = @CveUsuarioEjecuta AND UC.Activo = 1 WHERE BRCliente.CveBitacora = R2.CveBitacora AND BRCliente.VisibleDesarrollador = 1 AND TCliente.NombreTablaSQL = 'Clientes' AND CCliente.NombreCampoSQL = 'CveCliente' ) ) )) ) THEN E.DescripcionMovimiento ELSE N'Operación con registros de acceso restringido.' END AS DescripcionMovimiento FROM dbo.Bit_Bitacora AS R INNER JOIN dbo.Bit_BitacoraEventos AS E ON E.CveBitacoraEvento = R.CveBitacoraEvento ) AS B INNER JOIN dbo.Bit_Tablas AS T ON T.CveTabla = B.CveTabla INNER JOIN dbo.Bit_TiposMovimiento AS M ON M.CveTipoMovimiento = B.CveTipoMovimiento LEFT JOIN dbo.Bit_DefinicionesAccion AS A ON A.CveDefinicionAccion = B.CveDefinicionAccion LEFT JOIN dbo.Sis_Usuarios AS U ON U.CveUsuario = B.CveUsuarioEjecuta WHERE B.CveBitacora = @CveBitacora; CREATE TABLE #RegistrosAutorizados (CveBitacora bigint NOT NULL PRIMARY KEY); INSERT INTO #RegistrosAutorizados VALUES (@CveBitacora); CREATE TABLE #CamposDetalle ( CveBitacoraCampo bigint NOT NULL PRIMARY KEY, CveBitacora bigint NOT NULL, CveCampo int NOT NULL, NombreCampoSQL varchar(128) COLLATE Modern_Spanish_CS_AS NOT NULL, NombreMostrar varchar(150) COLLATE Modern_Spanish_CS_AS NOT NULL, ValorAnterior nvarchar(max) COLLATE Modern_Spanish_CS_AS NULL, ValorNuevo nvarchar(max) COLLATE Modern_Spanish_CS_AS NULL, ValorAnteriorMostrar nvarchar(max) COLLATE Modern_Spanish_CS_AS NULL, ValorNuevoMostrar nvarchar(max) COLLATE Modern_Spanish_CS_AS NULL ); INSERT INTO #CamposDetalle ( CveBitacoraCampo, CveBitacora, CveCampo, NombreCampoSQL, NombreMostrar, ValorAnterior, ValorNuevo, ValorAnteriorMostrar, ValorNuevoMostrar ) SELECT BC.CveBitacoraCampo, BC.CveBitacora, BC.CveCampo, C.NombreCampoSQL, C.NombreMostrar, BC.ValorAnterior, BC.ValorNuevo, BC.ValorAnteriorMostrar, BC.ValorNuevoMostrar FROM dbo.Bit_BitacoraCampos AS BC INNER JOIN dbo.Bit_Campos AS C ON C.CveCampo = BC.CveCampo WHERE EXISTS (SELECT 1 FROM #RegistrosAutorizados AS V WHERE V.CveBitacora = BC.CveBitacora) AND ( @EsAdministrador = 1 OR C.MostrarEnBitacora = 1 ); DECLARE @CveCampoOrigenReferencia int; DECLARE @NombreTablaReferencia varchar(128); DECLARE @NombreCampoLlaveReferencia varchar(128); DECLARE @NombreCampoMostrarReferencia varchar(128); DECLARE @SqlCampoReferencia nvarchar(max); DECLARE CursorCamposReferencia CURSOR LOCAL FAST_FORWARD FOR SELECT DISTINCT CO.CveCampo, TR.NombreTablaSQL, CL.NombreCampoSQL, CM.NombreCampoSQL FROM #CamposDetalle AS CD INNER JOIN dbo.Bit_Campos AS CO ON CO.CveCampo = CD.CveCampo AND CO.Activo = 1 INNER JOIN dbo.Bit_Tablas AS TR ON TR.CveTabla = CO.CveTablaReferencia AND TR.Activo = 1 AND TR.NombreCampoLlaveSQL IS NOT NULL INNER JOIN dbo.Bit_Campos AS CL ON CL.CveTabla = TR.CveTabla AND CL.NombreCampoSQL = TR.NombreCampoLlaveSQL AND CL.Activo = 1 INNER JOIN dbo.Bit_Campos AS CM ON CM.CveCampo = CO.CveCampoMostrarReferencia AND CM.CveTabla = TR.CveTabla AND CM.MostrarEnBitacora = 1 AND CM.Activo = 1 INNER JOIN sys.schemas AS SC ON SC.name = 'dbo' INNER JOIN sys.tables AS ST ON ST.schema_id = SC.schema_id AND ST.name COLLATE Modern_Spanish_CS_AS = TR.NombreTablaSQL INNER JOIN sys.columns AS SCL ON SCL.object_id = ST.object_id AND SCL.name COLLATE Modern_Spanish_CS_AS = CL.NombreCampoSQL INNER JOIN sys.columns AS SCM ON SCM.object_id = ST.object_id AND SCM.name COLLATE Modern_Spanish_CS_AS = CM.NombreCampoSQL; OPEN CursorCamposReferencia; FETCH NEXT FROM CursorCamposReferencia INTO @CveCampoOrigenReferencia, @NombreTablaReferencia, @NombreCampoLlaveReferencia, @NombreCampoMostrarReferencia; WHILE @@FETCH_STATUS = 0 BEGIN SET @SqlCampoReferencia = N' UPDATE CD SET CD.ValorAnteriorMostrar = CASE WHEN CD.ValorAnteriorMostrar IS NOT NULL THEN CD.ValorAnteriorMostrar WHEN CD.ValorAnterior IS NULL THEN NULL WHEN VA.' + QUOTENAME(@NombreCampoLlaveReferencia) + N' IS NULL THEN CONCAT(N''('', CD.ValorAnterior, N'') Eliminada'') WHEN NULLIF(CONVERT(nvarchar(max), VA.' + QUOTENAME(@NombreCampoMostrarReferencia) + N'), N'''') IS NULL THEN CONCAT(N''('', CD.ValorAnterior, N'')'') ELSE CONCAT(CONVERT(nvarchar(max), VA.' + QUOTENAME(@NombreCampoMostrarReferencia) + N'), N'' (catálogo actual)'') END, CD.ValorNuevoMostrar = CASE WHEN CD.ValorNuevoMostrar IS NOT NULL THEN CD.ValorNuevoMostrar WHEN CD.ValorNuevo IS NULL THEN NULL WHEN VN.' + QUOTENAME(@NombreCampoLlaveReferencia) + N' IS NULL THEN CONCAT(N''('', CD.ValorNuevo, N'') Eliminada'') WHEN NULLIF(CONVERT(nvarchar(max), VN.' + QUOTENAME(@NombreCampoMostrarReferencia) + N'), N'''') IS NULL THEN CONCAT(N''('', CD.ValorNuevo, N'')'') ELSE CONCAT(CONVERT(nvarchar(max), VN.' + QUOTENAME(@NombreCampoMostrarReferencia) + N'), N'' (catálogo actual)'') END FROM #CamposDetalle AS CD LEFT JOIN dbo.' + QUOTENAME(@NombreTablaReferencia) + N' AS VA ON CONVERT(nvarchar(250), VA.' + QUOTENAME(@NombreCampoLlaveReferencia) + N') COLLATE Modern_Spanish_CS_AS = CD.ValorAnterior COLLATE Modern_Spanish_CS_AS LEFT JOIN dbo.' + QUOTENAME(@NombreTablaReferencia) + N' AS VN ON CONVERT(nvarchar(250), VN.' + QUOTENAME(@NombreCampoLlaveReferencia) + N') COLLATE Modern_Spanish_CS_AS = CD.ValorNuevo COLLATE Modern_Spanish_CS_AS WHERE CD.CveCampo = @CveCampoOrigen;'; EXEC sys.sp_executesql @SqlCampoReferencia, N'@CveCampoOrigen int', @CveCampoOrigen = @CveCampoOrigenReferencia; FETCH NEXT FROM CursorCamposReferencia INTO @CveCampoOrigenReferencia, @NombreTablaReferencia, @NombreCampoLlaveReferencia, @NombreCampoMostrarReferencia; END; CLOSE CursorCamposReferencia; DEALLOCATE CursorCamposReferencia; UPDATE D SET ValorAnteriorMostrar = COALESCE(D.ValorAnteriorMostrar, CASE D.ValorAnterior WHEN N'1' THEN N'Lunes' WHEN N'2' THEN N'Martes' WHEN N'3' THEN N'Miércoles' WHEN N'4' THEN N'Jueves' WHEN N'5' THEN N'Viernes' WHEN N'6' THEN N'Sábado' WHEN N'7' THEN N'Domingo' ELSE D.ValorAnterior END), ValorNuevoMostrar = COALESCE(D.ValorNuevoMostrar, CASE D.ValorNuevo WHEN N'1' THEN N'Lunes' WHEN N'2' THEN N'Martes' WHEN N'3' THEN N'Miércoles' WHEN N'4' THEN N'Jueves' WHEN N'5' THEN N'Viernes' WHEN N'6' THEN N'Sábado' WHEN N'7' THEN N'Domingo' ELSE D.ValorNuevo END) FROM #CamposDetalle AS D INNER JOIN dbo.Bit_Campos AS C ON C.CveCampo = D.CveCampo INNER JOIN dbo.Bit_Tablas AS T ON T.CveTabla = C.CveTabla WHERE T.NombreTablaSQL = 'Sis_TurnosHorarios' AND C.NombreCampoSQL = 'DiaSemana'; SELECT CveBitacoraCampo, CveBitacora, CveCampo, NombreCampoSQL, NombreMostrar, ValorAnterior, ValorNuevo, COALESCE(ValorAnteriorMostrar, ValorAnterior) AS ValorAnteriorMostrar, COALESCE(ValorNuevoMostrar, ValorNuevo) AS ValorNuevoMostrar FROM #CamposDetalle ORDER BY CveBitacoraCampo; SET @Return = 1; END TRY BEGIN CATCH SET @ErrorMostrar = 'Ocurrió un error al consultar el detalle de la bitácora; favor de contactar a Sistemas.'; SET @ErrorTipo = 1; SET @Return = 0; DECLARE @ErrorTecnico nvarchar(2048) = CONCAT( 'Número: ', ERROR_NUMBER(), '; Procedimiento: ', COALESCE( ERROR_PROCEDURE(), 'PA_Bit_BitacoraDetalleGet' ), '; Línea: ', ERROR_LINE(), '; Detalle: ', ERROR_MESSAGE() ); RAISERROR(N'%s', 10, 1, @ErrorTecnico); END CATCH; END; GO