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
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():
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:
- 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.
- Validación de nombre: Se verifica que el nombre contiene
solo letras y espacios.
- Validación de cédula: Se verifica que la cédula contiene
solo dígitos usando cedula.isdigit().
- 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).
- 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:
Parte 2: Visualización de movimientos
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:
- Datos del empleado (1 fila con 3 columnas: cédula, nombre, saldo actual)
- Lista de movimientos (N filas con 7 columnas: fecha, tipo movimiento, monto, nuevo saldo, usuario, IP, timestamp)
- 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
Flujo:
- Verifica
sesión activa.
- Obtiene
el id_empleado desde los query parameters (viene del botón
"Movimientos" en index.html).
- Valida que exista un id_empleado
válido, si no, redirige al listado.
- Obtiene id del usuario y IP.
- Llama
a listar_movimientos() del modelo.
- Si hay error, obtiene el mensaje y lo
muestra.
- 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
- pyodbc Documentation — nextset(): https://github.com/mkleehammer/pyodbc/wiki
- pyodbc Documentation — cursor.description: https://github.com/mkleehammer/pyodbc/wiki
- Jinja2 Documentation — for loops: https://jinja.palletsprojects.com/en/3.0.x/templates/#for
- SQL Server — DBCC CHECKIDENT: https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkident-transact-sql
- Tarea 1
- Connecting to and querying SQL Server with Python
Comentarios
Publicar un comentario