# Reglas obligatorias de procedimientos almacenados Estas reglas complementan `AGENTS.md` y son obligatorias para toda consulta nueva o modificada en la aplicación. ## Acceso a datos - Todas las lecturas y escrituras de la aplicación deben usar procedimientos almacenados. - No escribir `SELECT`, `INSERT`, `UPDATE`, `DELETE` ni `MERGE` directamente en C# dentro de repositorios, servicios, páginas, controles o la master page. - Los repositorios deben usar `CommandType.StoredProcedure`. - Todos los valores proporcionados por la aplicación deben enviarse como parámetros. - Nunca concatenar entrada del usuario en SQL. ## Nombres - Usar el formato `PA__`. - `PA` significa procedimiento almacenado. - `` identifica el grupo funcional: `Sis`, `Tra`, `Vta`, `Conta`, `Clientes`, `Fraccionamientos`, etcétera. - `` identifica el objeto y `` describe claramente la operación; por ejemplo `Add`, `Update`, `List`, `Activate`, `Deactivate` o `Delete`. - Ejemplos: `PA_Sis_UsuarioAdd`, `PA_Sis_UsuarioUpdate`, `PA_Sis_UsuarioList`, `PA_Sis_UsuarioActivate`, `PA_Sis_UsuarioDeactivate` y `PA_Sis_UsuarioDelete`. ## Parámetros y resultado obligatorios Todos los procedimientos deben declarar: ```sql @CveUsuarioEjecuta int, @ErrorMostrar varchar(max) OUTPUT, @ErrorTipo int OUTPUT, @Return bit OUTPUT ``` - `@CveUsuarioEjecuta` identifica al usuario autenticado que ejecuta la operación. - Al inicio, establecer `@ErrorMostrar = NULL`, `@ErrorTipo = 0` y `@Return = 0`. - Establecer `@Return = 1` únicamente cuando la operación termine correctamente. - `@ErrorTipo = 0`: sin error ni advertencia. - `@ErrorTipo = 1`: error. - `@ErrorTipo = 2`: advertencia. - `@ErrorMostrar` contiene el mensaje amigable en español que puede mostrarse al usuario. - No devolver contraseñas, hashes, cadenas de conexión, SQL, stack traces ni detalles internos mediante `@ErrorMostrar`. - Los procedimientos de consulta pueden devolver un result set, pero también deben poblar los parámetros comunes. - El repositorio debe convertir los parámetros de salida en un modelo de resultado como `DbResult`. ## Control de errores - Usar `SET NOCOUNT ON` y `TRY/CATCH` en todos los procedimientos. - Usar `SET XACT_ABORT ON` cuando exista una mutación o transacción. - Iniciar la transacción dentro de `TRY`, confirmar sólo después del éxito y ejecutar rollback en `CATCH` cuando `XACT_STATE() <> 0`. - Una validación fallida debe establecer los parámetros de salida y ejecutar `RETURN`; no debe continuar la mutación. - Para validar existencia, usar normalmente `EXISTS` en lugar de `COUNT(*)`. - En `CATCH`, generar un mensaje técnico separado con `ERROR_NUMBER()`, `ERROR_PROCEDURE()`, `ERROR_LINE()` y `ERROR_MESSAGE()` para el log. - El mensaje técnico puede notificarse con severidad informativa para que el ejecutor lo registre, pero nunca debe mostrarse directamente al usuario. ## Borrado lógico - Está prohibido usar `DELETE` físico en procedimientos CRUD normales. - Para borrar: `Borrado = 1` y `FechaBorrado = SYSDATETIME()`. - Para restaurar: `Borrado = 0` y `FechaBorrado = NULL`. - Las consultas normales deben filtrar `Borrado = 0` en cada tabla participante, excepto cuando el caso de uso solicite expresamente registros borrados. - La eliminación permanente sólo puede realizarse mediante el procedimiento controlado de retención y limpieza. - Ninguna página ASPX puede llamar directamente a la limpieza física. ## Plantilla requerida ```sql CREATE OR ALTER PROCEDURE [dbo].[PA_Sis_RolAdd] @Rol varchar(50), @CveUsuarioEjecuta int, @ErrorMostrar varchar(max) OUTPUT, @ErrorTipo int OUTPUT, @Return bit OUTPUT AS BEGIN SET NOCOUNT ON; SET XACT_ABORT ON; SELECT @ErrorMostrar = NULL, @ErrorTipo = 0, @Return = 0; BEGIN TRY IF EXISTS ( SELECT 1 FROM dbo.Sis_Roles WHERE NombreRol = @Rol AND Borrado = 0 ) BEGIN SET @ErrorMostrar = CONCAT('El rol ''', @Rol, ''' ya se encuentra registrado.'); SET @ErrorTipo = 2; RETURN; END; INSERT INTO dbo.Sis_Roles (NombreRol, Activo, FechaCreacion) VALUES (@Rol, 1, SYSDATETIME()); SET @Return = 1; END TRY BEGIN CATCH IF XACT_STATE() <> 0 ROLLBACK TRANSACTION; SET @ErrorMostrar = 'Ocurrió un error al agregar el rol; 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_Sis_RolAdd'), '; Línea: ', ERROR_LINE(), '; Detalle: ', ERROR_MESSAGE() ); RAISERROR(N'%s', 10, 1, @ErrorTecnico); END CATCH; END; GO ``` Cuando el procedimiento use una transacción, debe incluir `BEGIN TRANSACTION` dentro de `TRY` y `COMMIT TRANSACTION` antes de establecer `@Return = 1`.