Implementación de inserción de empleados y visualización de movidimeintos

 Fecha: 23 de abril 2026

Hora de inicio: 9:00 a.m. 

Hora de finalización: 1:40 p.m.

Horas trabajadas: 4 h 30 min

Después de implementar el listado y filtrado de empleados, los siguientes pasos eran permitir que los usuarios insertaran nuevos empleados y consultaran el historial de movimientos de vacaciones de cada empleado. Estas funcionalidades cerraban mi parte en el proyecto de gestión de empleados, estableciendo patrones que serían reutilizables para otras operaciones CRUD. 

Actividades realizadas

9:00 a.m. - 12:00 m.d.

Parte 1: Inserción de empleados

Implementación de modelo_empleado.py — funciones nuevas

Se agregaron dos funciones al módulo modelo_empleado.py: listar_puestos() e insertar_empleado().

Función listar_puestos():

                                                Img 1: listar_puestos()

Es un método sencillo que ejecuta el SP ListarPuestos que devuelve 2 resulsets: la lista de puestos y luego el código del resultado. El patrón es igual al de listar_empleados, con 2 resultset en orddn fijo, que se pueden navegar con cursor.nextset().

Función insertar_empleado():

    Img 2: Fragmento de insertar_empleado()

Esta función ejecuta el SP dbo.InsertarEmpleado pasando los datos del empleado a insertar. A diferencia de listar_empleados() que devuelve datos, este SP solo devuelve el código de resultado, pero complica la lectura porque el SP llama internamente a RegistrarBitacora, que devuelve su propio resultset.

Por eso se usa el patrón while True con cursor.description, itera sobre todos los resultsets hasta encontrar el que contiene la columna resultCode. Una vez encontrado, extrae el valor y rompe el ciclo.

Después de leer el resultado, se ejecuta conn.commit() explícitamente. Este es un detalle crítico de Azure SQL Database — a diferencia de SQL Server Express local, Azure requiere que el cliente envíe un commit manual después de operaciones de escritura (INSERT/UPDATE/DELETE).


Implementación de controlador_empleado.py /insertar_empleado:

    Img 3: Frgamente de insertar_empleado_view()


Lógica GET:Cuando el usuario accede a /insertar_empleado, se llama a listar_puestos() para obtener la lista de puestos que se mostrarán en el dropdown. Esta lista se pasa al template como puestos=puestos. El template itera sobre la lista con Jinja2 para generar las opciones del dropdown.

Lógica POST:

Cuando el usuario envía el formulario:

  1. Extracción de datos: Se obtienen nombre, cédula e id del puesto del formulario. Se usa .strip() para eliminar espacios al inicio y final.
  2. Validación de nombre: Se verifica que el nombre contiene solo letras y espacios.
  3. Validación de cédula: Se verifica que la cédula contiene solo dígitos usando cedula.isdigit().
  4. Validaciones de negocio en el SP: El SP InsertarEmpleado hace validaciones adicionales, verifica que no exista otro empleado con la misma cédula (error 50004) o nombre (error 50005).
  5. Manejo de errores: Si la inserción fue exitosa (result_code == 0), se muestra un mensaje de éxito y se redirige al listado de empleados. Si falló, se obtiene el mensaje de error de la BD y se muestra en flash, redirigiendo de nuevo al formulario.



Implementación de insertar_empleado.html:

El template HTML contiene:

  • Navbar: Igual a index.html, muestra usuario actual y link a logout.
  • Mensajes flash: Muestra errores o mensajes de éxito.
  • Formulario con tres campos:

-Nombre (input text)

-Cédula/Identificación (input text)

-Puesto (dropdown llenado desde puestos)

  • Dropdown de puestos: Itera sobre la lista de puestos con Jinja2. El value es el id (que viaja oculto), pero el usuario ve el nombre:
                                  Img 4: For de puestos



Parte 2: Visualización de movimientos

Implementación de movimientos.html:


    Img 5: movimientos.html

Implementación de modelo_movimiento.py función listar_movimientos():


    Img 6: listar_movimientos()

Esta función es más compleja que las anteriores porque el SP devuelve tres resultsets en orden fijo:

  1. Datos del empleado (1 fila con 3 columnas: cédula, nombre, saldo actual)
  2. Lista de movimientos (N filas con 7 columnas: fecha, tipo movimiento, monto, nuevo saldo, usuario, IP, timestamp)
  3. Código de resultado (1 fila con 1 columna)

La navegación es explícita:

  • empleado = cursor.fetchone() lee la primera fila del primer resultset
  • cursor.nextset() avanza al segundo resultset
  • movimientos = cursor.fetchall() lee todas las filas del segundo resultset
  • cursor.nextset() avanza al tercer resultset
  • result_code = cursor.fetchone()[0] lee el código de resultado


Implementación de controlador_movimiento.py ruta/movimientos:
    Img 7: listar_movimientos

Flujo:

  1. Verifica sesión activa.
  2. Obtiene el id_empleado desde los query parameters (viene del botón "Movimientos" en index.html).
  3. Valida que exista un id_empleado válido, si no, redirige al listado.
  4. Obtiene id del usuario y IP.
  5. Llama a listar_movimientos() del modelo.
  6. Si hay error, obtiene el mensaje y lo muestra.
  7. Renderiza movimientos.html pasando datos del empleado, lista de movimientos, e id del empleado (para el botón de insertar movimiento).

Errores encontrados

Error 1 Inserción exitosa en el SP pero datos no aparecen en la BD

El SP devolvía resultCode = 0 (éxito), pero al consultar la tabla Empleado en SSMS, el registro no estaba.

Causa: Faltaba conn.commit() después de leer el resultado del SP. En Azure SQL Database, las operaciones de escritura (INSERT/UPDATE/DELETE) requieren un COMMIT explícito desde el cliente. Sin él, la transacción queda "abierta" y nunca se persiste.

Esto fue sorpresivo porque en SQL Server Express local, a veces el autocommit está habilitado por defecto. Azure SQL es más estricto.

Solución: Agregar conn.commit() en insertar_empleado() después de leer el result_code:

                                  Img 8: Fragmento de insertar_empleado

Error 2 FK constraint violation al insertar empleado

El SP fallaba con un error de constraint cuando se intentaba insertar un empleado, diciendo que el idPuesto no existía en la tabla Puesto.

Causa: Después de limpiar y recargar la BD varias veces durante debugging, los ids de la tabla Puesto no empezaban en 1. La tabla tenía ids 2, 3, 4... porque la identidad (auto-increment) no fue reseteada. Cuando el dropdown mostraba "Puesto X" con id=1, ese id no existía en la BD.

Solución: Agregar DBCC CHECKIDENT ('dbo.Puesto', RESEED, 0) al script de limpieza de la BD. Este comando resetea la identidad de la tabla a 0, de modo que el próximo insert asignará id=1.

Error 3 Llamada incorrecta a SP con prefijo

El dropdown de puestos aparecía vacío.

Causa: En modelo_empleado.py , la línea era:

                                            Img 9: Fragmento de listar_puestos()

Solución: Cambiar a:

                                            Img 10: Fragmento corregido en listar_puestos()


Fue un error muy simple pero que me llevó un tiempo corregir.

Forma de trabajo del equipo

JJ implementó todas las funciones de inserción y visualización de movimientos. El tiempo más largo se gastó en debugging de los 4 errores, especialmente el Error 1 (missing commit) que fue sutil porque el SP devolvía éxito pero los datos no se persistían. Una vez identificado, se documentó para aplicar a todas las funciones de escritura. La comunicación se dio mediante WhatsApp.

Buenas prácticas descubiertas

  • Azure SQL requiere conn.commit() explícito después de escrituras. Esto es diferente al comportamiento de SQL Server local. Documentar esta diferencia evita horas de debugging.
  • El patrón while True con cursor.description es robusto para múltiples resultsets. No asume un número fijo de resultsets — es tolerante a cambios en la lógica de bitácora.
  • Validar nombre con replace(' ', '').isalpha() es simple y directo. Permite espacios (como "Juan Carlos") pero rechaza números y caracteres especiales.
  • Los dropdowns siempre deben poblarse desde BD, nunca hardcodeados. Los datos cambian, y el HTML no debería estar fuera de sincronización.
  • Los ids nunca deben mostrarse al usuario. Aunque el id del empleado pase por URL, se obtiene silenciosamente sin que el usuario lo vea o manipule.


Referencias consultadas

Comentarios

Entradas más populares de este blog

Creación del SP de Cargar Datos

Implementación de funcionalidades: Editar Empleado y Eliminar Empleado

Implementación de funcionalidad: Consultar Empleado