Entrada 13: Refactor

Bitácora de Sesión

Fecha: 11/06/2026

Inicio: [13:30] | Fin: [16:30] || Total: [3 horas y 30 min]

Presente: Matías Benavides Sandoval

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

¿QUÉ HICIMOS HOY?

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Se realizó una limpieza arquitectónica mayor del backend. El objetivo fue eliminar toda SQL inline restante (queries directas a la BD) y migrarlas a stored procedures dedicados, y eliminar código muerto acumulado de sesiones anteriores.

Se crearon 4 SPs nuevos: sp_GetUsuarioId (resuelve id desde Username), sp_GetPuestos (catálogo de puestos), sp_GetTiposEvento (catálogo de tipos de evento), sp_GetLastDbError (obtiene el último error de DBError para un usuario). Todos deployados en PlanillaDB.

Se refactorizó empleadoController.ts: de ~584 líneas a ~200 líneas. Se extrajeron puestoController.ts (getPuestos vía sp_GetPuestos), tiposMovimientoController.ts (getTiposMovimiento vía sp_GetTiposMovimiento) y usuarioHelper.ts (resolveUsuarioId vía sp_GetUsuarioId). Se creó utils.ts en frontend con funciones compartidas (setEstado, formatearFecha, formatearFechaHora, logout).

Se eliminaron 15 archivos muertos: 5 SPs de Tarea2-BD (VacacionesDB), impersonarController.ts (duplicado), movimientoController.ts, routes/impersonar.ts, routes/movimientos.ts, models/Auth.ts, public/js/empleado.js, movimientos.html, insertarMovimiento.html, src/frontend/movimientos.ts, src/frontend/insertarMovimiento.ts.

Se limpió errorhelper.ts: la función getErrorMessage ahora llama a sp_GetError en vez de hacer SQL inline. Se eliminó la función resolveUsuarioId (ya vivía en usuarioHelper.ts).

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

PROBLEMAS DETECTADOS Y CÓMO SE RESOLVIERON

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Problema: errorhelper.ts tenía SQL inline para resolver mensajes de error y el último DBError.

Causa: era código legacy de Tarea2-BD que nunca se migró a SPs.

Solución: se reescribió para llamar a sp_GetError y sp_GetLastDbError.

Problema: imports de utils.ts en frontend fallaban con "Failed to resolve module specifier".

Causa: los imports usaban './utils' sin extensión .js, pero browsers con ES modules requieren la extensión explícita.

Solución: se cambiaron todos los imports a './utils.js'.

Problema: frontend y backend compilaban con warnings de archivos eliminados.

Causa: tsconfig.json incluía src/frontend/** en la compilación backend, y tsconfigFronted.json no excluía archivos muertos.

Solución: se actualizó tsconfig.json para excluir src/frontend/** y se limpió dist/frontend/.

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

AVANCE DEL CÓDIGO

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

USE [PlanillaDB];
GO

IF OBJECT_ID(N'dbo.sp_GetBitacora', N'P') IS NOT NULL
    DROP PROCEDURE [dbo].[sp_GetBitacora];
GO

CREATE PROCEDURE [dbo].[sp_GetBitacora]
    @inIdTipoEvento INT = NULL,
    @inIdUsuario INT = NULL,
    @inFechaDesde DATETIME = NULL,
    @inFechaHasta DATETIME = NULL,
    @inIpPostIn VARCHAR(64) = NULL,
    @inPageSize INT = 50,
    @inPageNumber INT = 1,
    @outResultCode INT OUTPUT
AS
BEGIN
    SET NOCOUNT ON;
    SET @outResultCode = 0;

    BEGIN TRY
        DECLARE @offset INT;

        IF @inPageNumber < 1 SET @inPageNumber = 1;
        IF @inPageSize < 1 SET @inPageSize = 50;

        SET @offset = (@inPageNumber - 1) * @inPageSize;

        SELECT
            b.id,
            b.idTipoEvento,
            te.Nombre AS TipoEvento,
            b.idUsuario,
            u.Username,
            b.Descripcion,
            b.PostTime,
            b.IpPostIn
        FROM dbo.BitacoraEvento b
        INNER JOIN dbo.TipoEvento te ON b.idTipoEvento = te.id
        LEFT JOIN dbo.Usuario u ON b.idUsuario = u.id
        WHERE (@inIdTipoEvento IS NULL OR b.idTipoEvento = @inIdTipoEvento)
          AND (@inIdUsuario IS NULL OR b.idUsuario = @inIdUsuario)
          AND (@inFechaDesde IS NULL OR b.PostTime >= @inFechaDesde)
          AND (@inFechaHasta IS NULL OR b.PostTime <= @inFechaHasta)
          AND (@inIpPostIn IS NULL OR b.IpPostIn LIKE '%' + @inIpPostIn + '%')
        ORDER BY b.PostTime DESC
        OFFSET @offset ROWS
        FETCH NEXT @inPageSize ROWS ONLY;

        SELECT COUNT(*) AS Total
        FROM dbo.BitacoraEvento b
        WHERE (@inIdTipoEvento IS NULL OR b.idTipoEvento = @inIdTipoEvento)
          AND (@inIdUsuario IS NULL OR b.idUsuario = @inIdUsuario)
          AND (@inFechaDesde IS NULL OR b.PostTime >= @inFechaDesde)
          AND (@inFechaHasta IS NULL OR b.PostTime <= @inFechaHasta)
          AND (@inIpPostIn IS NULL OR b.IpPostIn LIKE '%' + @inIpPostIn + '%');

    END TRY
    BEGIN CATCH
        INSERT INTO dbo.DBError (UserName, Number, State, Severity, Line, [Procedure], Message, DateTime)
        VALUES (
            SYSTEM_USER,
            ERROR_NUMBER(),
            CAST(ERROR_STATE() AS VARCHAR(32)),
            CAST(ERROR_SEVERITY() AS VARCHAR(32)),
            ERROR_LINE(),
            ISNULL(ERROR_PROCEDURE(), 'sp_GetBitacora'),
            ERROR_MESSAGE(),
            GETDATE()
        );
        SET @outResultCode = 50008;
    END CATCH
END;
GO


USE [PlanillaDB];
GO

IF OBJECT_ID(N'dbo.sp_GetPuestos', N'P') IS NOT NULL
    DROP PROCEDURE [dbo].[sp_GetPuestos];
GO

CREATE PROCEDURE [dbo].[sp_GetPuestos]
AS
BEGIN
    SET NOCOUNT ON;

    SELECT id, Nombre
    FROM dbo.Puesto
    ORDER BY Nombre ASC;
END;
GO


USE [PlanillaDB];
GO

IF OBJECT_ID(N'dbo.sp_GetTiposEvento', N'P') IS NOT NULL
    DROP PROCEDURE [dbo].[sp_GetTiposEvento];
GO

CREATE PROCEDURE [dbo].[sp_GetTiposEvento]
AS
BEGIN
    SET NOCOUNT ON;

    SELECT id, Nombre
    FROM dbo.TipoEvento
    ORDER BY id ASC;
END;
GO


USE [PlanillaDB];
GO

IF OBJECT_ID(N'dbo.sp_GetUsuarioId', N'P') IS NOT NULL
    DROP PROCEDURE [dbo].[sp_GetUsuarioId];
GO

CREATE PROCEDURE [dbo].[sp_GetUsuarioId]
    @inUsername VARCHAR(128)
AS
BEGIN
    SET NOCOUNT ON;

    SELECT id
    FROM dbo.Usuario
    WHERE Username = @inUsername;
END;
GO

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

MORALEJAS / BUENAS PRÁCTICAS

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Toda consulta a la BD debe ir por SP, nunca SQL inline en controllers. Esto permite cambiar la lógica de BD sin tocar el backend, y mantiene la capa de datos separada.

Los imports de frontend con ES modules requieren extensión .js explícita. Sin ella, el browser falla silenciosamente con "Failed to resolve module specifier".

Antes de eliminar un archivo, verificar que ningún otro archivo lo importe. 

Mantener utility functions compartidas (fechas, estado) en un archivo utils.ts evita duplicación entre módulos del frontend.

El backend carga código de dist/, no de src/. Después de cualquier cambio en src/, recompilar con npx tsc y reiniciar el servidor.

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

PRÓXIMA SESIÓN: ¿QUÉ SIGUE?

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  • Persona B (Sebastián): implementar sp_ProcesarAsistencia, sp_ProcesarPlanillaSemanal, sp_ProcesarPlanillaMensual, sp_CrearCalendario, sp_GetPlanillaSemanal, sp_GetPlanillaMensual, sp_CargarCatalogosXML.
  • Conectar SPs de planilla a empleado-view.html/ts.
  • Verificar que empleados, impersonar, empleado-view y bitácora sigan funcionando tras la limpieza.
  • Posibles mejoras: índices en tablas consultadas frecuentemente (BitacoraEvento, Empleado).

Comentarios