Compare commits

..

188 Commits

Author SHA1 Message Date
54ff167124 improve tables layout
All checks were successful
Builder / Build-Project (push) Successful in 2m44s
2024-11-17 17:15:58 -05:00
2a561a925c change list page size
All checks were successful
Builder / Build-Project (push) Successful in 2m45s
2024-11-17 16:37:57 -05:00
0695223c31 fix file upload
All checks were successful
Builder / Build-Project (push) Successful in 2m43s
2024-11-17 16:15:08 -05:00
4de84d7320 fix packages.json
All checks were successful
Builder / Build-Project (push) Successful in 2m47s
2024-11-15 11:42:46 -05:00
1857dcb5a9 Merge pull request 'registro-horas' (#76) from registro-horas into main
Some checks failed
Builder / Build-Project (push) Has been cancelled
Reviewed-on: #76
Reviewed-by: alex <alex@primefactorsolutions.com>
2024-11-15 16:40:49 +00:00
Melina Gutierrez
7b4953a3aa Añadir correcion de service y repository
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m44s
2024-11-15 12:34:43 -04:00
Melina Gutierrez
03146b1b9b Añadir correcion de formato
Some checks failed
PR Builder / Build-PR (pull_request) Failing after 2m40s
2024-11-15 12:26:47 -04:00
Melina Gutierrez
e8612aa5df Añadir correcion a Employee repository y service
Some checks failed
PR Builder / Build-PR (pull_request) Failing after 24s
2024-11-15 12:18:44 -04:00
Melina Gutierrez
f46d2cac03 Añadir correcion a Employee 2024-11-15 12:08:06 -04:00
Melina Gutierrez
886a19f2cf Añadir correcion a Employee 2024-11-15 11:54:52 -04:00
Melina Gutierrez
c58b37bbaf Formulario de registro de horas trabajadas mas el formulario 2024-11-15 11:51:32 -04:00
50fd791449 add version in login page
All checks were successful
Builder / Build-Project (push) Successful in 2m47s
2024-11-14 23:15:17 -05:00
b9320a3d6a adding git version in build
All checks were successful
Builder / Build-Project (push) Successful in 2m50s
2024-11-14 22:47:11 -05:00
439eab3541 Merge pull request 'correcciones' (#73) from En-desarrollo into main
All checks were successful
Builder / Build-Project (push) Successful in 2m46s
Reviewed-on: #73
Reviewed-by: alex <alex@primefactorsolutions.com>
2024-11-15 02:52:45 +00:00
005f0235fa correcciones vacacion gestion anterior
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m42s
2024-11-14 22:42:42 -04:00
305a5c53f3 correcciones
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m38s
2024-11-14 22:31:29 -04:00
97b9e515bf correcciones
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m38s
2024-11-14 22:19:43 -04:00
1c93e9c20c Merge pull request 'solicitud vacaciones priorizar primero de gestion anterior' (#72) from En-desarrollo into main
All checks were successful
Builder / Build-Project (push) Successful in 2m44s
Reviewed-on: #72
Reviewed-by: alex <alex@primefactorsolutions.com>
2024-11-15 01:15:32 +00:00
ddefc54e9e solicitud vacaciones priorizar primero de gestion anterior
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m38s
2024-11-14 20:58:17 -04:00
bdfaa7da85 Merge pull request 'En-desarrollo' (#71) from En-desarrollo into main
All checks were successful
Builder / Build-Project (push) Successful in 2m45s
Reviewed-on: #71
2024-11-15 00:24:15 +00:00
5ba432079b Resolver errores de checkstyle
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m36s
2024-11-14 04:15:29 -04:00
1c638ff27b Lista de Solicitudes de empleado - actualizar resumen
Some checks failed
PR Builder / Build-PR (pull_request) Failing after 24s
2024-11-14 04:09:26 -04:00
a3e94040ff correcciones
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m38s
2024-11-14 03:17:26 -04:00
b1e95cf8a5 Resolver errores de checkstyle
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m36s
2024-11-13 19:58:33 -04:00
8e1f0fdd7d Lista de Solicitudes de empleado - actualizar solicitudes permisos de salud
Some checks failed
PR Builder / Build-PR (pull_request) Failing after 22s
2024-11-13 19:54:36 -04:00
9579b33acc Lista de Solicitudes de empleado - Actualizar resumen de empleado 2024-11-13 18:34:42 -04:00
efca245c1d Corregir datos de prueba
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m40s
2024-11-13 14:47:28 -04:00
ca5fb122fb Lista de Solicitudes de empleado - Actualizar estado
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m40s
2024-11-13 14:10:09 -04:00
6d60fd00cc Lista de Solicitudes de empleado - Refactorizar 2024-11-13 12:38:25 -04:00
51a1d22be9 Lista de Solicitudes de empleado - Calcular total disponibilidad de dias 2024-11-12 21:54:28 -04:00
3b0e5833f8 Merge pull request '#45-Registro Semanal de Horas Trabajadas' (#69) from hoursworked into main
All checks were successful
Builder / Build-Project (push) Successful in 2m47s
Reviewed-on: #69
2024-11-11 19:21:21 +00:00
Melina Gutierrez
21d1313b69 #45-Registro Semanal de Horas Trabajadas corregir employee solucionar error
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m45s
2024-11-11 15:00:45 -04:00
Melina Gutierrez
249a8cb1ee #45-Registro Semanal de Horas Trabajadas corregir employee
Some checks failed
PR Builder / Build-PR (pull_request) Failing after 2m47s
2024-11-09 13:03:06 -04:00
Melina Gutierrez
b58a833444 Merge remote-tracking branch 'origin/hoursworked' into hoursworked
Some checks failed
PR Builder / Build-PR (pull_request) Failing after 21s
# Conflicts:
#	src/main/java/com/primefactorsolutions/model/Actividad.java
#	src/main/java/com/primefactorsolutions/service/HoursWorkedService.java
#	src/main/java/com/primefactorsolutions/views/EmployeeView.java
#	src/main/java/com/primefactorsolutions/views/HoursWorkedView.java
#	src/main/java/com/primefactorsolutions/views/ReporteView.java
2024-11-09 12:58:42 -04:00
Melina Gutierrez
ca2cd583a0 #45-Registro Semanal de Horas Trabajadas mas Reporte Excel 2024-11-09 12:43:31 -04:00
Melina Gutierrez
36287611b8 hoursworked sin reporte vista 2024-11-09 11:43:42 -04:00
Melina Gutierrez
5a0aa27382 primer commit 2024-11-09 11:42:54 -04:00
1ab10e286e Merge pull request 'En-desarrollo' (#65) from En-desarrollo into main
All checks were successful
Builder / Build-Project (push) Successful in 2m37s
Reviewed-on: #65
2024-11-09 12:15:26 +00:00
Melina Gutierrez
40d0cef2a1 hoursworked sin reporte vista 2024-11-08 08:36:22 -04:00
3922220439 Resolver errores de checkstyle
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m33s
2024-11-07 18:53:47 -04:00
6024b3fdd0 Resolved merge conflict in RequestsListView.java
Some checks failed
PR Builder / Build-PR (pull_request) Failing after 17s
2024-11-07 18:45:01 -04:00
5e37cbbcd0 Perfil de Personal Administrativo - Listado Vacaciones de empleado (Calcular automadicamente dias de las vacaciones gestion actual y anterior) 2024-11-07 18:40:34 -04:00
f7e7dea7cb #37 Perfil de Personal Administrativo - Listado General de Vacaciones (Calcular automadicamente dias de las vacaciones gestion actual y anterior)
Some checks failed
PR Builder / Build-PR (pull_request) Failing after 21s
2024-11-07 18:31:05 -04:00
cb34868b9c Descarte funcionalidad generar automaticamente solicitud vacacion
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m42s
2024-11-07 15:42:42 -04:00
d3fdb128b7 Merge branch 'En-desarrollo' of https://git.primefactorsolutions.com/PFS/pfs-intra into En-desarrollo
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m37s
2024-11-07 02:18:18 -04:00
91aad85146 #5, #6 Perfil de Empleado - GUARDAR y EDITAR Información de Empleados (Refactorizacion generar solicitudes vacaciones) 2024-11-07 02:17:27 -04:00
9a29f91994 Merge pull request '#5, #6 Perfil de Empleado - GUARDAR y EDITAR Información de Empleados (Asignar o actualizar vacaciones)' (#68) from Vacaciones into En-desarrollo
Some checks failed
PR Builder / Build-PR (pull_request) Failing after 19s
Reviewed-on: #68
2024-11-06 18:56:16 +00:00
ed88936683 Resolviendo conflictos y finalizando fusión entre ramas Vacaciones y En-desarrollo
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 0s
2024-11-06 14:55:53 -04:00
a0bd00fba4 #5, #6 Perfil de Empleado - GUARDAR y EDITAR Información de Empleados (Asignar o actualizar vacaciones)
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 0s
2024-11-06 14:49:02 -04:00
81acc686e6 Merge pull request 'Correccion registro solicitud vacaciones actual y anterior' (#67) from Vacaciones into En-desarrollo
Some checks failed
PR Builder / Build-PR (pull_request) Failing after 19s
Reviewed-on: #67
2024-11-05 18:59:37 +00:00
9fdf1c08b5 Correccion registro solicitud vacaciones actual y anterior
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 0s
2024-11-05 14:58:28 -04:00
4f128fd02e Merge pull request 'Vacaciones' (#66) from Vacaciones into En-desarrollo
Some checks failed
PR Builder / Build-PR (pull_request) Failing after 22s
Reviewed-on: #66
2024-11-05 18:32:42 +00:00
88e80a7dcc traducir a español y cambiar uso de grid a paging grid de RequestEmploye
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 1s
2024-11-05 13:33:00 -04:00
7e415ae837 cambiar team string a un team class 2024-11-05 10:47:16 -04:00
Melina Gutierrez
4561a43866 primer commit 2024-11-04 21:45:02 -04:00
2b6400fe24 Merge branch 'En-desarrollo' of https://git.primefactorsolutions.com/PFS/pfs-intra into En-desarrollo
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m35s
2024-11-04 00:49:43 -04:00
4615febec3 Actualizar BD - Actualizar el estado de los registros de solicitudes de vacaciones 2024-11-04 00:48:59 -04:00
9d87493f7d Merge pull request 'Vacaciones' (#64) from Vacaciones into En-desarrollo
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m27s
Reviewed-on: #64
2024-11-04 04:25:22 +00:00
eb6183df19 Correcciones
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 0s
2024-11-04 00:24:34 -04:00
45bf76c3d8 Actualizar BD - Actualizar el estado de los registros de solicitudes de vacaciones 2024-11-04 00:19:12 -04:00
cfd5599d32 #55 Perfil de Personal Administrativo - Añadir Vacaciones del Empleado (No aceptar fechas pasadas) 2024-11-03 23:40:13 -04:00
4421c6fe45 #55 Perfil de Personal Administrativo - Añadir Vacaciones del Empleado (correcciones) 2024-11-03 23:22:09 -04:00
71cb2d67c9 Perfil de Personal Administrativo - Listado solicitudes pendientes de Vacaciones 2024-11-02 00:51:21 -04:00
3efa8c73d5 Traducir a epañol las sentencias de incercion de datos 2024-11-01 13:42:23 -04:00
c0cc1e86de Categoria vacaciones - traducirlo al español 2024-10-31 01:42:33 -04:00
a02499d53f Estado de la solicitud - traducirlo al español 2024-10-31 01:34:18 -04:00
ca630ef5c6 #37 Perfil de Personal Administrativo - Listado General de Vacaciones (Tracducir a español) 2024-10-31 01:29:59 -04:00
f7ab9f8421 #37 Perfil de Personal Administrativo - Listado General de Vacaciones (Modificar calcular total general) 2024-10-31 01:16:48 -04:00
1e22223f27 Merge pull request 'En-desarrollo' (#62) from En-desarrollo into main
All checks were successful
Builder / Build-Project (push) Successful in 2m40s
Reviewed-on: #62
2024-10-30 20:14:57 +00:00
2f4d1ab130 adding default jwt secret
All checks were successful
Builder / Build-Project (push) Successful in 2m37s
2024-10-30 16:10:57 -04:00
94e7432551 #37 Perfil de Personal Administrativo - Listado General de Vacaciones (Modificar estado del empleado) 2024-10-30 15:10:05 -04:00
83ba9f7478 Merge pull request 'Vacaciones' (#63) from Vacaciones into En-desarrollo
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m28s
Reviewed-on: #63
2024-10-30 18:56:02 +00:00
68c94e6193 Agregar solicitud vacaciones a la bd para pruebas
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 0s
2024-10-30 14:55:22 -04:00
860b453138 #37 Perfil de Personal Administrativo - Listado General de Vacaciones (Correccion filtrado) 2024-10-30 14:46:51 -04:00
4f6935341d Merge pull request 'Vacaciones' (#61) from Vacaciones into En-desarrollo
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m32s
Reviewed-on: #61
2024-10-30 18:31:55 +00:00
335e895197 Merge pull request '#10-Registro-Informacion-Profesional' (#60) from #10-Registro-Informacion-Profesional into main
All checks were successful
Builder / Build-Project (push) Successful in 2m38s
Reviewed-on: #60
2024-10-30 01:52:11 +00:00
Melina Gutierrez
937b1ef531 \#46 Perfil de Empleado - Registro Semanal mas reporte, falta mejorar el reporte semanal
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m35s
#46 Perfil de Empleado - Registro Semanal

\#46 Perfil de Empleado - Registro Semanal Y Mensual falta bd

\#46 Perfil de Empleado - Registro Mensual

\#46 Perfil de Empleado - Registro Semanal de Horas trabajadas con el numero de semanas pero sin la bd

\#46 Perfil de Empleado - Registro Semanal de Horas trabajadas con el numero de semanas

\#46 Perfil de Empleado - Registro Semanal de Horas trabajadas

#46 Perfil de Empleado - Registro Semanal Y Mensual falta bd
2024-10-29 21:46:25 -04:00
98ed64dfda Listado Solicitudes de Empleado - Calcular totales
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 0s
2024-10-29 15:28:20 -04:00
476884408a #37 Perfil de Personal Administrativo - Listado General de Vacaciones (Agregar total general) 2024-10-28 19:12:24 -04:00
f0c0811f0d #55 Perfil de Personal Administrativo - Añadir Vacaciones del Empleado (Verificacion obtencion de datos) 2024-10-28 18:16:44 -04:00
7d20703a7d #55 Perfil de Personal Administrativo - Añadir Vacaciones del Empleado (Verificacion de validaciones) 2024-10-28 00:55:41 -04:00
73915b1364 #55 Perfil de Personal Administrativo - Añadir Vacaciones del Empleado (Correccion) 2024-10-27 20:05:31 -04:00
6dfa5c0ec2 updated vaadin version
All checks were successful
Builder / Build-Project (push) Successful in 2m36s
2024-10-27 14:43:45 -04:00
6a1d3e70c6 #37 Perfil de Personal Administrativo - Listado General de Vacaciones (Correcciones) 2024-10-27 00:19:56 -04:00
82232d23c7 fix build
All checks were successful
Builder / Build-Project (push) Successful in 1m28s
2024-10-23 15:25:45 -04:00
807555a2dd #55 Perfil de Personal Administrativo - Añadir Vacaciones del Empleado (Configurar habilitacion categoria si previa solicitud expira) 2024-10-23 15:09:36 -04:00
cb3ed95c0c #55 Perfil de Personal Administrativo - Añadir Vacaciones del Empleado (Configurar vacacion por antiguedad del empleado) 2024-10-23 08:02:12 -04:00
52bed61ae8 #55 Perfil de Personal Administrativo - Añadir Vacaciones del Empleado (Configurar categoria Permiso de Salud) 2024-10-23 07:28:15 -04:00
3ddacfc771 fix reset password view
Some checks failed
Builder / Build-Project (push) Failing after 2m22s
2024-10-22 22:30:08 -04:00
292825d1a8 reset password views
Some checks failed
Builder / Build-Project (push) Failing after 2m25s
2024-10-22 22:13:31 -04:00
0117915b9d #55 Perfil de Personal Administrativo - Añadir Vacaciones del Empleado (Configurar si DaysBalance sea mayor a cero) 2024-10-22 00:20:13 -04:00
623b5b0ee6 Merge pull request 'En-desarrollo' (#59) from En-desarrollo into main
All checks were successful
Builder / Build-Project (push) Successful in 2m29s
Reviewed-on: #59
2024-10-22 01:01:10 +00:00
6806bdc24b Merge pull request 'Vacaciones' (#58) from Vacaciones into En-desarrollo
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m31s
Reviewed-on: #58
2024-10-21 16:43:11 +00:00
a5e166351a #55 Perfil de Personal Administrativo - Añadir Vacaciones del Empleado (Refactorizacion configuracion form)
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 0s
2024-10-21 12:39:28 -04:00
69c61e093e #55 Perfil de Personal Administrativo - Añadir Vacaciones del Empleado (Configuara fecha manual) 2024-10-21 12:14:26 -04:00
0858566e8e #55 Perfil de Personal Administrativo - Añadir Vacaciones del Empleado (Inactivar categoria por genero) 2024-10-20 22:49:47 -04:00
1c18cd566f #55 Perfil de Personal Administrativo - Añadir Vacaciones del Empleado (Excluir las categorias que cuenten con un registro) 2024-10-20 18:24:40 -04:00
2582c5e903 #55 Perfil de Personal Administrativo - Añadir Vacaciones del Empleado (Para seleccionar categoria requerir empleado) 2024-10-20 15:16:34 -04:00
2c30bed91f #55 Perfil de Personal Administrativo - Añadir Vacaciones del Empleado (Actualizar automaticamente fechas) 2024-10-20 15:11:24 -04:00
a37b93591f #55 Perfil de Personal Administrativo - Añadir Vacaciones del Empleado (Guardar solicitud) 2024-10-20 15:05:05 -04:00
8ab5016757 #55 Perfil de Personal Administrativo - Añadir Vacaciones del Empleado (Crear base BD vacaciones) 2024-10-19 00:01:15 -04:00
dac11494ea Merge pull request 'Vacaciones' (#57) from Vacaciones into En-desarrollo
Reviewed-on: #57
2024-10-18 17:35:12 +00:00
85dad4900c #55 Perfil de Personal Administrativo - Añadir Vacaciones del Empleado
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 0s
2024-10-18 13:33:47 -04:00
34940ff794 Lista de Vacaciones - Calcular totales de vacaciones 2024-10-16 21:01:18 -04:00
3b085e835b Merge pull request 'En-desarrollo' (#53) from En-desarrollo into main
All checks were successful
Builder / Build-Project (push) Successful in 2m28s
Reviewed-on: #53
2024-10-16 01:30:59 +00:00
5a270d6795 Merge pull request '#37, #41#, 42, #43, #44 Listado General Vacaciones - Vista y Filtros' (#56) from Vacaciones into En-desarrollo
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m38s
Reviewed-on: #56
2024-10-15 19:06:19 +00:00
22963adad9 #37, #41#, 42, #43, #44 Listado General Vacaciones - Vista y Filtros
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 0s
2024-10-15 15:04:26 -04:00
7d3b63f6a8 Merge pull request 'Vacaciones' (#54) from Vacaciones into En-desarrollo
Some checks failed
PR Builder / Build-PR (pull_request) Failing after 20s
Reviewed-on: #54
2024-10-14 21:43:44 +00:00
696481a695 Resolver conflictos entre las Ramas Vacaciones y En-desarrollo
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 0s
2024-10-14 17:42:31 -04:00
0d9e0755b0 #49, #50 Perfil de Personal Administrativo - Ver/Editar Vacaciones del Empleado
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 0s
2024-10-14 01:17:23 -04:00
81b3003d1b Modelo empleado - Unir bd Team a la bd Employee 2024-10-13 20:25:39 -04:00
602ab686f5 Vacaciones del Empleado - Refactorizar 2024-10-13 20:25:38 -04:00
62d678c407 Vacaciones del Empleado - Filtrar por categoria y estado 2024-10-13 20:25:37 -04:00
3768a69cf4 adding user avatar and logout menu
All checks were successful
Builder / Build-Project (push) Successful in 2m33s
2024-10-11 22:45:51 -04:00
2a47bb8d9a Merge pull request 'Listado-General-Vacaciones' (#52) from Listado-General-Vacaciones into En-desarrollo
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m30s
Reviewed-on: #52
2024-10-11 19:30:22 +00:00
0fd278ac62 Modelo empleado - Unir bd Team a la bd Employee
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 0s
2024-10-11 01:25:56 -04:00
f8f71b187c Merge branch 'main' into Listado-General-Vacaciones 2024-10-11 00:55:37 -04:00
334802e004 Vacaciones del Empleado - Refactorizar 2024-10-11 00:53:46 -04:00
f7d0e9df23 Merge branch 'Listado-General-Vacaciones' of https://git.primefactorsolutions.com/PFS/pfs-intra into Listado-General-Vacaciones 2024-10-10 18:47:23 -04:00
2a59bc4e7e Merge pull request '#37, #41, #42, #43, #44 Perfil de Personal Administrativo - Listado General de Vacaciones y Filtrado' (#48) from Listado-General-Vacaciones into main
All checks were successful
Builder / Build-Project (push) Successful in 2m32s
Reviewed-on: #48
2024-10-10 19:56:03 +00:00
e8e0f60c4e Vacaciones del Empleado - Filtrar por categoria y estado 2024-10-10 13:29:14 -04:00
02c0b848af Actualizar src/main/java/com/primefactorsolutions/model/TimeOffRequest.java
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m27s
2024-10-09 14:44:28 +00:00
b14e7902cc Correcciones
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m27s
2024-10-08 15:38:41 -04:00
f90efef23c Merge pull request '#37, #41, #42, #43, #44 Perfil de Personal Administrativo - Listado General de Vacaciones y Filtrado' (#47) from Listado-General-Vacaciones into En-desarrollo
Reviewed-on: #47
2024-10-08 18:48:54 +00:00
d6dce9a4d0 #37, #41, #42, #43, #44 Perfil de Personal Administrativo - Listado General de Vacaciones y Filtrado
Some checks failed
PR Builder / Build-PR (pull_request) Failing after 19s
2024-10-08 14:47:50 -04:00
ef78cc91a8 Merge pull request '#10-Registro-Informacion-Profesional' (#38) from #10-Registro-Informacion-Profesional into main
All checks were successful
Builder / Build-Project (push) Successful in 2m29s
Reviewed-on: #38
2024-10-08 18:00:43 +00:00
Melina Gutierrez
379e822572 #46 Perfil de Empleado - Registro Semanal de Horas trabajadas
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m24s
2024-10-08 13:14:34 -04:00
Melina Gutierrez
067941e540 #46 Perfil de Empleado - Registro Semanal de Horas trabajadas
Some checks failed
PR Builder / Build-PR (pull_request) Failing after 20s
2024-10-08 12:16:19 -04:00
Melina Gutierrez
2c18455581 Merge branch 'main' into #10-Registro-Informacion-Profesional 2024-10-08 11:57:41 -04:00
2782fcfb86 Merge pull request 'En-desarrollo' (#27) from En-desarrollo into main
All checks were successful
Builder / Build-Project (push) Successful in 2m31s
Reviewed-on: #27
Reviewed-by: alex <alex@primefactorsolutions.com>
2024-10-07 18:23:33 +00:00
d2357a2adf Merge branch 'main' into En-desarrollo
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m29s
2024-10-04 15:18:21 -04:00
Melina Gutierrez
6316230a2a #4 Perfil de Empleado - Detalles para formulario de Empleado
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m27s
2024-10-04 13:26:10 -04:00
Melina Gutierrez
4ae2badb7b #4 Perfil de Empleado - Revisar campos del Formulario 2024-10-04 01:25:26 -04:00
Melina Gutierrez
592a8d18ea Merge remote-tracking branch 'refs/remotes/origin/main' into #10-Registro-Informacion-Profesional
# Conflicts:
#	src/main/java/com/primefactorsolutions/views/EmployeeView.java
2024-10-03 09:20:15 -04:00
2d4dbbd023 Coreccion cambios ver documento
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m8s
2024-10-02 16:06:59 -04:00
2f343daf89 Adding report service
All checks were successful
Builder / Build-Project (push) Successful in 2m33s
2024-10-01 22:24:27 -04:00
78f41d59e0 Coreccion del enlace a documentos
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m4s
2024-10-01 18:35:52 -04:00
e77e63491a Resuelve construccion fallido
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m13s
2024-10-01 17:57:26 -04:00
9c94b703ca Merge branch 'En-desarrollo' of https://git.primefactorsolutions.com/PFS/pfs-intra into En-desarrollo
Some checks failed
PR Builder / Build-PR (pull_request) Failing after 13s
2024-10-01 17:39:26 -04:00
88342555a0 Resuelve conflictos en Employee.java y MainLayout.java 2024-10-01 15:40:46 -04:00
2e544519da Merge pull request 'Perfil-Empleado-Administrativo-Documentacion' (#40) from Perfil-Empleado-Administrativo-Documentacion into En-desarrollo
Reviewed-on: #40
2024-10-01 19:35:51 +00:00
85e0e44f4f Merge branch 'Perfil-Empleado-Administrativo-Documentacion' of https://git.primefactorsolutions.com/PFS/pfs-intra into Perfil-Empleado-Administrativo-Documentacion 2024-10-01 15:32:29 -04:00
dccc3ea09d #31 Perfil de Usuario Administrativo - EDITAR Documentos (Correccion validacion para editar) 2024-10-01 13:27:49 -04:00
9b67ee3902 Perfil de Personal Administrativo - VER, EDITAR, AÑADIR Documento (Correccion en el el boton view document) 2024-09-30 19:28:44 -04:00
Melina Gutierrez
40aa10bc7d Merge remote-tracking branch 'origin/#10-Registro-Informacion-Profesional' into #10-Registro-Informacion-Profesional
# Conflicts:
#	src/main/java/com/primefactorsolutions/views/EmployeeView.java
2024-09-30 08:43:13 -04:00
b6a27c435e Merge pull request 'Perfil-Empleado-Administrativo-Documentacion' (#39) from Perfil-Empleado-Administrativo-Documentacion into En-desarrollo
Reviewed-on: #39
2024-09-27 07:31:14 +00:00
26af5f63c7 Merge branch 'En-desarrollo' into Perfil-Empleado-Administrativo-Documentacion 2024-09-27 07:30:59 +00:00
6d9a2f65e5 #32 Perfil de Personal Administrativo - VER Documento (agregar boton ver documento) 2024-09-27 03:29:26 -04:00
1519d82993 #29 Perfil de Empleado Administrativo - LISTAR Documentos (filtar por tipo de documento y empleado) 2024-09-27 00:07:27 -04:00
25448fff44 fix readonly fields
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m16s
2024-09-26 22:08:51 -04:00
831f9ec9a5 merge conflicts 2024-09-26 22:08:51 -04:00
b4c27f8a9b merged conflicts 2024-09-26 22:08:51 -04:00
a7d1c08f90 Crear views para el modulo de documentos 2024-09-26 22:08:51 -04:00
2342984de5 fix PR workflow
All checks were successful
Builder / Build-Project (push) Successful in 2m8s
2024-09-26 22:08:02 -04:00
Melina Gutierrez
b7a63b8c78 #4 Perfil de Empleado - Revisar campos del Formulario 2024-09-26 15:26:34 -04:00
Melina Gutierrez
8bf4a97c88 #4 Perfil de Empleado - Añadir nuevos Campos para el formulario de registro del empleado 2024-09-26 07:51:03 -04:00
4241e7b52b #29 Perfil de Empleado Administrativo - LISTAR Documentos (correccion download, view, edit) 2024-09-25 18:50:59 -04:00
439c1f135d adding wf details
All checks were successful
Builder / Build-Project (push) Successful in 2m2s
2024-09-24 22:11:30 -04:00
5c903ba360 adding pr build wf
All checks were successful
Builder / Build-Project (push) Successful in 2m10s
2024-09-24 22:00:06 -04:00
f0b41743c7 #30 Perfil de Usuario Administrativo - Añadir NUEVO Documento (agregar creador del registro del doc) 2024-09-24 20:44:16 -04:00
af3f13773b #29 Perfil de Empleado Administrativo - LISTAR Documentos (implementacion de download) 2024-09-24 19:26:18 -04:00
f3bda474d0 #32 Perfil de Personal Administrativo - VER Documento 2024-09-24 19:07:58 -04:00
45987ccad9 #29 Perfil de Empleado Administrativo - LISTAR Documentos (correccionse) 2024-09-24 17:25:43 -04:00
514e4eaf49 #29 Perfil de Empleado Administrativo - LISTAR Documentos 2024-09-24 17:07:53 -04:00
2caa878c07 #30 Perfil de Usuario Administrativo - Añadir NUEVO Documento 2024-09-24 12:30:03 -04:00
16a9a91790 fix tests
All checks were successful
Builder / Build-Project (push) Successful in 2m11s
2024-09-23 22:37:32 -04:00
ff2db18f0a deploy workflow
Some checks failed
Builder / Build-Project (push) Failing after 1m57s
2024-09-23 22:19:49 -04:00
9c4a7fc45c fix build
All checks were successful
Builder / Build-Project (push) Successful in 2m4s
2024-09-23 20:18:25 -04:00
8cb8915434 adding checkout step
Some checks failed
Builder / Build-Project (push) Failing after 8s
2024-09-23 20:13:26 -04:00
ddd0f9e0ce buidl workflow
Some checks failed
Builder / Build-Project (push) Failing after 0s
2024-09-23 20:05:12 -04:00
fc2784b728 fix workflow
All checks were successful
Builder / Explore-Gitea-Actions (push) Successful in 0s
2024-09-23 19:52:20 -04:00
6df1d45c81 Merge pull request 'Change menu' (#33) from Perfil-Personal-Administrativo-Documentacion into En-desarrollo
Reviewed-on: #33
2024-09-23 17:53:00 +00:00
e349503fb4 Change menu 2024-09-23 13:50:27 -04:00
c1fb36591a fix workflow
Some checks are pending
Builder / Explore-Gitea-Actions (push) Waiting to run
2024-09-19 22:39:57 -04:00
ab0059b0d2 fix workflow
Some checks are pending
Builder / Explore-Gitea-Actions (push) Waiting to run
2024-09-19 22:31:22 -04:00
db4c43d35e adding builder action
Some checks are pending
Builder / Explore-Gitea-Actions (push) Waiting to run
2024-09-19 22:28:57 -04:00
c997e3c282 Merge pull request 'Perfil-Personal-Administrativo-Documentacion' (#28) from Perfil-Personal-Administrativo-Documentacion into En-desarrollo
Reviewed-on: #28
2024-09-19 04:59:31 +00:00
613221dfb6 Refactorizacion del modulo de documentos 2024-09-19 00:56:44 -04:00
23e633cc5f remove println 2024-09-18 10:28:54 -04:00
451242bea4 custom userdetails implementation 2024-09-18 10:27:49 -04:00
13c81de48c Correcciones 2024-09-16 20:09:16 -04:00
2208450692 Merge pull request 'Perfil-Personal-Administrativo-Documentacion' (#26) from Perfil-Personal-Administrativo-Documentacion into En-desarrollo
Reviewed-on: #26
2024-09-15 22:14:02 +00:00
f2a1d53e4a #19 Perfil de Personal Administrativo - Cargar Documentación de Gobierno (Ver, cargar y guardar) 2024-09-15 18:13:11 -04:00
109ddde243 #18 Perfil de Personal Administrativo - Carga de Documentación Corporativa (Ver, cargar y guardar) 2024-09-15 17:52:43 -04:00
0744e16dea #21 Perfil de Personal Administrativo - Carga de Documentación Profesional (Ver, cargar y guardar) 2024-09-15 03:22:18 -04:00
ca307e2c0b #20 Perfil de Personal Administrativo - Carga de Documentación Personal (Ver, cargar y guardar) 2024-09-15 03:09:38 -04:00
4d12dfdd18 Merge pull request '#17 Perfil de Personal Administrativo - Carga de Documentación Laboral (Revisiòn)' (#25) from #17-Perfil-Personal-Administrativo into En-desarrollo
Reviewed-on: #25
2024-09-15 06:50:44 +00:00
50a7809e6d Merge pull request '#17-Perfil-Personal-Administrativo' (#24) from #17-Perfil-Personal-Administrativo into En-desarrollo
Reviewed-on: #24
2024-09-15 06:27:56 +00:00
70 changed files with 6666 additions and 1578 deletions

View File

@ -0,0 +1,14 @@
name: PR Builder
run-name: ${{ gitea.actor }} building PR
on: [pull_request]
jobs:
Build-PR:
runs-on: ubuntu-22.04
steps:
- run: echo "The job was automatically triggered by a ${{ gitea.event_name }} event on branch ${{ gitea.head_ref }} and ref is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
- name: Build PR
if: gitea.base_ref == 'main'
run: |
git clone --single-branch --branch "${{ gitea.head_ref }}" https://git.primefactorsolutions.com/PFS/pfs-intra.git && cd pfs-intra && ./mvnw clean package -Pproduction
- run: echo "This job's status is ${{ job.status }}."

View File

@ -0,0 +1,16 @@
name: Builder
run-name: ${{ gitea.actor }} building
on:
push:
branches:
- main
jobs:
Build-Project:
runs-on: ubuntu-22.04
steps:
- run: echo "The job was automatically triggered by a ${{ gitea.event_name }} event on branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
- name: Build package
run: |
git clone --single-branch --branch main https://git.primefactorsolutions.com/PFS/pfs-intra.git && cd pfs-intra && ./mvnw clean package -Pproduction && unlink /home/ubuntu/pfs-intra/app.jar && cp target/*.jar /home/ubuntu/pfs-intra/app.jar && sudo systemctl restart pfs-intra
- run: echo "This job's status is ${{ job.status }}."

1
.gitignore vendored
View File

@ -19,3 +19,4 @@ drivers/
# Error screenshots generated by TestBench for failed integration tests
error-screenshots/
webpack.generated.js
*.env

2492
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -5,21 +5,23 @@
"dependencies": {
"@f0rce/ace-widget": "1.0.2",
"@polymer/polymer": "3.5.1",
"@vaadin/bundles": "24.4.2",
"@vaadin-component-factory/vcf-pdf-viewer": "2.0.1",
"@vaadin/bundles": "24.5.1",
"@vaadin/common-frontend": "0.0.19",
"@vaadin/polymer-legacy-adapter": "24.4.2",
"@vaadin/react-components": "24.4.2",
"@vaadin/react-components-pro": "24.4.2",
"@vaadin/router": "1.7.5",
"@vaadin/polymer-legacy-adapter": "24.5.1",
"@vaadin/react-components": "24.5.1",
"@vaadin/react-components-pro": "24.5.1",
"@vaadin/router": "2.0.0",
"@vaadin/vaadin-development-mode-detector": "2.0.7",
"@vaadin/vaadin-lumo-styles": "24.4.2",
"@vaadin/vaadin-material-styles": "24.4.2",
"@vaadin/vaadin-themable-mixin": "24.4.2",
"@vaadin/vaadin-usage-statistics": "2.1.2",
"@vaadin/vaadin-lumo-styles": "24.5.1",
"@vaadin/vaadin-material-styles": "24.5.1",
"@vaadin/vaadin-themable-mixin": "24.5.1",
"@vaadin/vaadin-usage-statistics": "2.1.3",
"construct-style-sheets-polyfill": "3.1.0",
"date-fns": "2.29.3",
"lit": "3.1.4",
"proj4": "2.11.0",
"print-js": "1.6.0",
"proj4": "2.12.1",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-router-dom": "6.23.1"
@ -48,21 +50,23 @@
"dependencies": {
"@f0rce/ace-widget": "1.0.2",
"@polymer/polymer": "3.5.1",
"@vaadin/bundles": "24.4.2",
"@vaadin-component-factory/vcf-pdf-viewer": "2.0.1",
"@vaadin/bundles": "24.5.1",
"@vaadin/common-frontend": "0.0.19",
"@vaadin/polymer-legacy-adapter": "24.4.2",
"@vaadin/react-components": "24.4.2",
"@vaadin/react-components-pro": "24.4.2",
"@vaadin/router": "1.7.5",
"@vaadin/polymer-legacy-adapter": "24.5.1",
"@vaadin/react-components": "24.5.1",
"@vaadin/react-components-pro": "24.5.1",
"@vaadin/router": "2.0.0",
"@vaadin/vaadin-development-mode-detector": "2.0.7",
"@vaadin/vaadin-lumo-styles": "24.4.2",
"@vaadin/vaadin-material-styles": "24.4.2",
"@vaadin/vaadin-themable-mixin": "24.4.2",
"@vaadin/vaadin-usage-statistics": "2.1.2",
"@vaadin/vaadin-lumo-styles": "24.5.1",
"@vaadin/vaadin-material-styles": "24.5.1",
"@vaadin/vaadin-themable-mixin": "24.5.1",
"@vaadin/vaadin-usage-statistics": "2.1.3",
"construct-style-sheets-polyfill": "3.1.0",
"date-fns": "2.29.3",
"lit": "3.1.4",
"proj4": "2.11.0",
"print-js": "1.6.0",
"proj4": "2.12.1",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-router-dom": "6.23.1"
@ -87,7 +91,7 @@
"workbox-core": "7.1.0",
"workbox-precaching": "7.1.0"
},
"hash": "ff8035f5a0dd319a5c705d9cea5b3e1d6c1a0bb871c35af8dd8e4ede5c65fe76"
"hash": "1a0f17d48b329307b5862bc57499307d1b89f7d89260121c2b7189f76957c436"
},
"overrides": {
"@vaadin/bundles": "$@vaadin/bundles",
@ -109,6 +113,8 @@
"@vaadin/vaadin-themable-mixin": "$@vaadin/vaadin-themable-mixin",
"@vaadin/vaadin-lumo-styles": "$@vaadin/vaadin-lumo-styles",
"@vaadin/vaadin-material-styles": "$@vaadin/vaadin-material-styles",
"@f0rce/ace-widget": "$@f0rce/ace-widget"
"@f0rce/ace-widget": "$@f0rce/ace-widget",
"print-js": "$print-js",
"@vaadin-component-factory/vcf-pdf-viewer": "$@vaadin-component-factory/vcf-pdf-viewer"
}
}

74
pom.xml
View File

@ -11,7 +11,8 @@
<properties>
<java.version>21</java.version>
<vaadin.version>24.4.6</vaadin.version>
<vaadin.version>24.5.1</vaadin.version>
<vaadin-maven-plugin.version>24.4.6</vaadin-maven-plugin.version>
</properties>
<parent>
@ -119,6 +120,15 @@
<artifactId>commons-beanutils</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.17.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
@ -181,6 +191,16 @@
<artifactId>viritin</artifactId>
<version>2.8.22</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>com.flowingcode.addons</groupId>
<artifactId>simple-timer</artifactId>
@ -225,16 +245,64 @@
<artifactId>commons-lang3</artifactId>
<version>3.14.0</version>
</dependency>
<dependency>
<groupId>org.vaadin.addons.componentfactory</groupId>
<artifactId>vcf-pdf-viewer</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>com.openhtmltopdf</groupId>
<artifactId>openhtmltopdf-pdfbox</artifactId>
<version>1.0.10</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.32</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
<version>9.0.1</version>
</dependency>
</dependencies>
<build>
<defaultGoal>spring-boot:run</defaultGoal>
<plugins>
<plugin>
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
<version>9.0.1</version>
<executions>
<execution>
<id>get-the-git-infos</id>
<goals>
<goal>revision</goal>
</goals>
<phase>initialize</phase>
</execution>
</executions>
<configuration>
<generateGitPropertiesFile>true</generateGitPropertiesFile>
<generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties</generateGitPropertiesFilename>
<includeOnlyProperties>
<includeOnlyProperty>^git.build.(time|version)$</includeOnlyProperty>
<includeOnlyProperty>^git.commit.id.(abbrev|full)$</includeOnlyProperty>
</includeOnlyProperties>
<commitIdGenerationMode>full</commitIdGenerationMode>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
@ -264,7 +332,7 @@
<plugin>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-maven-plugin</artifactId>
<version>${vaadin.version}</version>
<version>${vaadin-maven-plugin.version}</version>
<executions>
<execution>
<goals>
@ -325,7 +393,7 @@
<plugin>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-maven-plugin</artifactId>
<version>${vaadin.version}</version>
<version>${vaadin-maven-plugin.version}</version>
<executions>
<execution>
<goals>

Binary file not shown.

Binary file not shown.

View File

@ -4,7 +4,6 @@ import com.vaadin.flow.component.page.AppShellConfigurator;
import com.vaadin.flow.theme.Theme;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* The entry point of the Spring Boot application.
*

View File

@ -0,0 +1,19 @@
package com.primefactorsolutions.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.ClassPathResource;
@Configuration
public class PropertiesConfig {
@Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
final PropertySourcesPlaceholderConfigurer propsConfig = new PropertySourcesPlaceholderConfigurer();
propsConfig.setLocation(new ClassPathResource("git.properties"));
propsConfig.setIgnoreResourceNotFound(true);
propsConfig.setIgnoreUnresolvablePlaceholders(true);
return propsConfig;
}
}

View File

@ -1,20 +1,32 @@
package com.primefactorsolutions.config;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.views.LoginView;
import com.vaadin.flow.spring.security.VaadinWebSecurity;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.ldap.LdapBindAuthenticationManagerFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import java.util.Collection;
@EnableWebSecurity
@Configuration
public class SecurityConfig extends VaadinWebSecurity {
@Value("${spring.ldap.url}")
private String ldapUrl;
@Override
protected void configure(final HttpSecurity http) throws Exception {
@ -37,13 +49,28 @@ public class SecurityConfig extends VaadinWebSecurity {
}
@Bean
public AuthenticationManager authenticationManager() {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(
"ldap://localhost:8389/dc=primefactorsolutions,dc=com");
public AuthenticationManager authenticationManager(final UserDetailsContextMapper userDetailsContextMapper) {
final DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(
String.format("%s/dc=primefactorsolutions,dc=com", ldapUrl));
contextSource.setCacheEnvironmentProperties(false);
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
final LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
factory.setUserDnPatterns("uid={0},ou=users");
factory.setUserDetailsContextMapper(userDetailsContextMapper);
return factory.createAuthenticationManager();
}
@Bean
public UserDetailsContextMapper userDetailsContextMapper(final EmployeeService employeeService) {
return new LdapUserDetailsMapper() {
@Override
public UserDetails mapUserFromContext(final DirContextOperations ctx, final String username,
final Collection<? extends GrantedAuthority> authorities) {
final UserDetails details = super.mapUserFromContext(ctx, username, authorities);
final Employee employee = employeeService.getDetachedEmployeeByUsername(details.getUsername());
return employee == null ? details : employee;
}
};
}
}

View File

@ -10,12 +10,6 @@ public abstract class BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
private String fileName;
@Lob
@Column(columnDefinition = "BLOB")
private byte[] fileData;
@Enumerated(EnumType.STRING)
private DocumentType documentType;
@Version
private int version;
@ -50,16 +44,4 @@ public abstract class BaseEntity {
}
return super.equals(that);
}
public String getFileName() {
return fileName;
}
public byte[] getFileData() {
return fileData;
}
public DocumentType getDocumentType() {
return documentType;
}
}

View File

@ -4,8 +4,8 @@ import jakarta.persistence.*;
import lombok.*;
@Entity
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@ -13,7 +13,11 @@ public class Document extends BaseEntity {
private String fileName;
@Enumerated(EnumType.STRING)
private DocumentType documentType;
@ManyToOne
@JoinColumn(name = "employee_id", nullable = false)
private Employee employee;
@Lob
@Column(columnDefinition = "BLOB")
private byte[] fileData;
private String creator;
}

View File

@ -1,6 +1,7 @@
package com.primefactorsolutions.model;
public enum DocumentType {
All,
ID_CARD,
PAY_STUB,
PAY_SLIPS,
@ -9,5 +10,27 @@ public enum DocumentType {
NDA,
MEMORANDUMS,
CONTRACT_APPROVAL_MTEPS,
BACKGROUND_CHECK_CERTIFICATE,
PRE_EMPLOYMENT_EVALUATION,
INSURANCE_REGISTRATION_FORM,
INSURANCE_CANCELLATION_FORM,
PROFESSIONAL_DEGREE_1,
PROFESSIONAL_CERTIFICATE_1,
PROFESSIONAL_DEGREE_2,
PROFESSIONAL_CERTIFICATE_2,
PROFESSIONAL_DEGREE_3,
PROFESSIONAL_CERTIFICATE_3,
GENERAL_LABOR_REGULATIONS,
REMOTE_WORK_GUIDELINES,
SAFETY_REGULATIONS,
HUMAN_RESOURCES_GUIDELINES,
ADMINISTRATION_FUNCTIONS_MANUAL,
ENGINEERING_FUNCTIONS_MANUAL,
GENERAL_LABOR_LAW,
SUPREME_DECREE,
REGULATORY_RESOLUTION,
COMPLEMENTARY_REGULATION,
HEALTH_SAFETY_LAW,
INTERNSHIP_RULES,
OTHER
}

View File

@ -1,52 +1,171 @@
package com.primefactorsolutions.model;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
package com.primefactorsolutions.model;
import java.time.LocalDate;
import com.google.common.collect.Lists;
import jakarta.persistence.*;
import jakarta.validation.constraints.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Employee extends BaseEntity {
private String userName;
private String firstName;
private String lastName;
private LocalDate birthday;
private String birthCity;
@Enumerated(EnumType.STRING)
private MaritalStatus maritalStatus;
private String residenceAddress;
private String phoneNumber;
private String personalEmail;
private String position;
private String team;
private String emergencyCName;
private String emergencyCAddress;
private String emergencyCPhone;
private String emergencyCEmail;
@Column(columnDefinition = "TEXT")
private String profileImage;
@Enumerated(EnumType.STRING)
private Status status;
public enum Status {
ACTIVE,
INACTIVE
}
public enum MaritalStatus {
SINGLE,
MARRIED,
WIDOWED,
DIVORCED
}
public Status getStatus() {
return status;
}
public void setStatus(final Status status) {
this.status = status;
}
import java.time.LocalDate;
import java.util.Collection;
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Employee extends BaseEntity implements UserDetails {
private String username;
@NotNull(message = "El nombre no puede estar vacío")
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El nombre solo debe contener letras")
private String firstName;
@NotNull(message = "El apellido no puede estar vacío")
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El apellido solo debe contener letras")
private String lastName;
private LocalDate birthday;
@Pattern(regexp = "^[a-zA-Z ,]+$", message = "La ciudad de nacimiento solo debe contener letras, espacios o comas")
private String birthCity;
private String age;
@Size(max = 50, message = "La dirección de residencia no debe exceder 50 caracteres")
private String residenceAddress;
@Size(max = 30, message = "La dirección local no debe exceder 100 caracteres")
@Pattern(regexp = "^[a-zA-Z -]+$", message = "La dirección local solo debe contener letras y guion")
private String localAddress;
@Pattern(regexp = "^[0-9]+$", message = "El número de teléfono debe contener solo números")
private String phoneNumber;
@Email(message = "El correo personal no tiene un formato válido")
private String personalEmail;
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El cargo solo debe contener letras")
private String position;
@ManyToOne
@JoinColumn(name = "team_id", nullable = false)
private Team team;
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El nombre y apellido de contacto"
+ " de emergencia solo debe contener letras")
private String emergencyCName;
private String emergencyCAddress;
@Pattern(regexp = "^[0-9]+$", message = "El teléfono de contacto de emergencia "
+ " debe contener solo números")
private String emergencyCPhone;
@Email(message = "El correo de contacto de emergencia no tiene un formato válido")
private String emergencyCEmail;
@Max(value = 10, message = "El número de hijos no puede exceder a 10")
@Pattern(regexp = "^[0-9]+$", message = "La cantidad de hijos debe contener solo números")
private String numberOfChildren;
@Pattern(regexp = "^[0-9]+$", message = "El CI debe contener solo números")
private String ci;
private String issuedIn;
private String pTitle1;
private String pTitle2;
private String pTitle3;
private String pStudy1;
private String pStudy2;
private String pStudy3;
private String certification1;
private String certification2;
private String certification3;
private String certification4;
private String recognition;
private String achievements;
private String language;
private String languageLevel;
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "El código debe contener solo letras y números")
private String cod;
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El lead manager solo debe contener letras")
private String leadManager;
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El proyecto solo debe contener letras")
private String project;
private LocalDate dateOfEntry;
private LocalDate dateOfExit;
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El tipo de contrato solo debe contener letras")
private String contractType;
@Pattern(regexp = "^[0-9]+$", message = "La antigüedad debe contener solo números")
private String seniority;
@Pattern(regexp = "^[0-9]+(\\.[0-9]{1,2})?$", message = "El salario debe ser un número con hasta dos decimales")
private String salary;
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El nombre del banco solo debe contener letras")
private String bankName;
@Pattern(regexp = "^[0-9]+$", message = "El número de cuenta debe contener solo números")
private String accountNumber;
private String gpss;
private String sss;
@Pattern(regexp = "^[a-zA-Z ]+$", message = "Los derechohabientes solo deben contener letras")
private String beneficiaries;
@Column(columnDefinition = "TEXT")
private String profileImage;
@Enumerated(EnumType.STRING)
private Status status;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Lists.newArrayList();
}
@Override
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public enum Status {
ACTIVE,
INACTIVE
}
@Enumerated(EnumType.STRING)
private MaritalStatus maritalStatus;
public enum MaritalStatus {
SINGLE,
MARRIED,
WIDOWED,
DIVORCED
}
@Enumerated(EnumType.STRING)
private Gender gender;
public enum Gender {
MALE,
FEMALE
}
}

View File

@ -0,0 +1,149 @@
package com.primefactorsolutions.model;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
import java.time.temporal.WeekFields;
import java.util.List;
import java.util.Locale;
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class HoursWorked extends BaseEntity {
@ManyToOne
@JoinColumn(name = "employee_id", nullable = true)
private Employee employee;
@ManyToOne
@JoinColumn(name = "team_id", nullable = true)
private Team team;
private int weekNumber;
private LocalDate date;
private String actividad;
private String tareasEspecificas;
private double hours;
private double horasTareasEspecificas;
private double horaspendientes;
private double totalHours;
public static double calculateTotalHours(final List<HoursWorked> activities) {
return activities.stream()
.mapToDouble(activity -> activity.hours + activity.horasTareasEspecificas)
.sum();
}
public static double calculatePendingHours(final List<HoursWorked> activities) {
double totalHoursWorked = calculateTotalHours(activities);
return Math.max(0, 40 - totalHoursWorked);
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(final Employee employee) {
this.employee = employee;
}
public int getWeekNumber() {
return weekNumber;
}
public void setWeekNumber(final int weekNumber) {
this.weekNumber = weekNumber;
}
public LocalDate getDate() {
return date;
}
public void setDate(final LocalDate date) {
this.date = date;
if (date != null) {
WeekFields weekFields = WeekFields.of(Locale.getDefault());
this.weekNumber = date.get(weekFields.weekOfWeekBasedYear());
}
}
public String getActividad() {
return actividad;
}
public void setActividad(final String actividad) {
this.actividad = actividad;
}
public double getHours() {
return hours;
}
public void setHours(final double hours) {
this.hours = hours;
}
public double getTotalHours() {
double total = this.getHours();
return totalHours + total;
}
public void setTotalHours(final double totalHours) {
this.totalHours = totalHours;
}
public Team getTeam() {
return team;
}
public void setTeam(final Team team) {
this.team = team;
}
public String getTareasEspecificas() {
return tareasEspecificas;
}
public void setTareasEspecificas(final String tareasEspecificas) {
this.tareasEspecificas = tareasEspecificas;
}
public double getHorasTareasEspecificas() {
return horasTareasEspecificas;
}
public void setHorasTareasEspecificas(final double horasTareasEspecificas) {
this.tareasEspecificas = tareasEspecificas;
}
public double getHoraspendientes() {
//double horasTrabajadas = this.getTotalHours() + this.getHorasTareasEspecificas();
return 40;
}
public void setHoraspendientes(final double horaspendientes) {
this.horaspendientes = horaspendientes;
}
public double getHoursWorked() {
return hours;
}
public void setHoursWorked(final double hoursWorked) {
this.hours = hoursWorked;
}
public double getTotalHoursWorked() {
return totalHours;
}
public void setTotalHoursWorked(final double totalHoursWorked) {
this.totalHours = totalHoursWorked;
}
}

View File

@ -0,0 +1,14 @@
package com.primefactorsolutions.model;
import jakarta.persistence.*;
import lombok.*;
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Team extends BaseEntity {
private String name;
}

View File

@ -1,21 +1,30 @@
package com.primefactorsolutions.model;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.OneToMany;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.List;
import java.time.LocalDate;
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class TimeOffRequest extends BaseEntity {
@OneToMany(fetch = FetchType.EAGER, mappedBy = "request", cascade = {CascadeType.ALL})
private List<TimeOffRequestEntry> entries;
private TimeOffRequestType type;
@ManyToOne
@JoinColumn(name = "employee_id", nullable = false)
private Employee employee;
@Enumerated(EnumType.STRING)
private TimeOffRequestType category;
@Enumerated(EnumType.STRING)
private TimeOffRequestStatus state;
private Double availableDays;
private LocalDate expiration;
private LocalDate startDate;
private LocalDate endDate;
private Double daysToBeTake;
private Double daysBalance;
}

View File

@ -0,0 +1,16 @@
package com.primefactorsolutions.model;
public enum TimeOffRequestStatus {
TODOS,
TOMADO,
APROBADO,
EN_USO,
PENDIENTE,
RECHAZADO,
VENCIDO,
SOLICITADO,
EN_REVISION,
COMPLETADO,
CANCELADO,
}

View File

@ -1,7 +1,31 @@
package com.primefactorsolutions.model;
public enum TimeOffRequestType {
VACATION,
MATERNITY,
OTHER
TODOS,
AÑO_NUEVO,
LUNES_CARNAVAL,
MARTES_CARNAVAL,
VIERNES_SANTO,
DIA_DEL_TRABAJADOR,
DIA_DE_LA_INDEPENDENCIA,
NAVIDAD,
DIA_DEL_ESTADO_PLURINACIONAL,
CORPUS_CHRISTI,
AÑO_NUEVO_ANDINO,
ANIVERSARIO_DEPARTAMENTAL,
DIA_DE_TODOS_LOS_DIFUNTOS,
CUMPLEAÑOS,
MATERNIDAD,
PATERNIDAD,
MATRIMONIO,
DUELO_1ER_GRADO,
DUELO_2ER_GRADO,
DIA_DEL_PADRE,
DIA_DE_LA_MADRE,
DIA_DE_LA_MUJER_INTERNACIONAL,
DIA_DE_LA_MUJER_NACIONAL,
PERMISOS_DE_SALUD,
VACACION_GESTION_ACTUAL,
VACACION_GESTION_ANTERIOR,
}

View File

@ -0,0 +1,30 @@
package com.primefactorsolutions.model;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Vacation extends BaseEntity {
@Enumerated(EnumType.STRING)
private TimeOffRequestType category;
private Integer monthOfYear;
private Integer dayOfMonth;
private Double duration;
private Double expiration;
@Enumerated(EnumType.STRING)
private Type type;
public enum Type {
FIXED,
MOVABLE,
OTHER
}
}

View File

@ -3,7 +3,15 @@ package com.primefactorsolutions.repositories;
import com.primefactorsolutions.model.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public interface EmployeeRepository extends JpaRepository<Employee, UUID> {
}
Optional<Employee> findByUsername(String username);
Optional<Employee> findByPersonalEmail(String personalEmail);
Optional<Employee> findByTeamId(UUID teamId);
List<Employee> findByTeamName(String teamName);
}

View File

@ -0,0 +1,16 @@
package com.primefactorsolutions.repositories;
import com.primefactorsolutions.model.HoursWorked;
import org.springframework.data.jpa.repository.JpaRepository;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
public interface HoursWorkedRepository extends JpaRepository<HoursWorked, UUID> {
List<HoursWorked> findByWeekNumber(int weekNumber);
List<HoursWorked> findByDate(LocalDate date);
List<HoursWorked> findByEmployeeIdAndWeekNumber(UUID employeeId, int weekNumber);
}

View File

@ -0,0 +1,9 @@
package com.primefactorsolutions.repositories;
import com.primefactorsolutions.model.Team;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.UUID;
public interface TeamRepository extends JpaRepository<Team, UUID> {
}

View File

@ -0,0 +1,18 @@
package com.primefactorsolutions.repositories;
import com.primefactorsolutions.model.TimeOffRequest;
import com.primefactorsolutions.model.TimeOffRequestStatus;
import com.primefactorsolutions.model.TimeOffRequestType;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public interface TimeOffRequestRepository extends JpaRepository<TimeOffRequest, UUID> {
List<TimeOffRequest> findByEmployeeId(UUID idEmployee);
Optional<TimeOffRequest> findByEmployeeIdAndState(UUID employeeId, TimeOffRequestStatus state);
List<TimeOffRequest> findByEmployeeIdAndCategory(UUID employeeId, TimeOffRequestType category);
List<TimeOffRequest> findByState(TimeOffRequestStatus state);
void deleteByEmployeeIdAndCategory(UUID employeeId, TimeOffRequestType category);
}

View File

@ -0,0 +1,11 @@
package com.primefactorsolutions.repositories;
import com.primefactorsolutions.model.TimeOffRequestType;
import com.primefactorsolutions.model.Vacation;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.UUID;
public interface VacationRepository extends JpaRepository<Vacation, UUID> {
Vacation findByCategory(TimeOffRequestType category);
}

View File

@ -0,0 +1,101 @@
package com.primefactorsolutions.service;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import com.primefactorsolutions.model.Employee;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
@Service
@Slf4j
public class AccountService {
private final EmailService emailService;
private final EmployeeService employeeService;
private final String secret;
public AccountService(final EmailService emailService, final EmployeeService employeeService,
@Value("${application.jwtSecret}") final String secret) {
this.emailService = emailService;
this.employeeService = employeeService;
this.secret = secret;
}
public void sendResetPasswordEmail(final String personalEmail) {
final Employee employee = employeeService.getEmployeeByPersonalEmail(personalEmail);
if (employee == null) {
log.warn("Could not find employee for email {}", personalEmail);
return;
}
final String link = createResetPasswordLink(employee.getUsername());
final String content = "Visit this link to reset your password: " + link;
emailService.sendEmail(personalEmail, "PFS - Reset Password", content);
}
public void resetPassword(final String username, final String newPassword, final String token) {
DecodedJWT decodedJWT;
try {
Algorithm algorithm = Algorithm.HMAC512(secret);
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer("pfs")
.build();
decodedJWT = verifier.verify(token);
final Instant expiry = decodedJWT.getExpiresAtAsInstant();
final String claim = decodedJWT.getClaim("username").asString();
if (expiry.isBefore(Instant.now())
|| !username.equals(claim)) {
log.warn("token invalid {} {} {}", username, claim, expiry);
return;
}
} catch (JWTVerificationException e) {
log.warn("error updating password", e);
return;
}
final Employee employee = employeeService.getDetachedEmployeeByUsername(username);
if (employee == null) {
log.warn("Could not find employee for username {}", username);
return;
}
if (StringUtils.isBlank(newPassword) || newPassword.length() < 8) {
throw new IllegalArgumentException("New password should be at least 8 chars long");
}
employeeService.updatePassword(employee, newPassword);
log.info("updated password for {}", username);
}
private String createResetPasswordLink(final String username) {
String token = "";
try {
Algorithm algorithm = Algorithm.HMAC512(secret);
token = JWT.create()
.withIssuer("pfs")
.withClaim("username", username)
.withExpiresAt(Instant.now().plus(1, ChronoUnit.HOURS))
.sign(algorithm);
} catch (JWTCreationException e) {
throw new RuntimeException(e);
}
return String.format("https://intra.primefactorsolutions.com/reset-password?username=%s&token=%s", username,
token);
}
}

View File

@ -1,30 +1,78 @@
package com.primefactorsolutions.service;
import com.primefactorsolutions.model.Document;
import com.primefactorsolutions.model.DocumentType;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.repositories.DocumentRepository;
import lombok.AllArgsConstructor;
import org.apache.commons.beanutils.BeanComparator;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
@AllArgsConstructor
public class DocumentService {
private final DocumentRepository documentRepository;
public DocumentService(final DocumentRepository documentRepository) {
this.documentRepository = documentRepository;
}
public void saveDocument(final Document newDocument) {
documentRepository.save(newDocument);
}
public void deleteDocument(final UUID id) {
documentRepository.deleteById(id);
}
public List<Document> getAllDocuments() {
return documentRepository.findAll();
}
public Optional<Document> getDocumentById(final UUID id) {
return documentRepository.findById(id);
public Document getDocument(final UUID id) {
Optional<Document> employee = documentRepository.findById(id);
return employee.orElse(null);
}
public List<Document> findDocuments(
final int start, final int pageSize, final String sortProperty, final boolean asc) {
List<Document> documents = documentRepository.findAll();
int end = Math.min(start + pageSize, documents.size());
documents.sort(new BeanComparator<>(sortProperty));
if (!asc) {
Collections.reverse(documents);
}
return documents.subList(start, end);
}
public List<Document> findDocuments(final int start, final int pageSize) {
List<Document> employees = documentRepository.findAll();
int end = Math.min(start + pageSize, employees.size());
return employees.subList(start, end);
}
public List<Document> findDocumentBy(final DocumentType documentType,
final Employee employee,
final int start,
final int pageSize) {
List<Document> documents = documentRepository.findAll();
if (documentType != null) {
documents = documents.stream()
.filter(doc -> doc.getDocumentType().equals(documentType))
.collect(Collectors.toList());
}
if (employee != null) {
documents = documents.stream()
.filter(doc -> doc.getEmployee().equals(employee))
.collect(Collectors.toList());
}
int end = Math.min(start + pageSize, documents.size());
return documents.subList(start, end);
}
}

View File

@ -0,0 +1,32 @@
package com.primefactorsolutions.service;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
@Service
@AllArgsConstructor
@Slf4j
public class EmailService {
public static final String NO_REPLY_PRIMEFACTORSOLUTIONS_COM = "no-reply@primefactorsolutions.com";
private final JavaMailSender emailSender;
public void sendEmail(final String email, final String title, final String messageContent) {
try {
final SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(NO_REPLY_PRIMEFACTORSOLUTIONS_COM);
message.setBcc(NO_REPLY_PRIMEFACTORSOLUTIONS_COM);
message.setTo(email);
message.setSubject(title);
message.setText(messageContent);
emailSender.send(message);
log.info("Sent email to {}", email);
} catch (Exception e) {
log.error("Error sending email to {}", email, e);
throw e;
}
}
}

View File

@ -1,5 +1,6 @@
package com.primefactorsolutions.service;
import com.primefactorsolutions.model.Employee;
import jakarta.persistence.EntityManager;
import lombok.AllArgsConstructor;
import org.apache.commons.beanutils.BeanComparator;
import com.primefactorsolutions.repositories.EmployeeRepository;
@ -17,18 +18,43 @@ import java.util.Collections;
@Service
@AllArgsConstructor
public class EmployeeService {
private static final String USERPASSWORD = "userPassword";
private static final String OBJECTCLASS = "objectclass";
private static final String ORGANIZATIONAL_PERSON = "organizationalPerson";
private static final String INET_ORG_PERSON = "inetOrgPerson";
private static final String TOP = "top";
private static final String PERSON = "person";
public static final String BASE_DN = "dc=primefactorsolutions,dc=com";
private final EmployeeRepository employeeRepository;
private final LdapTemplate ldapTemplate;
public static final String BASE_DN = "dc=primefactorsolutions,dc=com";
private final EntityManager entityManager;
protected Name buildDn(final Employee employee) {
return LdapNameBuilder.newInstance(BASE_DN)
.add("ou", "users")
.add("uid", employee.getUserName())
.add("uid", employee.getUsername())
.build();
}
public Employee getDetachedEmployeeByUsername(final String username) {
final Employee employee = employeeRepository.findByUsername(username).orElse(null);
if (employee != null) {
entityManager.detach(employee);
return employee;
}
return null;
}
public String getTeamLeadName(final UUID teamId) {
// Encuentra al empleado con el rol de lead_manager en el equipo especificado
Optional<Employee> leadManager = employeeRepository.findByTeamId(teamId);
return leadManager.map(employee -> employee.getFirstName() + " " + employee.getLastName())
.orElse("No asignado");
}
public List<Employee> findEmployees(
final int start, final int pageSize, final String sortProperty, final boolean asc) {
List<Employee> employees = employeeRepository.findAll();
@ -50,6 +76,10 @@ public class EmployeeService {
return employees.subList(start, end);
}
public Employee getEmployeeByPersonalEmail(final String email) {
return employeeRepository.findByPersonalEmail(email).orElse(null);
}
public Employee createOrUpdate(final Employee employee) {
if (employee.getId() == null) {
final Name dn = buildDn(employee);
@ -61,30 +91,39 @@ public class EmployeeService {
}
public Employee getEmployee(final UUID id) {
Optional<Employee> employee = employeeRepository.findById(id);
final Optional<Employee> employee = employeeRepository.findById(id);
return employee.orElse(null);
}
private Attributes buildAttributes(final Employee employee) {
final Attributes attrs = new BasicAttributes();
final BasicAttribute ocattr = new BasicAttribute("objectclass");
ocattr.add("top");
ocattr.add("person");
ocattr.add("organizationalPerson");
ocattr.add("inetOrgPerson");
final BasicAttribute ocattr = new BasicAttribute(OBJECTCLASS);
ocattr.add(TOP);
ocattr.add(PERSON);
ocattr.add(ORGANIZATIONAL_PERSON);
ocattr.add(INET_ORG_PERSON);
attrs.put(ocattr);
attrs.put("cn", String.format("%s %s", employee.getFirstName(), employee.getLastName()));
attrs.put("sn", String.format("%s %s", employee.getFirstName(), employee.getLastName()));
attrs.put("uid", employee.getUserName());
attrs.put("userpassword", String.format("%s%s", employee.getUserName(), 123));
attrs.put("uid", employee.getUsername());
attrs.put(USERPASSWORD, String.format("%s%s", employee.getUsername(), 123));
return attrs;
}
public void updatePassword(final Employee employee) {
final Attribute attr = new BasicAttribute("userpassword", employee.getUserName() + "123");
public void updatePassword(final Employee employee, final String newPassword) {
final Attribute attr = new BasicAttribute(USERPASSWORD, newPassword);
final ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr);
ldapTemplate.modifyAttributes(buildDn(employee), new ModificationItem[] {item});
}
public List<Employee> findAllEmployees() {
return employeeRepository.findAll();
}
public List<Employee> findEmployeesByTeam(final String teamName) {
return employeeRepository.findByTeamName(teamName);
}
}

View File

@ -0,0 +1,99 @@
package com.primefactorsolutions.service;
import com.primefactorsolutions.model.HoursWorked;
import com.primefactorsolutions.repositories.HoursWorkedRepository;
import org.apache.commons.beanutils.BeanComparator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.util.*;
@Service
public class HoursWorkedService {
private final HoursWorkedRepository hoursWorkedRepository;
@Autowired
public HoursWorkedService(final HoursWorkedRepository hoursWorkedRepository) {
this.hoursWorkedRepository = hoursWorkedRepository;
}
public List<HoursWorked> findAll() {
return hoursWorkedRepository.findAll();
}
public double getTotalHoursWorkedByEmployeeForWeek(final UUID employeeId, final int weekNumber) {
List<HoursWorked> hoursWorkedList = hoursWorkedRepository.findByWeekNumber(weekNumber);
return hoursWorkedList.stream()
.filter(hw -> hw.getEmployee().getId().equals(employeeId))
.mapToDouble(HoursWorked::getTotalHours)
.sum();
}
public HoursWorked findHoursWorked(final UUID id) {
Optional<HoursWorked> hoursWorked = hoursWorkedRepository.findById(id);
HoursWorked hw = hoursWorked.get();
return hw;
}
public HoursWorked saveHoursWorked(final HoursWorked hoursWorked) {
return hoursWorkedRepository.save(hoursWorked);
}
public HoursWorked save(final HoursWorked hoursWorked) {
return hoursWorkedRepository.save(hoursWorked);
}
public double getTotalHoursForEmployee(final UUID employeeId, final int weekNumber) {
List<HoursWorked> activities = hoursWorkedRepository.findByEmployeeIdAndWeekNumber(employeeId, weekNumber);
return HoursWorked.calculateTotalHours(activities);
}
public double getPendingHoursForEmployee(final UUID employeeId, final int weekNumber) {
List<HoursWorked> activities = hoursWorkedRepository.findByEmployeeIdAndWeekNumber(employeeId, weekNumber);
return HoursWorked.calculatePendingHours(activities);
}
public List<HoursWorked> findByWeekNumber(final int weekNumber) {
return hoursWorkedRepository.findByWeekNumber(weekNumber);
}
public List<HoursWorked> findByDate(final LocalDate date) {
return hoursWorkedRepository.findByDate(date);
}
public List<HoursWorked> findByDateAndWeekNumber(final LocalDate date, final int weekNumber) {
return hoursWorkedRepository.findByDate(date);
}
public List<HoursWorked> findHoursWorkeds(
final int start, final int pageSize, final String sortProperty, final boolean asc) {
List<HoursWorked> hoursWorkeds = hoursWorkedRepository.findAll();
int end = Math.min(start + pageSize, hoursWorkeds.size());
hoursWorkeds.sort(new BeanComparator<>(sortProperty));
if (!asc) {
Collections.reverse(hoursWorkeds);
}
return hoursWorkeds.subList(start, end);
}
public List<HoursWorked> findHoursWorkeds(final int start, final int pageSize) {
List<HoursWorked> hoursWorkeds = hoursWorkedRepository.findAll();
int end = Math.min(start + pageSize, hoursWorkeds.size());
return hoursWorkeds.subList(start, end);
}
public HoursWorked getHoursWorked(final UUID id) {
final Optional<HoursWorked> hoursWorked = hoursWorkedRepository.findById(id);
return hoursWorked.get();
}
public List<HoursWorked> findListHoursWorkedEmployee(final UUID employeeId, final int weekNumber) {
return hoursWorkedRepository.findByEmployeeIdAndWeekNumber(employeeId, weekNumber);
}
}

View File

@ -0,0 +1,177 @@
package com.primefactorsolutions.service;
import com.openhtmltopdf.pdfboxout.PdfBoxRenderer;
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import com.primefactorsolutions.repositories.HoursWorkedRepository;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
import lombok.SneakyThrows;
import org.apache.pdfbox.io.MemoryUsageSetting;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
@Service
public class ReportService {
private final HoursWorkedRepository hoursWorkedRepository;
public ReportService(final HoursWorkedRepository hoursWorkedRepository) {
this.hoursWorkedRepository = hoursWorkedRepository;
}
// Este método ahora solo crea el archivo Excel a partir de los datos que recibe.
public byte[] writeAsExcel(final String reportName, final List<String> headers,
final List<Map<String, Object>> data, final String selectedTeam,
final int weekNumber, final int currentYear)
throws IOException {
return createExcelFile(reportName, headers, data, selectedTeam, weekNumber, currentYear);
}
private byte[] createExcelFile(final String reportName, final List<String> headers,
final List<Map<String, Object>> data, final String selectedTeam,
final int weekNumber, final int currentYear)
throws IOException {
try (Workbook workbook = new XSSFWorkbook();
ByteArrayOutputStream os = new ByteArrayOutputStream()) {
Sheet sheet = workbook.createSheet(reportName);
// Crear encabezados
// Crear una fila para el rótulo "Reporte por equipo"
Row titleRow = sheet.createRow(0); // Fila 0 para el rótulo
Cell titleCell = titleRow.createCell(0);
// Concatenar el nombre del equipo al rótulo
String titleText = "Informe: " + weekNumber + "/" + currentYear;
titleCell.setCellValue(titleText);
// Estilo del rótulo
CellStyle titleStyle = workbook.createCellStyle();
Font titleFont = workbook.createFont();
titleFont.setBold(true);
titleFont.setFontHeightInPoints((short) 14); // Tamaño de la fuente
titleStyle.setFont(titleFont);
titleCell.setCellStyle(titleStyle);
// Fusionar celdas para el rótulo
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, headers.size() - 1)); // Ajusta el rango de celdas
// Crear filas adicionales con la información solicitada
Row asuntoRow = sheet.createRow(1); // Fila 1: Asunto
asuntoRow.createCell(0).setCellValue("Asunto: Informe semanal de horas trabajadas");
sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, headers.size() - 1));
Row semanaRow = sheet.createRow(2); // Fila 2: Semana
semanaRow.createCell(0).setCellValue("Semana: " + weekNumber); // Puedes insertar una fecha real aquí
sheet.addMergedRegion(new CellRangeAddress(2, 2, 0, headers.size() - 1));
Row horasCumplirRow = sheet.createRow(3); // Fila 3: Horas a cumplir
horasCumplirRow.createCell(0).setCellValue("Horas a cumplir: 40 horas"); // Puedes agregar las horas reales
sheet.addMergedRegion(new CellRangeAddress(3, 3, 0, headers.size() - 1));
Row teamLeadRow = sheet.createRow(4); // Fila 4: Team Lead
teamLeadRow.createCell(0).setCellValue("Team Lead: "); // Solo texto
sheet.addMergedRegion(new CellRangeAddress(4, 4, 0, headers.size() - 1));
// Crear encabezados (fila 5)
Row headerRow = sheet.createRow(5); // Los encabezados empiezan en la fila 5
CellStyle headerStyle = workbook.createCellStyle();
Font headerFont = workbook.createFont();
headerFont.setBold(true);
headerStyle.setFont(headerFont);
for (int i = 0; i < headers.size(); i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers.get(i));
cell.setCellStyle(headerStyle);
}
// Crear filas de datos (a partir de la fila 6)
for (int i = 0; i < data.size(); i++) {
Row dataRow = sheet.createRow(i + 6); // Los datos empiezan después de la fila de encabezados
Map<String, Object> rowData = data.get(i);
int cellIndex = 0;
for (String key : headers) {
Cell cell = dataRow.createCell(cellIndex++);
Object value = rowData.get(key);
if (value != null) {
if (value instanceof String) {
cell.setCellValue((String) value);
} else if (value instanceof Number) {
cell.setCellValue(((Number) value).doubleValue());
}
} else {
cell.setCellValue(""); // Manejo de valores nulos
}
}
}
workbook.write(os);
return os.toByteArray();
} catch (IOException e) {
System.err.println("Error al generar el archivo Excel: " + e.getMessage());
throw e; // Propagar la excepción después de registrarla
}
}
@SneakyThrows
public byte[] writeAsPdf(final String reportName, final Object model) {
try (var os = new ByteArrayOutputStream()) {
writeAsPdf(reportName, model, os);
return os.toByteArray();
}
}
@SneakyThrows
public void writeAsPdf(final String reportName, final Object model, final OutputStream out) {
var in = getTemplate(reportName);
final Configuration cfg = getConfiguration();
final Reader reader = new InputStreamReader(in);
final Template temp = new Template(reportName, reader, cfg);
var wrapper = new DefaultObjectWrapper(Configuration.VERSION_2_3_32);
ByteArrayOutputStream oo = new ByteArrayOutputStream();
Writer outTemplate = new OutputStreamWriter(oo);
temp.process(wrapper.wrap(model), outTemplate);
var builder = new PdfRendererBuilder();
builder.usePDDocument(new PDDocument(MemoryUsageSetting.setupMixed(1000000)));
builder.withHtmlContent(oo.toString(StandardCharsets.UTF_8), "/test");
builder.toStream(out);
try (PdfBoxRenderer pdfBoxRenderer = builder.buildPdfRenderer()) {
pdfBoxRenderer.layout();
pdfBoxRenderer.createPDF();
}
}
public static InputStream getTemplate(final String reportName) {
return ReportService.class.getResourceAsStream(String.format("/reports/%s.html", reportName));
}
@NotNull
private static Configuration getConfiguration() {
final Configuration cfg = new Configuration(Configuration.VERSION_2_3_32);
cfg.setDefaultEncoding("UTF-8");
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
cfg.setLogTemplateExceptions(false);
cfg.setWrapUncheckedExceptions(true);
cfg.setFallbackOnNullLoopVariable(false);
cfg.setSQLDateAndTimeTimeZone(TimeZone.getDefault());
return cfg;
}
}

View File

@ -0,0 +1,27 @@
package com.primefactorsolutions.service;
import com.primefactorsolutions.model.Team;
import com.primefactorsolutions.repositories.TeamRepository;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.UUID;
@Service
@AllArgsConstructor
public class TeamService {
private final TeamRepository teamRepository;
public void saveTeam(final Team newTeam) {
teamRepository.save(newTeam);
}
public void deleteTeam(final UUID id) {
teamRepository.deleteById(id);
}
public List<Team> findAllTeams() {
return teamRepository.findAll();
}
}

View File

@ -0,0 +1,88 @@
package com.primefactorsolutions.service;
import com.primefactorsolutions.model.*;
import com.primefactorsolutions.repositories.TimeOffRequestRepository;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
import java.util.Optional;
@Service
@AllArgsConstructor
public class TimeOffRequestService {
private final TimeOffRequestRepository timeOffRequestRepository;
public void saveTimeOffRequest(final TimeOffRequest newTimeOffRequest) {
timeOffRequestRepository.save(newTimeOffRequest);
}
public void deleteTimeOffRequestByEmployeeAndCategory(final UUID employeeId, final TimeOffRequestType category) {
timeOffRequestRepository.deleteByEmployeeIdAndCategory(employeeId, category);
}
public void saveAll(final List<TimeOffRequest> requests) {
timeOffRequestRepository.saveAll(requests);
}
public void deleteTimeOffRequest(final UUID id) {
timeOffRequestRepository.deleteById(id);
}
public List<TimeOffRequest> findAllTimeOffRequests() {
return timeOffRequestRepository.findAll();
}
public TimeOffRequest findTimeOffRequest(final UUID id) {
Optional<TimeOffRequest> timeOffRequest = timeOffRequestRepository.findById(id);
return timeOffRequest.orElse(null);
}
public List<TimeOffRequest> findRequestsByState(final TimeOffRequestStatus state) {
return timeOffRequestRepository.findByState(state);
}
public List<TimeOffRequest> findRequestsByEmployeeId(final UUID idEmployee) {
return timeOffRequestRepository.findByEmployeeId(idEmployee);
}
public Optional<TimeOffRequest> findByEmployeeAndState(final UUID employeeId, final TimeOffRequestStatus state) {
return timeOffRequestRepository.findByEmployeeIdAndState(employeeId, state);
}
public List<TimeOffRequest> findByEmployeeAndCategory(final UUID employeeId, final TimeOffRequestType category) {
return timeOffRequestRepository.findByEmployeeIdAndCategory(employeeId, category);
}
public void updateRequestStatuses() {
List<TimeOffRequest> requests = findAllTimeOffRequests();
LocalDate now = LocalDate.now();
LocalDate startOfYear = LocalDate.of(now.getYear(), 1, 1);
for (TimeOffRequest request : requests) {
if (request.getCategory() == TimeOffRequestType.VACACION_GESTION_ACTUAL && now.isEqual(startOfYear)) {
deleteTimeOffRequestByEmployeeAndCategory(
request.getEmployee().getId(),
TimeOffRequestType.VACACION_GESTION_ANTERIOR
);
request.setCategory(TimeOffRequestType.VACACION_GESTION_ANTERIOR);
}
if (request.getState() == TimeOffRequestStatus.APROBADO
|| request.getState() == TimeOffRequestStatus.EN_USO) {
LocalDate startDate = request.getStartDate();
LocalDate endDate = request.getEndDate();
if (now.isAfter(endDate)) {
request.setState(TimeOffRequestStatus.TOMADO);
} else if (now.isEqual(startDate) || (now.isAfter(startDate) && now.isBefore(endDate))) {
request.setState(TimeOffRequestStatus.EN_USO);
}
}
}
saveAll(requests);
}
}

View File

@ -0,0 +1,23 @@
package com.primefactorsolutions.service;
import com.primefactorsolutions.model.TimeOffRequestType;
import com.primefactorsolutions.model.Vacation;
import com.primefactorsolutions.repositories.VacationRepository;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@AllArgsConstructor
public class VacationService {
private final VacationRepository vacationRepository;
public Vacation findVacationByCategory(final TimeOffRequestType category) {
return vacationRepository.findByCategory(category);
}
public List<Vacation> findVacations() {
return vacationRepository.findAll();
}
}

View File

@ -37,7 +37,7 @@ public class AssessmentsListView extends Main {
final HorizontalLayout hl = new HorizontalLayout();
final Button addAssessment = new Button("Add Assessment");
addAssessment.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
this.getUI().get().navigate(AssessmentView.class, "new");
this.getUI().flatMap(ui -> ui.navigate(AssessmentView.class, "new"));
});
hl.add(addAssessment);
@ -51,7 +51,7 @@ public class AssessmentsListView extends Main {
grid.addComponentColumn((ValueProvider<Assessment, Component>) assessment -> {
var result = new Button("Result", event ->
this.getUI().get().navigate(SubmissionView.class, assessment.getId().toString())
this.getUI().flatMap(ui -> ui.navigate(SubmissionView.class, assessment.getId().toString()))
);
result.setEnabled(assessment.isCompleted());
@ -95,6 +95,7 @@ public class AssessmentsListView extends Main {
return assessmentService.getAssessments().size();
}
@SuppressWarnings("unused")
@Override
public Stream<Assessment> fetch(final Query<Assessment, Object> query) {
int limit = query.getLimit();

View File

@ -0,0 +1,16 @@
package com.primefactorsolutions.views;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import lombok.Getter;
@Getter
public class BaseView extends Main {
private final VerticalLayout currentPageLayout;
public BaseView() {
currentPageLayout = new VerticalLayout();
add(currentPageLayout);
}
}

View File

@ -28,15 +28,13 @@ import java.util.stream.Stream;
@Route(value = "/candidates", layout = MainLayout.class)
@PermitAll
public class CandidatesListView extends Main {
private final CandidateService candidateService;
public CandidatesListView(final CandidateService candidateService) {
this.candidateService = candidateService;
final HorizontalLayout hl = new HorizontalLayout();
final Button addCandidate = new Button("Add Candidate");
addCandidate.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
this.getUI().get().navigate(CandidateView.class, "new");
this.getUI().flatMap(ui -> ui.navigate(CandidateView.class, "new"));
});
hl.add(addCandidate);
@ -46,7 +44,7 @@ public class CandidatesListView extends Main {
grid.addComponentColumn((ValueProvider<Candidate, Component>) candidate -> {
final Button edit = new Button("Edit");
edit.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent ->
this.getUI().get().navigate(CandidateView.class, candidate.getId().toString()));
this.getUI().flatMap(ui -> ui.navigate(CandidateView.class, candidate.getId().toString())));
return edit;
});
@ -61,6 +59,7 @@ public class CandidatesListView extends Main {
return candidateService.getCandidates().size();
}
@SuppressWarnings("unused")
@Override
public Stream<Candidate> fetch(final Query<Candidate, Object> query) {
int limit = query.getLimit();

View File

@ -0,0 +1,5 @@
package com.primefactorsolutions.views;
public class Constants {
public static final int PAGE_SIZE = 10;
}

View File

@ -1,16 +0,0 @@
package com.primefactorsolutions.views;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("CorporateDocuments")
@Route(value = "/corporate-documents/me", layout = MainLayout.class)
public class CorporateDocumentsView extends Main {
}

View File

@ -0,0 +1,273 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Document;
import com.primefactorsolutions.model.DocumentType;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.service.DocumentService;
import com.primefactorsolutions.service.EmployeeService;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.upload.Upload;
import com.vaadin.flow.component.upload.receivers.MemoryBuffer;
import com.vaadin.flow.router.*;
import com.vaadin.flow.server.StreamRegistration;
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.spring.annotation.SpringComponent;
import elemental.json.Json;
import elemental.json.JsonArray;
import elemental.json.JsonObject;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
import org.springframework.security.core.userdetails.UserDetails;
import org.vaadin.firitin.form.BeanValidationForm;
import com.vaadin.flow.spring.security.AuthenticationContext;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.List;
import java.util.UUID;
import java.io.InputStream;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("Document")
@Route(value = "/documents/:documentId?/:action?", layout = MainLayout.class)
public class DocumentView extends BeanValidationForm<Document> implements HasUrlParameter<String> {
private final TextField fileName = new TextField("Document Name");
private final ComboBox<DocumentType> documentType = new ComboBox<>("Document Type");
private final ComboBox<Employee> employeeComboBox = new ComboBox<>("Employee");
private final MemoryBuffer buffer = new MemoryBuffer();
private final Upload uploadButton = new Upload(buffer);
private final DocumentService documentService;
private final EmployeeService employeeService;
private final AuthenticationContext authContext;
private boolean fileUploaded = false;
private Button saveButton;
private Button viewDocumentButton;
public DocumentView(final DocumentService documentService,
final EmployeeService employeeService,
final AuthenticationContext authContext) {
super(Document.class);
this.documentService = documentService;
this.employeeService = employeeService;
this.authContext = authContext;
initializeView();
}
private void initializeView() {
configureComponents();
configureUploadButton();
}
protected Button createSaveButton() {
saveButton = new Button("Save");
saveButton.addClickListener(event -> saveDocument());
return saveButton;
}
protected Button createCloseButton() {
Button closeButton = new Button("Close");
closeButton.addClickListener(event -> closeForm());
return closeButton;
}
protected Button createViewDocumentButton() {
viewDocumentButton = new Button("View Document");
viewDocumentButton.setEnabled(false);
viewDocumentButton.addClickListener(event -> viewDocument());
return viewDocumentButton;
}
private void setFileNameProperties() {
fileName.setWidthFull();
}
private void setDocumentTypeProperties() {
documentType.setItems(DocumentType.values());
documentType.setWidthFull();
}
private void setEmployeeComboBoxProperties() {
List<Employee> employees = employeeService.findAllEmployees();
employeeComboBox.setItems(employees);
employeeComboBox.setItemLabelGenerator(employee -> employee.getFirstName() + " " + employee.getLastName());
employeeComboBox.setWidthFull();
}
private void setDocumentCreator(final Document document) {
authContext.getAuthenticatedUser(UserDetails.class).ifPresent(user -> {
document.setCreator(user.getUsername());
});
}
private void setFieldsReadOnly(final boolean option) {
fileName.setReadOnly(option);
documentType.setReadOnly(option);
employeeComboBox.setReadOnly(option);
}
private void viewDocument() {
StreamResource resource;
try {
InputStream inputStream = buffer.getInputStream();
if (inputStream != null && inputStream.available() > 0) {
resource = new StreamResource(fileName.getValue(), () -> new ByteArrayInputStream(readFileData()));
} else {
byte[] fileData = getEntity().getFileData();
resource = new StreamResource(fileName.getValue(), () -> new ByteArrayInputStream(fileData));
}
resource.setContentType("application/pdf");
getUI().ifPresent(ui -> {
StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource);
ui.getPage().open(registration.getResourceUri().toString());
});
} catch (IOException e) {
Notification.show("Error reading file.");
}
}
private void navigateToDocumentsListView() {
getUI().ifPresent(ui -> ui.navigate(DocumentsListView.class));
}
private void saveDocument() {
if (isFormValid()) {
Document document = getEntity();
document.setFileName(fileName.getValue());
document.setDocumentType(documentType.getValue());
document.setEmployee(employeeComboBox.getValue());
document.setFileData(readFileData());
setDocumentCreator(document);
documentService.saveDocument(document);
Notification.show("File saved successfully.");
clearForm();
} else {
Notification.show("Save failed: Please complete all fields and upload a file.");
}
}
private void closeForm() {
navigateToDocumentsListView();
}
private boolean isFormValid() {
return !fileName.isEmpty()
&& documentType.getValue() != null
&& employeeComboBox.getValue() != null
&& fileUploaded;
}
private void clearForm() {
fileName.clear();
documentType.clear();
employeeComboBox.clear();
fileUploaded = false;
uploadButton.getElement().setPropertyJson("files", Json.createArray());
viewDocumentButton.setEnabled(false);
}
private byte[] readFileData() {
try {
return buffer.getInputStream().readAllBytes();
} catch (IOException e) {
Notification.show("Error reading file data.");
return new byte[0];
}
}
private void preLoadFile(final Document document) {
JsonArray jsonArray = Json.createArray();
JsonObject jsonObject = Json.createObject();
jsonObject.put("name", document.getFileName());
jsonObject.put("progress", 100);
jsonObject.put("complete", true);
jsonObject.put("fileData", Base64.getEncoder().encodeToString(document.getFileData()));
jsonArray.set(0, jsonObject);
uploadButton.getElement().setPropertyJson("files", jsonArray);
fileUploaded = true;
}
private void updateSaveButtonState() {
boolean isModified = !fileName.getValue().equals(getEntity().getFileName())
|| documentType.getValue() != getEntity().getDocumentType()
|| employeeComboBox.getValue() != getEntity().getEmployee()
|| fileUploaded;
saveButton.setEnabled(isModified);
}
private void configureComponents() {
setFileNameProperties();
setDocumentTypeProperties();
setEmployeeComboBoxProperties();
fileName.addValueChangeListener(e -> updateSaveButtonState());
documentType.addValueChangeListener(e -> updateSaveButtonState());
employeeComboBox.addValueChangeListener(e -> updateSaveButtonState());
uploadButton.addSucceededListener(e -> updateSaveButtonState());
uploadButton.getElement().addEventListener("file-remove", event -> updateSaveButtonState());
}
private void configureUploadButton() {
uploadButton.setMaxFiles(1);
uploadButton.setAcceptedFileTypes(".pdf");
uploadButton.addSucceededListener(event -> {
fileUploaded = true;
Notification.show("File uploaded successfully.");
viewDocumentButton.setEnabled(true);
updateSaveButtonState();
});
uploadButton.getElement().addEventListener("file-remove", event -> {
fileUploaded = false;
Notification.show("File removed.");
viewDocumentButton.setEnabled(false);
updateSaveButtonState();
});
}
private void configureViewOrEditAction(final String action, final String documentIdString) {
if ("edit".equals(action) && !documentIdString.isEmpty()) {
setFieldsReadOnly(false);
preLoadFile(getEntity());
viewDocumentButton.setEnabled(true);
} else if ("view".equals(action) && !documentIdString.isEmpty()) {
setFieldsReadOnly(true);
preLoadFile(getEntity());
saveButton.setEnabled(false);
viewDocumentButton.setEnabled(true);
}
}
@Override
public void setParameter(final BeforeEvent beforeEvent, final String action) {
final RouteParameters params = beforeEvent.getRouteParameters();
final String documentIdString = params.get("documentId").orElse(null);
if ("new".equals(action)) {
setEntityWithEnabledSave(new Document());
} else {
assert documentIdString != null;
UUID documentId = UUID.fromString(documentIdString);
Document document = documentService.getDocument(documentId);
setEntity(document);
employeeComboBox.setValue(document.getEmployee());
preLoadFile(document);
configureViewOrEditAction(action, documentIdString);
}
}
@Override
protected List<Component> getFormComponents() {
HorizontalLayout buttonLayout = new HorizontalLayout();
buttonLayout.add(uploadButton, createViewDocumentButton());
buttonLayout.setSpacing(true);
return List.of(fileName, documentType, employeeComboBox, buttonLayout, createCloseButton());
}
}

View File

@ -0,0 +1,211 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Document;
import com.primefactorsolutions.model.DocumentType;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.service.DocumentService;
import com.primefactorsolutions.service.EmployeeService;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.grid.GridSortOrder;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.data.provider.SortDirection;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.StreamRegistration;
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.grid.PagingGrid;
import java.io.ByteArrayInputStream;
import java.util.List;
import static com.primefactorsolutions.views.Constants.PAGE_SIZE;
@SpringComponent
@Scope("prototype")
@PageTitle("Documents")
@Route(value = "/documents", layout = MainLayout.class)
@PermitAll
public class DocumentsListView extends BaseView {
private final DocumentService documentService;
private final EmployeeService employeeService;
private final PagingGrid<Document> documentGrid = new PagingGrid<>(Document.class);
private ComboBox<Employee> employeeFilter;
private ComboBox<DocumentType> documentTypeFilter;
public DocumentsListView(final DocumentService documentService, final EmployeeService employeeService) {
this.documentService = documentService;
this.employeeService = employeeService;
initializeView();
updateDocumentGrid(null, null);
}
private void initializeView() {
getCurrentPageLayout().add(createActionButton("Add Document", this::navigateToAddDocumentView));
final HorizontalLayout hl = new HorizontalLayout();
hl.add(createDocumentTypeFilter());
hl.add(createEmployeeFilter());
getCurrentPageLayout().add(hl);
configureDocumentGrid();
getCurrentPageLayout().add(documentGrid);
}
private void configureDocumentGrid() {
documentGrid.setColumns("fileName", "documentType", "creator");
documentGrid.addComponentColumn(this::createEmployeeSpan).setHeader("Employee");
addActionColumns();
configurePagination();
}
private Span createEmployeeSpan(final Document document) {
Employee employee = document.getEmployee();
String employeeName = employee.getFirstName() + " " + employee.getLastName();
return new Span(employeeName);
}
private void addActionColumns() {
addDocumentActionColumn("View", this::navigateToDocumentView);
addDocumentActionColumn("Edit", this::navigateToEditDocumentView);
addDocumentActionColumn("Download", this::downloadDocument);
}
private void addDocumentActionColumn(final String label, final DocumentActionHandler handler) {
documentGrid.addComponentColumn(document -> createActionButton(label, () -> handler.handle(document)));
}
private Button createActionButton(final String label, final Runnable onClickAction) {
Button actionButton = new Button(label);
actionButton.addClickListener(event -> onClickAction.run());
return actionButton;
}
private ComboBox<DocumentType> createDocumentTypeFilter() {
documentTypeFilter = new ComboBox<>("Document Type");
documentTypeFilter.setItems(DocumentType.values());
documentTypeFilter.setValue(DocumentType.values()[0]);
documentTypeFilter.addValueChangeListener(event -> {
updateDocumentGrid(event.getValue(), employeeFilter.getValue());
});
return documentTypeFilter;
}
private ComboBox<Employee> createEmployeeFilter() {
employeeFilter = new ComboBox<>("Employee");
List<Employee> employees = employeeService.findAllEmployees();
employees.addFirst(createAllEmployeesOption());
employeeFilter.setItems(employees);
employeeFilter.setItemLabelGenerator(this::getEmployeeLabel);
employeeFilter.setValue(employees.getFirst());
employeeFilter.addValueChangeListener(event -> {
updateDocumentGrid(documentTypeFilter.getValue(), event.getValue());
});
return employeeFilter;
}
private Employee createAllEmployeesOption() {
Employee allEmployeesOption = new Employee();
allEmployeesOption.setFirstName("All");
return allEmployeesOption;
}
private String getEmployeeLabel(final Employee employee) {
return employee.getFirstName().equals("All") ? "All" : employee.getFirstName() + " " + employee.getLastName();
}
private void navigateToEditDocumentView(final Document document) {
navigateToDocumentView(document, "edit");
}
private void navigateToDocumentView(final Document document) {
navigateToDocumentView(document, "view");
}
private void navigateToDocumentView(final Document document, final String action) {
getUI().ifPresent(ui -> ui.navigate(DocumentView.class, document.getId().toString() + "/" + action));
}
private void navigateToAddDocumentView() {
getUI().ifPresent(ui -> ui.navigate(DocumentView.class, "new"));
}
private void configurePagination() {
documentGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
documentGrid.setPageSize(PAGE_SIZE);
}
private void updateDocumentGrid(final DocumentType documentType, final Employee employee) {
DocumentType finalDocumentType = isValidDocumentType(documentType) ? documentType : null;
Employee finalEmployee = isValidEmployee(employee) ? employee : null;
documentGrid.setPagingDataProvider((page, pageSize) ->
(finalDocumentType == null && finalEmployee == null)
? fetchDocuments((int) page, pageSize)
: fetchFilteredDocuments((int) page, pageSize, finalDocumentType, finalEmployee)
);
documentGrid.getDataProvider().refreshAll();
}
private boolean isValidDocumentType(final DocumentType documentType) {
return documentType != null && !"All".equals(documentType.name());
}
private boolean isValidEmployee(final Employee employee) {
return employee != null && !"All".equals(employee.getFirstName());
}
private List<Document> fetchFilteredDocuments(final int page,
final int pageSize,
final DocumentType documentType,
final Employee employee) {
return documentService.findDocumentBy(documentType, employee, page, pageSize);
}
private List<Document> fetchDocuments(final int page, final int size) {
int startIndex = page * size;
return isSortOrderPresent()
? fetchSortedDocuments(startIndex, size)
: documentService.findDocuments(startIndex, size);
}
private boolean isSortOrderPresent() {
return !documentGrid.getSortOrder().isEmpty();
}
private List<Document> fetchSortedDocuments(final int start, final int pageSize) {
GridSortOrder<Document> sortOrder = documentGrid.getSortOrder().getFirst();
return documentService.findDocuments(start, pageSize,
sortOrder.getSorted().getKey(),
sortOrder.getDirection() == SortDirection.ASCENDING);
}
private void downloadDocument(final Document document) {
StreamResource resource = createDocumentStreamResource(document);
getUI().ifPresent(ui -> openDocumentStream(resource, ui));
}
private StreamResource createDocumentStreamResource(final Document document) {
StreamResource resource = new StreamResource(document.getFileName(),
() -> new ByteArrayInputStream(document.getFileData()));
resource.setContentType("application/pdf");
resource.setHeader("Content-Disposition", "attachment; filename=\"" + document.getFileName() + ".pdf\"");
return resource;
}
private void openDocumentStream(final StreamResource resource, final UI ui) {
StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource);
ui.getPage().open(registration.getResourceUri().toString());
}
@FunctionalInterface
private interface DocumentActionHandler {
void handle(Document document);
}
}

View File

@ -1,17 +0,0 @@
package com.primefactorsolutions.views;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("Documents")
@Route(value = "/documents/me", layout = MainLayout.class)
public class DocumentsView extends Main {
}

View File

@ -1,15 +1,25 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.model.Team;
import com.primefactorsolutions.model.*;
import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.ReportService;
import com.primefactorsolutions.service.TeamService;
import com.primefactorsolutions.service.TimeOffRequestService;
import com.vaadin.componentfactory.pdfviewer.PdfViewer;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.html.Image;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.EmailField;
@ -18,14 +28,18 @@ import com.vaadin.flow.component.upload.Upload;
import com.vaadin.flow.component.upload.receivers.MemoryBuffer;
import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.router.*;
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.datepicker.VDatePicker;
import org.vaadin.firitin.form.BeanValidationForm;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Base64;
import java.util.List;
import java.util.UUID;
@ -37,51 +51,113 @@ import java.util.UUID;
@Route(value = "/employees/:employeeId?/:action?", layout = MainLayout.class)
public class EmployeeView extends BeanValidationForm<Employee> implements HasUrlParameter<String> {
private final EmployeeService employeeService;
private final ReportService reportService;
private final TimeOffRequestService requestService;
private final TeamService teamService;
// TODO: campo usado para registrar al empleado en LDAP. Este campo podria estar en otro form eventualmente.
private final TextField username = createTextField("Username: ", 30, true);
private final TextField firstName = createTextField("Nombres: ", 30, true);
private final TextField lastName = createTextField("Apellidos", 30, true);
private final ComboBox<Employee.Status> status = createStatusComboBox();
private final ComboBox<Employee.Gender> gender = createGenderComboBox();
private final VDatePicker birthday = new VDatePicker("Fecha de Nacimiento");
private final TextField age = createTextField("Edad", 3, false);
private final TextField birthCity = createTextField("Ciudad y País de Nacimiento ejemplo: (Ciudad, País) ",
30, false);
private final TextField residenceAddress = createTextField("Dirección de Residencia", 50, false);
private final TextField localAddress = createTextField("Departamento y Provincia de Residencia "
+ " ejemplo: (Departamento-Provincia)", 30, false);
private final ComboBox<Employee.MaritalStatus> maritalStatus = createMaritalStatusComboBox();
private final TextField numberOfChildren = createTextField("Numero de Hijos", 2, false);
private final TextField ci = createTextField("CI", 10, false);
private final TextField issuedIn = createTextField("Expedido en ", 10, false);
private final TextField phoneNumber = createTextField("Teléfono", 8, false);
private final EmailField personalEmail = createEmailField("E-mail ejemplo: (ejemplo@gmail.com)");
private final TextField emergencyCName = createTextField("Nombres y Apellidos de Contacto", 50, false);
private final TextField emergencyCAddress = createTextField("Dirección de Contacto", 50, false);
private final TextField emergencyCPhone = createTextField("Teléfono de Contacto", 8, false);
private final EmailField emergencyCEmail = createEmailField("Email de Contacto ejemplo: (ejemplo@gmail.com)");
private final MemoryBuffer buffer = new MemoryBuffer();
private final Upload upload = new Upload(buffer);
private final Image profileImagePreview = new Image();
//INFORMACION PROFESIONAL
private final TextField pTitle1 = createTextField("Título 1", 30, false);
private final TextField pTitle2 = createTextField("Título 2", 30, false);
private final TextField pTitle3 = createTextField("Título 3", 30, false);
private final TextField pStudy1 = createTextField("Estudio 1", 30, false);
private final TextField pStudy2 = createTextField("Estudio 2", 30, false);
private final TextField pStudy3 = createTextField("Estudio 3", 30, false);
private final TextField certification1 = createTextField("Certificación 1", 30, false);
private final TextField certification2 = createTextField("Certificación 2", 30, false);
private final TextField certification3 = createTextField("Certificación 3", 30, false);
private final TextField certification4 = createTextField("Certificación 4", 30, false);
private final TextField recognition = createTextField("Reconocimientos", 30, false);
private final TextField achievements = createTextField("Logros Profesionales", 30, false);
private final TextField language = createTextField("Idioma", 50, false);
private final TextField languageLevel = createTextField("Nivel de Idioma", 30, false);
//INFORMACION ADMINISTRATIVA
private final TextField cod = createTextField("Codigo de Empleado", 20, false);
private final TextField position = createTextField("Cargo", 30, false);
private final ComboBox<Team> team = new ComboBox<>("Equipo");
private final TextField leadManager = createTextField("Lead/Manager", 30, false);
private final TextField project = createTextField("Proyecto", 30, false);
private final VDatePicker dateOfEntry = new VDatePicker("Fecha de Ingreso");
private final VDatePicker dateOfExit = new VDatePicker("Fecha de Retiro");
private final TextField contractType = createTextField("Tipo de Contratación", 30, false);
private final TextField seniority = createTextField("Antiguedad", 30, false);
private final TextField salary = createTextField("Salario", 30, false);
private final TextField bankName = createTextField("Banco", 30, false);
private final TextField accountNumber = createTextField("Nro. de Cuenta", 30, false);
private final TextField gpss = createTextField("Código Único de Asegurado (GPSS)", 30, false);
private final TextField sss = createTextField("Matricula de Asegurado (SSS)", 30, false);
private final TextField beneficiaries = createTextField("Derechohabientes", 30, false);
private static final String SAVE_BUTTON_TEXT = "Save";
private static final String EDIT_BUTTON_TEXT = "Edit";
private static final String NOTIFICATION_SAVE_SUCCESS = "Employee saved successfully.";
private static final String NOTIFICATION_VALIDATE_ERROR = "Please complete the required fields correctly.";
private static final String PHONE_NUMBER_ERROR_MESSAGE = "El teléfono debe contener solo números.";
private final EmployeeService employeeService;
// TODO: campo usado para registrar al empleado en LDAP. Este campo podria estar en otro form eventualmente.
private final TextField userName = createTextField("Username: ", 30, true);
private final TextField firstName = createTextField("Nombres: ", 30, true);
private final TextField lastName = createTextField("Apellidos", 30, true);
private final ComboBox<Employee.Status> status = createStatusComboBox();
private final VDatePicker birthday = new VDatePicker("Fecha de Nacimiento");
private final TextField birthCity = createTextField("Ciudad y País de Nacimiento", 20, false);
private final ComboBox<Employee.MaritalStatus> maritalStatus = createMaritalStatusComboBox();
private final TextField residenceAddress = createTextField("Dirección de Residencia", 50, false);
private final TextField phoneNumber = createTextField("Teléfono", 8, false);
private final EmailField personalEmail = createEmailField("E-mail");
private final TextField position = createTextField("Cargo", 30, false);
private final TextField team = createTextField("Equipo", 30, false);
private final TextField emergencyCName = createTextField("Nombres y Apellidos de Contacto", 50, false);
private final TextField emergencyCAddress = createTextField("Dirección de Contacto", 50, false);
private final TextField emergencyCPhone = createTextField("Teléfono de Contacto", 8, false);
private final EmailField emergencyCEmail = createEmailField("Email de Contacto");
private final MemoryBuffer buffer = new MemoryBuffer();
private final Upload upload = new Upload(buffer);
private final Image profileImagePreview = new Image();
private final Button saveButton = new Button(SAVE_BUTTON_TEXT, e -> saveEmployee());
private final Button editButton = new Button(EDIT_BUTTON_TEXT, e -> enableEditMode());
private final Button reportButton = new Button("Generar Ficha");
private final Dialog dialog = new Dialog();
private final PdfViewer pdfViewer = new PdfViewer();
private final H2 mt = new H2("Información General del Empleado");
private final H3 fs = new H3("Información Personal");
private final H3 ss = new H3("Datos de Contacto de Emergencia");
private final H3 si = new H3("Foto del Empleado");
//TITULOS PARA INFORMACION PERSONAL
private final H2 infoPer = new H2("Información Personal");
private final H3 infoGenr = new H3("Información General");
private final H3 contEmerg = new H3("Contacto de Emergencia");
//TITULOS PARA INFORMACIÓN PROFESIONAL
private final H2 infProf = new H2("Información Profesional");
private final H3 titulos = new H3("Titulos Profesionales y Estudios Realizados");
private final H3 certif = new H3("Certificaciones Profesionales");
private final H3 logros = new H3("Otros Logros y Reconocimientos");
private final H3 idioma = new H3("Dominio de Idiomas");
//TITULOS PARA INFORMACIÓN ADMINISTRATIVA
private final H2 infoAdm = new H2("Información Administrativa");
private final H3 infoCont = new H3("Información de Contratación");
private final H3 datBanc = new H3("Datos Bancarios");
private final H3 datGest = new H3("Datos Gestora Pública y Seguro Social");
public EmployeeView(final EmployeeService employeeService) {
public EmployeeView(final EmployeeService employeeService,
final ReportService reportService,
final TeamService teamService,
final TimeOffRequestService requestService) {
super(Employee.class);
this.employeeService = employeeService;
this.reportService = reportService;
this.requestService = requestService;
this.teamService = teamService;
saveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
configureComponents();
assembleLayout();
addClassName("main-layout");
}
private void configureComponents() {
@ -89,9 +165,70 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
phoneNumber.addValueChangeListener(e -> validatePhoneNumber(phoneNumber, e.getValue()));
emergencyCPhone.setValueChangeMode(ValueChangeMode.EAGER);
emergencyCPhone.addValueChangeListener(e -> validatePhoneNumber(emergencyCPhone, e.getValue()));
firstName.setValueChangeMode(ValueChangeMode.EAGER);
firstName.addValueChangeListener(e -> validateNameField(firstName, e.getValue()));
lastName.setValueChangeMode(ValueChangeMode.EAGER);
lastName.addValueChangeListener(e -> validateNameField(lastName, e.getValue()));
createTeamComboBox();
configureUpload();
saveButton.setVisible(true);
editButton.setVisible(true);
reportButton.setVisible(true);
birthday.addValueChangeListener(event -> calculateAge());
birthday.setMax(java.time.LocalDate.now());
dateOfEntry.addValueChangeListener(event -> calculateSeniority());
dateOfEntry.addValueChangeListener(event -> calculateSeniority());
reportButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
var employee = getEntity();
byte[] pdfContent = reportService.writeAsPdf("ficha", employee);
var resource = new StreamResource("ficha.pdf", () -> new ByteArrayInputStream(pdfContent));
pdfViewer.setSrc(resource);
dialog.open();
});
initDialog();
}
private void validateNameField(final TextField textField, final String value) {
if (!value.matches("^[a-zA-ZáéíóúÁÉÍÓÚñÑ\\s]*$")) {
textField.setInvalid(true);
textField.setErrorMessage("Este campo solo debe contener letras.");
} else {
textField.setInvalid(false);
}
}
private void calculateAge() {
if (birthday.getValue() != null) {
int currentYear = java.time.LocalDate.now().getYear();
int birthYear = birthday.getValue().getYear();
int ages = currentYear - birthYear;
age.setValue(String.valueOf(ages));
if (ages < 18) {
birthday.setInvalid(true);
birthday.setErrorMessage("La edad no puede ser menor a 18 años.");
Notification.show("La edad ingresada no es válida, debe ser mayor o igual a 18 años.");
} else {
birthday.setInvalid(false);
}
System.out.println(age);
}
}
private void calculateSeniority() {
LocalDate entryDate = dateOfEntry.getValue();
LocalDate exitDate = dateOfExit.getValue() != null ? dateOfExit.getValue() : LocalDate.now();
if (entryDate != null) {
long yearsOfService = ChronoUnit.YEARS.between(entryDate, exitDate);
String seniorityValue = yearsOfService + " años ";
seniority.setValue(seniorityValue);
} else {
seniority.setValue("No disponible");
}
}
private void configureUpload() {
@ -105,11 +242,15 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
getEntity().setProfileImage(base64Image);
profileImagePreview.setSrc("data:image/jpeg;base64," + base64Image);
profileImagePreview.setSrc("data:image/png;base64," + base64Image);
profileImagePreview.setMaxWidth("150px");
profileImagePreview.setMaxHeight("150px");
} catch (IOException e) {
Notification.show("Error al subir la imagen.");
Notification.show("Error al subir la imagen: " + e.getMessage());
e.printStackTrace();
} catch (Exception e) {
Notification.show("Error en el servidor al procesar la imagen.");
e.printStackTrace();
}
});
}
@ -120,32 +261,27 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
}
}
private void assembleLayout() {
HorizontalLayout mainLayout = new HorizontalLayout();
VerticalLayout contentLayout1 = createContentLayout();
VerticalLayout contentLayout2 = createContentLayout();
private void initDialog() {
pdfViewer.setSizeFull();
H2 headline = new H2("Ficha Empleado");
headline.getStyle().set("margin", "var(--lumo-space-m) 0 0 0")
.set("font-size", "1.5em").set("font-weight", "bold");
contentLayout1.add(
mt,
fs,
firstName,
lastName,
status,
birthday,
birthCity,
maritalStatus,
residenceAddress,
phoneNumber,
personalEmail,
position,
team);
contentLayout2.add(
ss, emergencyCName, emergencyCAddress,
emergencyCPhone, emergencyCEmail, si, upload,
profileImagePreview, saveButton, editButton);
final Button cancelDialogButton = new Button("Close", e -> dialog.close());
final HorizontalLayout buttonLayout = new HorizontalLayout(cancelDialogButton);
buttonLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.END);
mainLayout.add(contentLayout1, contentLayout2);
addClassName("main-layout");
final VerticalLayout dialogLayout = new VerticalLayout(headline, pdfViewer, buttonLayout);
dialogLayout.getStyle().set("height", "100%");
dialogLayout.getStyle().set("overflow", "hidden");
dialogLayout.getStyle().set("display", "flex");
dialogLayout.getStyle().set("flex-direction", "column");
dialogLayout.setPadding(false);
dialogLayout.setAlignItems(FlexComponent.Alignment.STRETCH);
dialogLayout.getStyle().set("width", "700px").set("max-width", "100%");
dialogLayout.getStyle().set("height", "800px").set("max-height", "100%");
dialog.add(dialogLayout);
}
private ComboBox<Employee.MaritalStatus> createMaritalStatusComboBox() {
@ -159,7 +295,7 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
ComboBox<Employee.Status> comboBox = new ComboBox<>("Estado");
comboBox.setItems(Employee.Status.values());
comboBox.setItemLabelGenerator(Employee.Status::name);
comboBox.setRequiredIndicatorVisible(true); // Indicador de campo requerido
comboBox.setRequiredIndicatorVisible(true);
return comboBox;
}
@ -184,6 +320,13 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
return emailField;
}
private void createTeamComboBox() {
List<Team> teams = teamService.findAllTeams();
team.setItems(teams);
team.setItemLabelGenerator(Team::getName);
team.setWidthFull();
}
private <T> ComboBox<T> createComboBox(final String label, final T[] items) {
ComboBox<T> comboBox = new ComboBox<>(label);
comboBox.setItems(items);
@ -192,14 +335,44 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
return comboBox;
}
private ComboBox<Employee.Gender> createGenderComboBox() {
ComboBox<Employee.Gender> comboBox = new ComboBox<>("Genero");
comboBox.setItems(Employee.Gender.values());
comboBox.setItemLabelGenerator(Employee.Gender::name);
comboBox.setRequiredIndicatorVisible(true);
return comboBox;
}
private boolean validateForm() {
return !firstName.isEmpty() && !lastName.isEmpty() && status.getValue() != null;
}
private void setVacationDuration(
final Employee employee,
final TimeOffRequest request,
final LocalDate referenceDate) {
double yearsOfService = ChronoUnit.YEARS.between(employee.getDateOfEntry(), referenceDate);
request.setAvailableDays(calculateAvailableDays(yearsOfService));
}
private double calculateAvailableDays(final double yearsOfService) {
if (yearsOfService > 10) {
return 30.0;
} else if (yearsOfService > 5) {
return 20.0;
} else if (yearsOfService > 1) {
return 15.0;
} else {
return 0.0;
}
}
private void saveEmployee() {
if (validateForm()) {
Employee employee = getEntity();
employee.setStatus(status.getValue());
employee.setAge(age.getValue());
employeeService.createOrUpdate(employee);
Notification.show(NOTIFICATION_SAVE_SUCCESS);
getUI().ifPresent(ui -> ui.navigate(EmployeesListView.class));
@ -214,7 +387,6 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
editButton.setVisible(false);
}
@Override
public void setParameter(final BeforeEvent beforeEvent, final String action) {
final RouteParameters params = beforeEvent.getRouteParameters();
@ -249,24 +421,27 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
if (employee.getProfileImage() != null && !employee.getProfileImage().isEmpty()) {
profileImagePreview.setSrc("data:image/jpeg;base64," + employee.getProfileImage());
profileImagePreview.setVisible(true);
profileImagePreview.setMaxWidth("150px");
profileImagePreview.setMaxHeight("150px");
profileImagePreview.setMaxWidth("250px");
profileImagePreview.setMaxHeight("250px");
upload.setVisible(false);
upload.setVisible(true);
} else {
profileImagePreview.setVisible(false);
profileImagePreview.setVisible(true);
upload.setVisible(true);
}
}
private void setFieldsReadOnly() {
username.setReadOnly(false);
firstName.setReadOnly(true);
lastName.setReadOnly(true);
status.setReadOnly(true);
birthday.setReadOnly(true);
birthCity.setReadOnly(true);
maritalStatus.setReadOnly(true);
residenceAddress.setReadOnly(true);
localAddress.setReadOnly(true);
maritalStatus.setReadOnly(true);
numberOfChildren.setReadOnly(true);
phoneNumber.setReadOnly(true);
personalEmail.setReadOnly(true);
position.setReadOnly(true);
@ -277,16 +452,51 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
emergencyCEmail.setReadOnly(true);
upload.setVisible(true);
profileImagePreview.setVisible(true);
age.setReadOnly(true);
gender.setReadOnly(true);
status.setReadOnly(true);
ci.setReadOnly(true);
issuedIn.setReadOnly(true);
pTitle1.setReadOnly(true);
pTitle2.setReadOnly(true);
pTitle3.setReadOnly(true);
pStudy1.setReadOnly(true);
pStudy2.setReadOnly(true);
pStudy3.setReadOnly(true);
certification1.setReadOnly(true);
certification2.setReadOnly(true);
certification3.setReadOnly(true);
certification4.setReadOnly(true);
recognition.setReadOnly(true);
achievements.setReadOnly(true);
language.setReadOnly(true);
languageLevel.setReadOnly(true);
cod.setReadOnly(true);
leadManager.setReadOnly(true);
project.setReadOnly(true);
dateOfEntry.setReadOnly(true);
dateOfExit.setReadOnly(true);
contractType.setReadOnly(true);
seniority.setReadOnly(true);
salary.setReadOnly(true);
bankName.setReadOnly(true);
accountNumber.setReadOnly(true);
gpss.setReadOnly(true);
sss.setReadOnly(true);
beneficiaries.setReadOnly(true);
}
private void setFieldsEditable() {
username.setReadOnly(false);
firstName.setReadOnly(false);
lastName.setReadOnly(false);
status.setReadOnly(false);
birthday.setReadOnly(false);
birthCity.setReadOnly(false);
maritalStatus.setReadOnly(false);
residenceAddress.setReadOnly(false);
localAddress.setReadOnly(false);
maritalStatus.setReadOnly(false);
numberOfChildren.setReadOnly(false);
phoneNumber.setReadOnly(false);
personalEmail.setReadOnly(false);
position.setReadOnly(false);
@ -295,17 +505,67 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
emergencyCAddress.setReadOnly(false);
emergencyCPhone.setReadOnly(false);
emergencyCEmail.setReadOnly(false);
upload.setVisible(true);
upload.setVisible(false);
profileImagePreview.setVisible(true);
age.setReadOnly(false);
gender.setReadOnly(false);
status.setReadOnly(false);
ci.setReadOnly(false);
issuedIn.setReadOnly(false);
pTitle1.setReadOnly(false);
pTitle2.setReadOnly(false);
pTitle3.setReadOnly(false);
pStudy1.setReadOnly(false);
pStudy2.setReadOnly(false);
pStudy3.setReadOnly(false);
certification1.setReadOnly(false);
certification2.setReadOnly(false);
certification3.setReadOnly(false);
certification4.setReadOnly(false);
recognition.setReadOnly(false);
achievements.setReadOnly(false);
language.setReadOnly(false);
languageLevel.setReadOnly(false);
cod.setReadOnly(false);
leadManager.setReadOnly(false);
project.setReadOnly(false);
dateOfEntry.setReadOnly(false);
dateOfExit.setReadOnly(false);
contractType.setReadOnly(false);
seniority.setReadOnly(false);
salary.setReadOnly(false);
bankName.setReadOnly(false);
accountNumber.setReadOnly(false);
gpss.setReadOnly(false);
sss.setReadOnly(false);
beneficiaries.setReadOnly(false);
}
@Override
protected List<Component> getFormComponents() {
return List.of(
mt, fs, userName, firstName, lastName, status, birthday, birthCity, maritalStatus,
residenceAddress, phoneNumber, personalEmail, position, team, ss, emergencyCName,
emergencyCAddress, emergencyCPhone, emergencyCEmail, si, upload, profileImagePreview,
saveButton, editButton
username,
infoPer,
infoGenr,
upload, profileImagePreview,
firstName, lastName,
gender, status,
birthday, age,
birthCity, residenceAddress, localAddress,
maritalStatus, ci, issuedIn, numberOfChildren,
phoneNumber, personalEmail,
contEmerg, emergencyCName, emergencyCAddress, emergencyCPhone, emergencyCEmail,
infProf,
titulos, pTitle1, pTitle2, pTitle3, pStudy1, pStudy2, pStudy3,
certif, certification1, certification2, certification3, certification4,
logros, recognition, achievements,
idioma, language, languageLevel,
infoAdm,
cod, position, team, leadManager, project,
infoCont, dateOfEntry, dateOfExit, contractType, seniority, salary,
datBanc, bankName, accountNumber,
datGest, gpss, sss, beneficiaries,
saveButton, editButton, reportButton, dialog
);
}
}
}

View File

@ -4,7 +4,6 @@ import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.service.EmployeeService;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
@ -16,13 +15,12 @@ import org.springframework.context.annotation.Scope;
import java.util.List;
@SpringComponent
@Scope("prototype")
@PageTitle("Employees")
@Route(value = "/employees", layout = MainLayout.class)
@PermitAll
public class EmployeesListView extends Main {
public class EmployeesListView extends BaseView {
private final EmployeeService employeeService;
private final PagingGrid<Employee> table = new PagingGrid<>(Employee.class);
@ -34,10 +32,10 @@ public class EmployeesListView extends Main {
}
private void setupView() {
add(new H2("Employee List"));
getCurrentPageLayout().add(new H2("Employee List"));
configureTable();
add(createAddEmployeeButton());
add(table);
getCurrentPageLayout().add(createAddEmployeeButton());
getCurrentPageLayout().add(table);
}
private void configureTable() {
@ -47,12 +45,6 @@ public class EmployeesListView extends Main {
setupPagingGrid();
}
private void updateEmployeeStatus(final Employee employee, final boolean isActive) {
employee.setStatus(isActive ? Employee.Status.ACTIVE : Employee.Status.INACTIVE);
employeeService.createOrUpdate(employee);
refreshGrid();
}
private void addEditButtonColumn(final String label, final ButtonClickHandler handler) {
table.addComponentColumn(employee -> createButton(label, () -> handler.handle(employee)));
}
@ -81,7 +73,7 @@ public class EmployeesListView extends Main {
private void setupPagingGrid() {
table.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
table.setPageSize(5);
table.setPageSize(Constants.PAGE_SIZE);
}
private void refreshGrid() {

View File

@ -196,7 +196,6 @@ public class EvaluationView extends Main implements HasUrlParameter<String> {
final MenuBar navMenuBar = new MenuBar();
prev = navMenuBar.addItem("Anterior pregunta",
(ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
log.info(">>> prev");
this.currSubmission.setResponse(this.questionEditor.getValue());
this.assessmentService.saveSubmission(assessment.getId(), this.currSubmission);
this.currSubmission = this.assessmentService.getPrevSubmission(assessment.getId(), this.currSubmission);
@ -253,7 +252,6 @@ public class EvaluationView extends Main implements HasUrlParameter<String> {
start = new Button("Empezar");
start.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SUCCESS);
start.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
log.info(">>> start");
this.assessment = this.assessmentService.startAssessment(this.assessment.getId());
if (tf.getValue().trim().equalsIgnoreCase(this.assessment.getCandidate().getEmail())) {
@ -318,7 +316,6 @@ public class EvaluationView extends Main implements HasUrlParameter<String> {
}
private void goToNext() {
log.info(">>> next");
Submission found = this.assessmentService.getNextSubmission(assessment.getId(),
this.currSubmission.getId());

View File

@ -1,16 +0,0 @@
package com.primefactorsolutions.views;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("GovernmentDocumentsView")
@Route(value = "/government-documents/me", layout = MainLayout.class)
public class GovernmentDocumentsView extends Main {
}

View File

@ -0,0 +1,256 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.model.HoursWorked;
import com.primefactorsolutions.model.Team;
import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.HoursWorkedService;
import com.primefactorsolutions.service.TeamService;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.grid.PagingGrid;
import java.time.LocalDate;
import java.time.temporal.IsoFields;
import java.util.*;
import java.util.stream.Collectors;
import static com.primefactorsolutions.views.Constants.PAGE_SIZE;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("Registro de Horas Trabajadas")
@Route(value = "/hours-worked-list", layout = MainLayout.class)
public class HoursWorkedListView extends BaseView {
private final HoursWorkedService hoursWorkedService;
private final EmployeeService employeeService;
private final TeamService teamService;
private final PagingGrid<HoursWorked> hoursWorkedGrid = new PagingGrid<>();
private ComboBox<Employee> employeeFilter;
private ComboBox<Team> teamFilter;
private UUID selectedEmployeeId;
public HoursWorkedListView(final HoursWorkedService hoursWorkedService,
final EmployeeService employeeService,
final TeamService teamService) {
this.hoursWorkedService = hoursWorkedService;
this.employeeService = employeeService;
this.teamService = teamService;
initializeView();
refreshGridListHoursWorked(null, null);
}
private void refreshGridListHoursWorked(final Employee employee,
final Team team) {
hoursWorkedGrid.setPagingDataProvider((page, pageSize) -> {
int start = (int) (page * hoursWorkedGrid.getPageSize());
List<HoursWorked> hoursWorkedList = fetchFilteredHoursWorked(start, pageSize, employee, team);
double totalHours = hoursWorkedList.stream()
.mapToDouble(HoursWorked::getTotalHours)
.sum();
Notification.show("Total de horas trabajadas: " + totalHours,
3000, Notification.Position.BOTTOM_CENTER);
return hoursWorkedList;
});
hoursWorkedGrid.getDataProvider().refreshAll();
}
private List<HoursWorked> fetchFilteredHoursWorked(final int start,
final int pageSize,
final Employee employee,
final Team team) {
List<HoursWorked> filteredHoursWorked = hoursWorkedService.findAll();
if (employee != null && !"TODOS".equals(employee.getFirstName())) {
filteredHoursWorked = filteredHoursWorked.stream()
.filter(hw -> hw.getEmployee().getId().equals(employee.getId()))
.collect(Collectors.toList());
}
if (team != null && !"TODOS".equals(team.getName())) {
filteredHoursWorked = filteredHoursWorked.stream()
.filter(hw -> hw.getEmployee().getTeam() != null
&& hw.getEmployee().getTeam().getId().equals(team.getId()))
.collect(Collectors.toList());
}
for (HoursWorked hoursWorked : filteredHoursWorked) {
if (employee != null && hoursWorked.getEmployee().getId().equals(employee.getId())) {
LocalDate date = hoursWorked.getDate();
int currentWeek = date.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
double totalWorkedInSameWeek = filteredHoursWorked.stream()
.filter(hw -> hw.getEmployee().getId().equals(employee.getId())
&&
hw.getDate().get(IsoFields.WEEK_OF_WEEK_BASED_YEAR) == currentWeek)
.mapToDouble(HoursWorked::getHours)
.sum();
double updatedPendingHours = totalWorkedInSameWeek - hoursWorked.getHours();
hoursWorked.setHoraspendientes(updatedPendingHours);
}
}
int end = Math.min(start + pageSize, filteredHoursWorked.size());
return filteredHoursWorked.subList(start, end);
}
private void initializeView() {
getCurrentPageLayout().add(createAddHoursWorked());
setupFilters();
setupListHoursWorkedGrid();
getCurrentPageLayout().add(hoursWorkedGrid);
getCurrentPageLayout().add(createActionButtons());
}
private void setupFilters() {
final HorizontalLayout hl = new HorizontalLayout();
hl.add(createEmployeeFilter());
hl.add(createTeamFilter());
getCurrentPageLayout().add(hl);
}
private void setupListHoursWorkedGrid() {
hoursWorkedGrid.addColumn(hw -> hw.getDate() != null ? hw.getDate().toString() : "")
.setHeader("Fecha")
.setSortable(true);
hoursWorkedGrid.addColumn(HoursWorked::getWeekNumber)
.setHeader("Semana")
.setSortable(true);
hoursWorkedGrid.addColumn(hw -> hw.getEmployee().getFirstName() + " " + hw.getEmployee().getLastName())
.setHeader("Empleado");
hoursWorkedGrid.addColumn(hw -> hw.getEmployee().getTeam() != null ? hw.getEmployee().getTeam()
.getName() : "Sin asignar")
.setHeader("Equipo");
hoursWorkedGrid.addColumn(HoursWorked::getActividad).setHeader("Actividad");
hoursWorkedGrid.addColumn(HoursWorked::getHours).setHeader("Total Horas").setSortable(true);
hoursWorkedGrid.addColumn(hw -> hw.getHoraspendientes() - calcularTotal(hw)).setHeader("Horas Pendientes")
.setSortable(true);
hoursWorkedGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
hoursWorkedGrid.setPageSize(PAGE_SIZE);
hoursWorkedGrid.asSingleSelect().addValueChangeListener(event -> {
HoursWorked selectedHoursWorked = event.getValue();
if (selectedHoursWorked != null) {
selectedEmployeeId = selectedHoursWorked.getEmployee().getId();
}
});
}
private double calcularTotal(final HoursWorked hoursWorked) {
List<HoursWorked> listHoursworkedemploye = hoursWorkedService.findListHoursWorkedEmployee(
hoursWorked.getEmployee().getId(), hoursWorked.getWeekNumber());
return calculateTotalUtilized(listHoursworkedemploye);
}
private double calculateTotalUtilized(final List<HoursWorked> employeeRequests) {
return employeeRequests.stream()
.filter(Objects::nonNull)
.mapToDouble(HoursWorked::getHours)
.sum();
}
private HorizontalLayout createActionButtons() {
Button viewButton = new Button("Ver", event -> {
if (selectedEmployeeId != null) {
navigateToHoursWorkedView(selectedEmployeeId);
} else {
Notification.show("Seleccione una solicitud.", 3000, Notification.Position.MIDDLE);
}
});
Button closeButton = new Button("Salir", event -> navigateToListView());
return new HorizontalLayout(viewButton, closeButton);
}
private void navigateToListView() {
getUI().ifPresent(ui -> ui.navigate(MainView.class));
}
private void navigateToHoursWorkedView(final UUID idEmployee) {
getUI().ifPresent(ui -> ui.navigate("hours-worked-list/" + idEmployee.toString()));
}
private Button createButton(final String label, final Runnable onClickAction) {
final Button button = new Button(label);
button.addClickListener(event -> onClickAction.run());
return button;
}
private Button createAddHoursWorked() {
return createButton("Agregar Actividad", this::navigateToHours);
}
private void navigateToHours() {
getUI().ifPresent(ui -> ui.navigate(HoursWorkedView.class, "new"));
}
private ComboBox<Employee> createEmployeeFilter() {
employeeFilter = new ComboBox<>("Empleado");
final List<Employee> employees = new ArrayList<>(employeeService.findAllEmployees());
employees.addFirst(createAllEmployeesOption());
employeeFilter.setItems(employees);
employeeFilter.setItemLabelGenerator(this::getEmployeeFullName);
employeeFilter.setValue(employees.getFirst());
employeeFilter.addValueChangeListener(event ->
refreshGridListHoursWorked(
event.getValue(),
teamFilter.getValue()
)
);
return employeeFilter;
}
private String getEmployeeFullName(final Employee employee) {
return "TODOS".equals(employee.getFirstName())
? "TODOS" : employee.getFirstName() + " " + employee.getLastName();
}
private ComboBox<Team> createTeamFilter() {
teamFilter = new ComboBox<>("Equipo");
List<Team> teams = new ArrayList<>(teamService.findAllTeams());
teams.addFirst(createAllTeamsOption());
teamFilter.setItems(teams);
teamFilter.setItemLabelGenerator(this::getTeamLabel);
teamFilter.setValue(teams.getFirst());
teamFilter.addValueChangeListener(event ->
refreshGridListHoursWorked(
employeeFilter.getValue(),
event.getValue()
)
);
return teamFilter;
}
private String getTeamLabel(final Team team) {
return team != null && !"TODOS".equals(team.getName()) ? team.getName() : "TODOS";
}
private Employee createAllEmployeesOption() {
Employee allEmployeesOption = new Employee();
allEmployeesOption.setFirstName("TODOS");
return allEmployeesOption;
}
private Team createAllTeamsOption() {
Team allTeamsOption = new Team();
allTeamsOption.setName("TODOS");
return allTeamsOption;
}
}

View File

@ -0,0 +1,256 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.model.HoursWorked;
import com.primefactorsolutions.model.Team;
import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.HoursWorkedService;
import com.primefactorsolutions.service.TeamService;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.router.*;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.datepicker.VDatePicker;
import org.vaadin.firitin.form.BeanValidationForm;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.temporal.IsoFields;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("Horas Trabajadas")
@Route(value = "/hours-worked-list/:hours-workedId?/:action?", layout = MainLayout.class)
public class HoursWorkedView extends BeanValidationForm<HoursWorked> implements HasUrlParameter<String> {
private final VDatePicker dateField = new VDatePicker("Fecha");
private final ComboBox<Team> teamField = new ComboBox<>("Equipo");
private ComboBox<Employee> employeeField;
private final ComboBox<String> tareasEspecificasDropdown = new ComboBox<>("Tarea Específica");
private final TextField tareaEspecificaInput = new TextField("Otra Tarea Específica");
private final TextField horasTareaEspecificaField = new TextField("Horas Tarea Específica");
private final TextField activityField = new TextField("Actividad");
private final TextField hoursField = new TextField("Horas");
private final H2 equipoLabel = new H2("Tareas del Cliente/Equipo");
private final H2 empresaLabel = new H2("Tareas de la Empresa");
private final Label totalCompletadoLabel = new Label();
private final HoursWorkedService hoursWorkedService;
private final EmployeeService employeeService;
private final TeamService teamService;
private HoursWorked hoursWorked;
private Employee employee;
private Button saveButton;
public HoursWorkedView(final HoursWorkedService hoursWorkedService,
final EmployeeService employeeService,
final TeamService teamService) {
super(HoursWorked.class);
this.hoursWorkedService = hoursWorkedService;
this.employeeService = employeeService;
this.teamService = teamService;
initializeDateField();
initializeTeamField();
initializeEmployeeField();
configureTareasEspecificas();
}
@Override
public void setParameter(final BeforeEvent beforeEvent, final String action) {
final RouteParameters params = beforeEvent.getRouteParameters();
final String s = params.get("hours-workedId").orElse(null);
if ("new".equals(action)) {
setEntityWithEnabledSave(new HoursWorked());
} else {
UUID hoursWorkedId = UUID.fromString(s);
var hoursWorked = hoursWorkedService.getHoursWorked(hoursWorkedId);
setEntityWithEnabledSave(hoursWorked);
if ("edit".equals(action) && !s.isEmpty()) {
saveButton.setVisible(true);
} else if ("view".equals(action) && !s.isEmpty()) {
saveButton.setVisible(false);
}
}
}
@Override
protected List<Component> getFormComponents() {
return List.of(
dateField,
teamField,
employeeField,
equipoLabel,
activityField,
hoursField,
empresaLabel,
tareasEspecificasDropdown,
tareaEspecificaInput,
horasTareaEspecificaField,
createCloseButton()
);
}
private void configureTareasEspecificas() {
tareasEspecificasDropdown.setItems("Entrevistas", "Reuniones",
"Colaboraciones", "Aprendizajes", "Proyectos PFS", "Otros");
tareasEspecificasDropdown.setPlaceholder("Selecciona una tarea...");
tareasEspecificasDropdown.addValueChangeListener(event -> {
String selected = event.getValue();
boolean isOtros = "Otros".equals(selected);
tareaEspecificaInput.setVisible(isOtros);
horasTareaEspecificaField.setVisible(true);
if (!isOtros) {
tareaEspecificaInput.clear();
horasTareaEspecificaField.clear();
}
});
tareaEspecificaInput.setVisible(false);
horasTareaEspecificaField.setVisible(false);
}
protected Button createSaveButton() {
saveButton = new Button("Guardar");
saveButton.addClickListener(event -> saveHoursWorked());
return saveButton;
}
protected Button createCloseButton() {
Button closeButton = new Button("Cerrar");
closeButton.addClickListener(event -> closeForm());
return closeButton;
}
private void initializeTeamField() {
List<Team> teams = new ArrayList<>(teamService.findAllTeams());
teamField.setItems(teamService.findAllTeams());
teamField.setItemLabelGenerator(Team::getName);
teamField.setValue(teams.getFirst());
teamField.addValueChangeListener(event -> {
if (teams != null) {
employeeField.getValue();
event.getValue();
}
}
);
}
private ComboBox<Employee> initializeEmployeeField() {
employeeField = new ComboBox<>("Empleado");
List<Employee> employees = new ArrayList<>(employeeService.findAllEmployees());
employeeField.setItems(employees);
employeeField.setItemLabelGenerator(this::getEmployeeFullName);
employeeField.setValue(employees.getFirst());
return employeeField;
}
private String getEmployeeFullName(final Employee employee) {
return "TODOS".equals(employee.getFirstName())
? "TODOS" : employee.getFirstName() + " " + employee.getLastName();
}
private void initializeDateField() {
LocalDate today = LocalDate.now();
YearMonth currentMonth = YearMonth.of(today.getYear(), today.getMonth());
LocalDate startOfMonth = currentMonth.atDay(1);
LocalDate maxSelectableDate = today;
dateField.setMin(startOfMonth);
dateField.setMax(maxSelectableDate);
dateField.setValue(today);
dateField.addValueChangeListener(event -> {
LocalDate selectedDate = event.getValue();
if (selectedDate != null) {
int weekNumber = selectedDate.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
Notification.show("Número de la semana: " + weekNumber, 3000, Notification.Position.BOTTOM_CENTER);
if (hoursWorked != null) {
hoursWorked.setWeekNumber(weekNumber);
}
}
});
}
private void saveHoursWorked() {
if (isFormValid()) {
HoursWorked hoursWorked = getEntity();
setFieldValues(hoursWorked);
hoursWorkedService.save(hoursWorked);
Notification.show("Horas trabajadas guardadas correctamente.");
closeForm();
}
}
private void setFieldValues(final HoursWorked hoursWorked) {
hoursWorked.setDate(dateField.getValue());
hoursWorked.setTeam(teamField.getValue());
hoursWorked.setEmployee(employeeField.getValue());
hoursWorked.setActividad(activityField.getValue());
try {
double hours = Double.parseDouble(hoursField.getValue());
hoursWorked.setHours(hours);
} catch (NumberFormatException e) {
Notification.show("Por favor, ingrese un número válido para las horas.");
}
if ("Otros".equals(tareasEspecificasDropdown.getValue())) {
hoursWorked.setTareasEspecificas(tareaEspecificaInput.getValue());
try {
double horasEspecifica = Double.parseDouble(horasTareaEspecificaField.getValue());
hoursWorked.setHorasTareasEspecificas(horasEspecifica);
double totalHoras = hoursWorked.getHours() + horasEspecifica;
hoursWorked.setTotalHours(totalHoras);
} catch (NumberFormatException e) {
Notification.show("Por favor, ingrese un número válido para las horas de la tarea específica.");
}
}
}
private void closeForm() {
getUI().ifPresent(ui -> ui.navigate("hours-worked-list/" + hoursWorked.getId().toString()));
}
private boolean isFormValid() {
return dateField.getValue() != null
&&
teamField.getValue() != null
&&
employeeField.getValue() != null
&&
!activityField.isEmpty();
}
private void configureViewOrEditAction(final String action) {
if ("edit".equals(action) && hoursWorked != null) {
setFieldsReadOnly(false);
} else if ("view".equals(action) && hoursWorked != null) {
setFieldsReadOnly(true);
saveButton.setEnabled(false);
}
}
private void setFieldsReadOnly(final boolean readOnly) {
dateField.setReadOnly(readOnly);
teamField.setReadOnly(readOnly);
employeeField.setReadOnly(readOnly);
activityField.setReadOnly(readOnly);
}
}

View File

@ -0,0 +1,54 @@
package com.primefactorsolutions.views;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.PasswordField;
import com.vaadin.flow.router.BeforeEnterEvent;
import com.vaadin.flow.router.BeforeEnterObserver;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.auth.AnonymousAllowed;
@SuppressWarnings("unused")
@Route("init-account")
@PageTitle("PFS Intra")
@AnonymousAllowed
public class InitAccountView extends VerticalLayout implements BeforeEnterObserver {
public InitAccountView() {
setSizeFull();
setAlignItems(Alignment.CENTER);
setJustifyContentMode(JustifyContentMode.CENTER);
final VerticalLayout vl = new VerticalLayout();
vl.setJustifyContentMode(JustifyContentMode.CENTER);
vl.setWidth("400px");
final PasswordField password = new PasswordField("Password");
final PasswordField confirmPassword = new PasswordField("Confirm Password");
final FormLayout formLayout = new FormLayout(password, confirmPassword);
formLayout.setColspan(password, 3);
formLayout.setColspan(confirmPassword, 3);
final Button primaryButton = new Button("Submit");
primaryButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
final Button secondaryButton = new Button("Cancel");
HorizontalLayout hl = new HorizontalLayout(secondaryButton, primaryButton);
vl.add(new H3("Set Account Password"));
vl.add(formLayout);
vl.add(hl);
add(vl);
}
@Override
public void beforeEnter(final BeforeEnterEvent beforeEnterEvent) {
}
}

View File

@ -1,6 +1,8 @@
package com.primefactorsolutions.views;
import com.vaadin.flow.component.html.Anchor;
import com.vaadin.flow.component.html.H1;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.login.LoginForm;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.BeforeEnterEvent;
@ -8,6 +10,9 @@ import com.vaadin.flow.router.BeforeEnterObserver;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.auth.AnonymousAllowed;
import com.vaadin.flow.theme.lumo.LumoUtility;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@Route("login")
@PageTitle("PFS Intra")
@ -16,16 +21,22 @@ public class LoginView extends VerticalLayout implements BeforeEnterObserver {
private final LoginForm login = new LoginForm();
public LoginView() {
public LoginView(@Autowired @Value("${git.commit.id.abbrev}") final String commitId) {
addClassName("login-view");
setSizeFull();
setAlignItems(Alignment.CENTER);
setJustifyContentMode(JustifyContentMode.CENTER);
login.setAction("login");
login.setForgotPasswordButtonVisible(false);
add(new H1("PFS Intra"));
add(login);
add(new Anchor("/password-recovery", "Reset password?"));
final Span version = new Span(String.format("v.%s", commitId));
version.addClassName(LumoUtility.FontSize.XSMALL);
add(version);
}
@Override

View File

@ -1,12 +1,19 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Employee;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.Text;
import com.vaadin.flow.component.applayout.AppLayout;
import com.vaadin.flow.component.applayout.DrawerToggle;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Footer;
import com.vaadin.flow.component.html.H1;
import com.vaadin.flow.component.html.Header;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.avatar.Avatar;
import com.vaadin.flow.component.contextmenu.HasMenuItems;
import com.vaadin.flow.component.contextmenu.MenuItem;
import com.vaadin.flow.component.contextmenu.SubMenu;
import com.vaadin.flow.component.html.*;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.menubar.MenuBar;
import com.vaadin.flow.component.menubar.MenuBarVariant;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.Scroller;
@ -15,9 +22,13 @@ import com.vaadin.flow.component.sidenav.SideNavItem;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.spring.security.AuthenticationContext;
import com.vaadin.flow.theme.lumo.LumoUtility;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.vaadin.lineawesome.LineAwesomeIcon;
import java.util.UUID;
/**
* The main view is a top-level placeholder for other views.
*/
@ -26,52 +37,98 @@ public class MainLayout extends AppLayout {
private H1 viewTitle;
public MainLayout(final AuthenticationContext authContext) {
public MainLayout(final AuthenticationContext authContext,
@Autowired @Value("${git.commit.id.abbrev}") final String commitId) {
this.authContext = authContext;
setPrimarySection(Section.DRAWER);
addDrawerContent();
addDrawerContent(commitId);
addHeaderContent();
}
private void addHeaderContent() {
DrawerToggle toggle = new DrawerToggle();
final DrawerToggle toggle = new DrawerToggle();
toggle.setAriaLabel("Menu toggle");
viewTitle = new H1();
viewTitle.addClassNames(LumoUtility.FontSize.LARGE, LumoUtility.Margin.NONE);
HorizontalLayout
header =
authContext.getAuthenticatedUser(UserDetails.class)
.map(user -> {
Button logout = new Button("Logout", click -> this.authContext.logout());
Span loggedUser = new Span("Welcome " + user.getUsername());
HorizontalLayout hl = new HorizontalLayout(loggedUser, logout);
hl.setJustifyContentMode(FlexComponent.JustifyContentMode.END);
return hl;
}).orElseGet(HorizontalLayout::new);
final HorizontalLayout header = authContext.getAuthenticatedUser(UserDetails.class)
.map(user -> {
String employeeId = "N/A";
if (user instanceof Employee) {
final UUID uuid = ((Employee) user).getId();
if (uuid != null) {
employeeId = uuid.toString();
}
}
final Avatar loggedUser = new Avatar(user.getUsername());
loggedUser.getStyle().set("display", "block");
loggedUser.getElement().setAttribute("tabindex", "-1");
final MenuBar menuBar = new MenuBar();
menuBar.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE);
final MenuItem actions = createIconItem(menuBar, loggedUser, null, employeeId);
final SubMenu actionsSubMenu = actions.getSubMenu();
final MenuItem signOutMenuItem = createIconItem(actionsSubMenu,
createIcon(VaadinIcon.EXIT, true), "Sign-out", null);
signOutMenuItem.addClickListener(c -> this.authContext.logout());
final HorizontalLayout hl = new HorizontalLayout(menuBar);
hl.setJustifyContentMode(FlexComponent.JustifyContentMode.END);
return hl;
}).orElseGet(HorizontalLayout::new);
header.setAlignItems(FlexComponent.Alignment.STRETCH);
header.setWidthFull();
addToNavbar(true, toggle, viewTitle, header);
}
private void addDrawerContent() {
Span appName = new Span("pfs-intra");
private MenuItem createIconItem(final HasMenuItems menu, final Component component,
final String label, final String ariaLabel) {
final MenuItem item = menu.addItem(component, e -> {
});
if (ariaLabel != null) {
item.setAriaLabel(ariaLabel);
}
if (label != null) {
item.add(new Text(label));
}
return item;
}
private Icon createIcon(final VaadinIcon iconName, final boolean isChild) {
final Icon icon = new Icon(iconName);
if (isChild) {
icon.getStyle().set("width", "var(--lumo-icon-size-s)");
icon.getStyle().set("height", "var(--lumo-icon-size-s)");
icon.getStyle().set("marginRight", "var(--lumo-space-s)");
}
return icon;
}
private void addDrawerContent(final String commitId) {
final Span appName = new Span("pfs-intra");
appName.addClassNames(LumoUtility.FontWeight.SEMIBOLD, LumoUtility.FontSize.LARGE);
Header header = new Header(appName);
Scroller scroller = new Scroller(createNavigation());
addToDrawer(header, scroller, createFooter());
final Header header = new Header(appName);
final Scroller scroller = new Scroller(createNavigation());
addToDrawer(header, scroller, createFooter(commitId));
}
private SideNav createNavigation() {
SideNav nav = new SideNav();
final SideNav nav = new SideNav();
authContext.getAuthenticatedUser(UserDetails.class).ifPresent(u -> {
SideNavItem recruiting = new SideNavItem("Recruiting", MainView.class,
LineAwesomeIcon.BUSINESS_TIME_SOLID.create());
SideNavItem recruiting = new SideNavItem("Recruiting");
recruiting.setPrefixComponent(LineAwesomeIcon.BUSINESS_TIME_SOLID.create());
recruiting.addItem(new SideNavItem("Assessments", AssessmentsListView.class,
LineAwesomeIcon.RIBBON_SOLID.create()));
recruiting.addItem(new SideNavItem("Candidates", CandidatesListView.class,
@ -79,38 +136,33 @@ public class MainLayout extends AppLayout {
recruiting.addItem(new SideNavItem("Questions", QuestionsListView.class,
LineAwesomeIcon.QUESTION_SOLID.create()));
SideNavItem admin = new SideNavItem("Admin", MainView.class,
LineAwesomeIcon.SUPERSCRIPT_SOLID.create());
admin.addItem(new SideNavItem("Requests", RequestsListView.class,
LineAwesomeIcon.TASKS_SOLID.create()));
admin.addItem(new SideNavItem("Timesheets", TimesheestReportView.class,
LineAwesomeIcon.HOURGLASS_END_SOLID.create()));
SideNavItem admin = new SideNavItem("Admin");
admin.setPrefixComponent(LineAwesomeIcon.BUILDING.create());
admin.addItem(new SideNavItem("Employees", EmployeesListView.class,
LineAwesomeIcon.USER_EDIT_SOLID.create()));
SideNavItem documents = new SideNavItem("Documents", MainView.class,
LineAwesomeIcon.FILE_ALT_SOLID.create());
documents.addItem(new SideNavItem("Personal Documents", PersonalDocumentsView.class,
LineAwesomeIcon.ID_CARD_SOLID.create()));
documents.addItem(new SideNavItem("Professional Documents", ProfessionalDocumentsView.class,
LineAwesomeIcon.BRIEFCASE_SOLID.create()));
documents.addItem(new SideNavItem("Work Documents", WorkDocumentsView.class,
LineAwesomeIcon.FOLDER_OPEN_SOLID.create()));
documents.addItem(new SideNavItem("Corporate Documents", CorporateDocumentsView.class,
LineAwesomeIcon.BUILDING_SOLID.create()));
documents.addItem(new SideNavItem("Government Documents", GovernmentDocumentsView.class,
LineAwesomeIcon.BALANCE_SCALE_SOLID.create()));
admin.addItem(new SideNavItem("Documents", DocumentsListView.class,
LineAwesomeIcon.FILE_ALT_SOLID.create()));
SideNavItem timeOff = new SideNavItem("My Time-off", TimeoffView.class,
LineAwesomeIcon.PLANE_DEPARTURE_SOLID.create());
timeOff.addItem(new SideNavItem("Vacations", RequestsListView.class,
LineAwesomeIcon.UMBRELLA_BEACH_SOLID.create()));
timeOff.addItem(new SideNavItem("Add Vacation", RequestRegisterView.class,
LineAwesomeIcon.CALENDAR_PLUS.create()));
timeOff.addItem(new SideNavItem("Pending Requests", PendingRequestsListView.class,
LineAwesomeIcon.LIST_ALT.create()));
SideNavItem timesheet = new SideNavItem("My Timesheet", TimesheetView.class,
LineAwesomeIcon.HOURGLASS_START_SOLID.create());
timesheet.addItem(new SideNavItem("Registro de Horas Trabajadas", HoursWorkedListView.class,
LineAwesomeIcon.ID_CARD_SOLID.create()));
timesheet.addItem(new SideNavItem("Reporte Horas Trabajadas", ReporteView.class,
LineAwesomeIcon.ID_CARD_SOLID.create()));
SideNavItem profile = new SideNavItem("My Profile", ProfileView.class,
LineAwesomeIcon.USER_EDIT_SOLID.create());
nav.addItem(new SideNavItem("Home", MainView.class, LineAwesomeIcon.HOME_SOLID.create()));
nav.addItem(admin);
nav.addItem(documents);
nav.addItem(recruiting);
nav.addItem(profile);
nav.addItem(timesheet);
@ -120,10 +172,8 @@ public class MainLayout extends AppLayout {
return nav;
}
private Footer createFooter() {
Footer layout = new Footer();
return layout;
private Footer createFooter(final String commitId) {
return new Footer(new Text(String.format("v.%s", commitId)));
}
@Override

View File

@ -10,7 +10,8 @@ import jakarta.annotation.security.PermitAll;
@Route(value = "", layout = MainLayout.class)
@PermitAll
public class MainView extends Main {
public MainView() {
add(new Text("welcome"));
add(new Text("Welcome"));
}
}

View File

@ -0,0 +1,67 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.service.AccountService;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.EmailField;
import com.vaadin.flow.router.BeforeEnterEvent;
import com.vaadin.flow.router.BeforeEnterObserver;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.auth.AnonymousAllowed;
@Route("password-recovery")
@PageTitle("PFS Intra")
@AnonymousAllowed
public class PasswordRecoveryView extends VerticalLayout implements BeforeEnterObserver {
public PasswordRecoveryView(final AccountService accountService) {
setSizeFull();
setAlignItems(Alignment.CENTER);
setJustifyContentMode(JustifyContentMode.CENTER);
final VerticalLayout vl = new VerticalLayout();
vl.setJustifyContentMode(JustifyContentMode.CENTER);
vl.setWidth("400px");
final EmailField personalEmail = new EmailField("Personal Email");
personalEmail.setRequired(true);
final EmailField confirmPersonalEmail = new EmailField("Confirm Personal Email");
confirmPersonalEmail.setRequired(true);
final FormLayout formLayout = new FormLayout(personalEmail, confirmPersonalEmail);
formLayout.setColspan(personalEmail, 3);
formLayout.setColspan(confirmPersonalEmail, 3);
final Button primaryButton = new Button("Submit");
primaryButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
primaryButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
if (personalEmail.getValue().equals(confirmPersonalEmail.getValue())) {
accountService.sendResetPasswordEmail(personalEmail.getValue());
getUI().ifPresent(ui -> ui.navigate(MainView.class));
}
});
final Button secondaryButton = new Button("Cancel");
final HorizontalLayout hl = new HorizontalLayout(secondaryButton, primaryButton);
secondaryButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent ->
getUI().ifPresent(ui -> ui.navigate(MainView.class)));
vl.add(new H3("PFS - Password Recovery"));
vl.add(formLayout);
vl.add(hl);
add(vl);
}
@Override
public void beforeEnter(final BeforeEnterEvent beforeEnterEvent) {
}
}

View File

@ -0,0 +1,230 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.*;
import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.TeamService;
import com.primefactorsolutions.service.TimeOffRequestService;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.grid.PagingGrid;
import java.util.*;
import java.util.stream.Collectors;
import static com.primefactorsolutions.views.Constants.PAGE_SIZE;
@SpringComponent
@Scope("prototype")
@PageTitle("PendingRequests")
@Route(value = "/pending-requests", layout = MainLayout.class)
@PermitAll
public class PendingRequestsListView extends BaseView {
private final TimeOffRequestService requestService;
private final EmployeeService employeeService;
private final TeamService teamService;
private final PagingGrid<TimeOffRequest> pendingRequestsGrid = new PagingGrid<>();
private ComboBox<Employee> employeeFilter;
private ComboBox<Team> teamFilter;
private ComboBox<TimeOffRequestType> categoryFilter;
private UUID selectedRequestId;
public PendingRequestsListView(final TimeOffRequestService requestService,
final EmployeeService employeeService,
final TeamService teamService) {
this.requestService = requestService;
this.employeeService = employeeService;
this.teamService = teamService;
initializeView();
refreshGeneralPendingRequestsGrid(null, null, null);
}
private void initializeView() {
setupFilters();
setupPendingRequestsGrid();
createActionButtons();
}
private void setupFilters() {
final HorizontalLayout hl = new HorizontalLayout();
hl.add(createEmployeeFilter());
hl.add(createTeamFilter());
hl.add(createCategoryFilter());
getCurrentPageLayout().add(hl);
}
private void setupPendingRequestsGrid() {
pendingRequestsGrid.addColumn(this::getEmployeeFullName).setHeader("Empleado");
pendingRequestsGrid.addColumn(this::getTeamName).setHeader("Equipo");
pendingRequestsGrid.addColumn(this::getCategory).setHeader("Categoría");
pendingRequestsGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
pendingRequestsGrid.setPageSize(PAGE_SIZE);
pendingRequestsGrid.asSingleSelect().addValueChangeListener(event -> {
TimeOffRequest selectedRequest = event.getValue();
if (selectedRequest != null) {
selectedRequestId = selectedRequest.getId();
}
});
getCurrentPageLayout().add(pendingRequestsGrid);
}
private void createActionButtons() {
final Button approveButton = createActionButton("Aprobar", TimeOffRequestStatus.APROBADO);
final Button rejectButton = createActionButton("Rechazar", TimeOffRequestStatus.RECHAZADO);
final Button closeButton = new Button("Salir", event -> navigateToMainView());
getCurrentPageLayout().add(new HorizontalLayout(approveButton, rejectButton, closeButton));
}
private Button createActionButton(final String caption, final TimeOffRequestStatus status) {
return new Button(caption, event -> {
if (selectedRequestId != null) {
TimeOffRequest request = requestService.findTimeOffRequest(selectedRequestId);
request.setState(status);
requestService.saveTimeOffRequest(request);
refreshGeneralPendingRequestsGrid(null, null, null);
} else {
Notification.show("Seleccione una solicitud.", 3000, Notification.Position.MIDDLE);
}
});
}
private void refreshGeneralPendingRequestsGrid(final Employee employee,
final Team team,
final TimeOffRequestType category) {
pendingRequestsGrid.setPagingDataProvider((page, pageSize) -> {
int start = (int) (page * pendingRequestsGrid.getPageSize());
return fetchFilteredPendingRequests(start, pageSize, employee, team, category);
});
pendingRequestsGrid.getDataProvider().refreshAll();
}
private List<TimeOffRequest> fetchFilteredPendingRequests(final int start,
final int pageSize,
final Employee employee,
final Team team,
final TimeOffRequestType category) {
List<TimeOffRequest> filteredPendingRequests
= requestService.findRequestsByState(TimeOffRequestStatus.SOLICITADO);
if (employee != null && !"TODOS".equals(employee.getFirstName())) {
filteredPendingRequests = filteredPendingRequests.stream()
.filter(emp -> emp.getEmployee().getId().equals(employee.getId()))
.collect(Collectors.toList());
}
if (team != null && !"TODOS".equals(team.getName())) {
filteredPendingRequests = filteredPendingRequests.stream()
.filter(emp -> emp.getEmployee().getTeam() != null
&& emp.getEmployee().getTeam().getId().equals(team.getId()))
.collect(Collectors.toList());
}
if (category != null && category != TimeOffRequestType.TODOS) {
filteredPendingRequests = filteredPendingRequests.stream()
.filter(emp -> emp.getCategory().equals(category))
.collect(Collectors.toList());
}
int end = Math.min(start + pageSize, filteredPendingRequests.size());
return filteredPendingRequests.subList(start, end);
}
private String getEmployeeFullName(final TimeOffRequest request) {
Employee employee = request.getEmployee();
return getEmployeeFullNameLabel(employee);
}
private String getEmployeeFullNameLabel(final Employee employee) {
return "TODOS".equals(employee.getFirstName())
? "TODOS" : employee.getFirstName() + " " + employee.getLastName();
}
private String getTeamName(final TimeOffRequest request) {
Team team = request.getEmployee().getTeam();
return team != null ? team.getName() : "Sin asignar";
}
private String getTeamLabel(final Team team) {
return "TODOS".equals(team.getName()) ? "TODOS" : team.getName();
}
private String getCategory(final TimeOffRequest request) {
return String.valueOf(request.getCategory());
}
private ComboBox<Employee> createEmployeeFilter() {
employeeFilter = new ComboBox<>("Empleado");
List<Employee> employees = new ArrayList<>(employeeService.findAllEmployees());
employees.addFirst(createAllEmployeesOption());
employeeFilter.setItems(employees);
employeeFilter.setItemLabelGenerator(this::getEmployeeFullNameLabel);
employeeFilter.setValue(employees.getFirst());
employeeFilter.addValueChangeListener(event ->
refreshGeneralPendingRequestsGrid(
event.getValue(),
teamFilter.getValue(),
categoryFilter.getValue()
)
);
return employeeFilter;
}
private ComboBox<Team> createTeamFilter() {
teamFilter = new ComboBox<>("Equipo");
List<Team> teams = new ArrayList<>(teamService.findAllTeams());
teams.addFirst(createAllTeamsOption());
teamFilter.setItems(teams);
teamFilter.setItemLabelGenerator(this::getTeamLabel);
teamFilter.setValue(teams.getFirst());
teamFilter.addValueChangeListener(event ->
refreshGeneralPendingRequestsGrid(
employeeFilter.getValue(),
event.getValue(),
categoryFilter.getValue()
)
);
return teamFilter;
}
private ComboBox<TimeOffRequestType> createCategoryFilter() {
categoryFilter = new ComboBox<>("Categoría");
categoryFilter.setItems(TimeOffRequestType.values());
categoryFilter.setValue(TimeOffRequestType.values()[0]);
categoryFilter.addValueChangeListener(event ->
refreshGeneralPendingRequestsGrid(
employeeFilter.getValue(),
teamFilter.getValue(),
event.getValue()
)
);
return categoryFilter;
}
private Employee createAllEmployeesOption() {
Employee allEmployeesOption = new Employee();
allEmployeesOption.setFirstName("TODOS");
return allEmployeesOption;
}
private Team createAllTeamsOption() {
Team allTeamsOption = new Team();
allTeamsOption.setName("TODOS");
return allTeamsOption;
}
private void navigateToMainView() {
getUI().ifPresent(ui -> ui.navigate(MainView.class));
}
}

View File

@ -1,16 +0,0 @@
package com.primefactorsolutions.views;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("PersonalDocuments")
@Route(value = "/personal-documents/me", layout = MainLayout.class)
public class PersonalDocumentsView extends Main {
}

View File

@ -1,16 +0,0 @@
package com.primefactorsolutions.views;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("ProfessionalDocuments")
@Route(value = "/professional-documents/me", layout = MainLayout.class)
public class ProfessionalDocumentsView extends Main {
}

View File

@ -28,16 +28,13 @@ import java.util.stream.Stream;
@Route(value = "/questions", layout = MainLayout.class)
@PermitAll
public class QuestionsListView extends Main {
private final QuestionService questionService;
public QuestionsListView(final QuestionService questionService) {
this.questionService = questionService;
final HorizontalLayout hl = new HorizontalLayout();
final Button addQuestion = new Button("Add Question");
addQuestion.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
this.getUI().get().navigate(QuestionView.class, "new");
});
addQuestion.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent ->
this.getUI().flatMap(ui -> ui.navigate(QuestionView.class, "new")));
hl.add(addQuestion);
final VGrid<Question> grid = new VGrid<>(Question.class);
@ -45,7 +42,7 @@ public class QuestionsListView extends Main {
grid.addComponentColumn((ValueProvider<Question, Component>) question -> {
final Button edit = new Button("Edit");
edit.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent ->
this.getUI().get().navigate(QuestionView.class, question.getId().toString()));
this.getUI().flatMap(ui -> ui.navigate(QuestionView.class, question.getId().toString())));
return edit;
});
grid.setDataProvider(new DataProvider<>() {
@ -59,6 +56,7 @@ public class QuestionsListView extends Main {
return questionService.getQuestions().size();
}
@SuppressWarnings("unused")
@Override
public Stream<Question> fetch(final Query<Question, Object> query) {
int limit = query.getLimit();

View File

@ -0,0 +1,234 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.HoursWorked;
import com.primefactorsolutions.model.Team;
import com.primefactorsolutions.service.HoursWorkedService;
import com.primefactorsolutions.service.ReportService;
import com.primefactorsolutions.service.TeamService;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Anchor;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.StreamResource;
import jakarta.annotation.security.PermitAll;
import org.springframework.beans.factory.annotation.Autowired;
import com.primefactorsolutions.service.EmployeeService;
import java.io.ByteArrayInputStream;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.TextStyle;
import java.time.temporal.WeekFields;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
@PermitAll
@Route(value = "/reportes", layout = MainLayout.class)
@PageTitle("Reporte de Horas Trabajadas")
public class ReporteView extends VerticalLayout {
private final EmployeeService employeeService;
private final HoursWorkedService hoursWorkedService;
private final ReportService reportService;
private final TeamService teamService;
private final ComboBox<Team> equipoComboBox = new ComboBox<>("Seleccionar Equipo");
private final ComboBox<String> semanaComboBox = new ComboBox<>("Seleccionar Semana");
private final Grid<Map<String, Object>> grid = new Grid<>();
private final VerticalLayout headerLayout = new VerticalLayout();
private Anchor downloadLink;
private final Span semanaInfoSpan = new Span();
// Obtener el año actual
private int currentYear = LocalDate.now().getYear();
@Autowired
public ReporteView(final HoursWorkedService hoursWorkedService,
final ReportService reportService, final TeamService teamService,
final EmployeeService employeeService) {
this.hoursWorkedService = hoursWorkedService;
this.reportService = reportService;
this.teamService = teamService;
this.employeeService = employeeService;
H2 title = new H2("Reporte de Horas Trabajadas");
add(title);
List<Team> teams = teamService.findAllTeams();
equipoComboBox.setItems(teams);
equipoComboBox.setItemLabelGenerator(Team::getName);
// Configurar el ComboBox de semanas
initializeSemanaComboBox();
// Listener para actualizar `semanaInfoSpan` con la selección del usuario en `semanaComboBox`
semanaComboBox.addValueChangeListener(event -> {
String selectedWeek = event.getValue();
semanaInfoSpan.setText(selectedWeek != null ? selectedWeek : "Selecciona una semana");
});
Button reportButton = new Button("Generar Reporte de Horas Trabajadas",
event -> generateHoursWorkedReport());
HorizontalLayout filtersLayout = new HorizontalLayout(equipoComboBox, semanaComboBox, reportButton);
add(filtersLayout);
// Añadir `headerLayout` al diseño principal para el encabezado dinámico
add(headerLayout);
updateHeaderLayout(null, null);
grid.addColumn(map -> map.get("Empleado")).setHeader("Empleado");
grid.addColumn(map -> map.get("Horas Trabajadas")).setHeader("Horas Trabajadas");
grid.addColumn(map -> map.get("Horas Pendientes")).setHeader("Horas Pendientes");
grid.addColumn(map -> map.get("Observaciones")).setHeader("Observaciones");
add(grid);
}
private void initializeSemanaComboBox() {
int year = LocalDate.now().getYear();
LocalDate startOfYear = LocalDate.of(year, 1, 5); // Suponemos que la semana comienza el 5 de enero.
List<String> semanas = startOfYear.datesUntil(LocalDate.of(year + 1, 1, 1),
java.time.Period.ofWeeks(1))
.map(date -> {
int weekNumber = date.get(WeekFields.of(DayOfWeek.MONDAY, 1)
.weekOfWeekBasedYear());
LocalDate startOfWeek = date;
LocalDate endOfWeek = startOfWeek.plusDays(6);
return String.format("Semana %d: %s - %s",
weekNumber,
startOfWeek.getDayOfMonth() + " de " + startOfWeek.getMonth()
.getDisplayName(TextStyle.FULL, Locale.getDefault()),
endOfWeek.getDayOfMonth() + " de " + endOfWeek.getMonth()
.getDisplayName(TextStyle.FULL, Locale.getDefault())
);
})
.collect(Collectors.toList());
semanaComboBox.setItems(semanas);
semanaComboBox.setPlaceholder("Seleccione una semana");
}
private void generateHoursWorkedReport() {
Team selectedEquipo = equipoComboBox.getValue();
String selectedWeek = semanaComboBox.getValue();
if (selectedEquipo == null || selectedWeek == null) {
Notification.show("Por favor, selecciona un equipo y una semana para generar el reporte.",
3000, Notification.Position.MIDDLE);
return;
}
int weekNumber = Integer.parseInt(selectedWeek.split(" ")[1].replace(":", ""));
LocalDate selectedDate = LocalDate.now().with(WeekFields.of(DayOfWeek.FRIDAY, 1)
.weekOfWeekBasedYear(), weekNumber);
updateHeaderLayout(selectedEquipo, selectedDate);
List<HoursWorked> hoursWorkedList = hoursWorkedService.findAll().stream()
.filter(hw -> hw.getEmployee().getTeam().getId().equals(selectedEquipo
.getId()) && hw.getWeekNumber() == weekNumber)
.collect(Collectors.toList());
System.out.println(hoursWorkedList);
if (hoursWorkedList.isEmpty()) {
Notification.show("No hay horas trabajadas disponibles para generar el reporte.",
3000, Notification.Position.MIDDLE);
return;
}
List<Map<String, Object>> data = hoursWorkedList.stream()
.map(hoursWorked -> {
Map<String, Object> map = new HashMap<>();
map.put("ID", hoursWorked.getId().toString());
map.put("Employee ID", hoursWorked.getEmployee().getId().toString());
map.put("Empleado", hoursWorked.getEmployee().getFirstName() + " "
+ hoursWorked.getEmployee().getLastName());
map.put("Horas Trabajadas", hoursWorked.getTotalHours());
map.put("Horas Pendientes", 40 - hoursWorked.getTotalHours());
map.put("Observaciones", "");
return map;
})
.collect(Collectors.toList());
grid.setItems(data);
generateExcelDownloadLink(data, weekNumber);
}
private void updateHeaderLayout(final Team team, final LocalDate dateInWeek) {
headerLayout.removeAll();
if (team != null && dateInWeek != null) {
LocalDate startOfWeek = dateInWeek.with(DayOfWeek.FRIDAY);
LocalDate endOfWeek = dateInWeek.with(DayOfWeek.THURSDAY);
int weekNumber = getWeekOfYear(dateInWeek);
String formattedStartDate = startOfWeek.getDayOfMonth() + " de "
+ startOfWeek.getMonth().getDisplayName(TextStyle.FULL, Locale.getDefault());
String formattedEndDate = endOfWeek.getDayOfMonth() + " de "
+ endOfWeek.getMonth().getDisplayName(TextStyle.FULL, Locale.getDefault());
headerLayout.add(new Span("Informe "
+ String.format("%03d", weekNumber) + "/" + currentYear) {{
getStyle().set("font-size", "24px");
getStyle().set("font-weight", "bold");
}});
String teamLeadName = employeeService.getTeamLeadName(team.getId());
headerLayout.add(
new Span("Asunto: Informe Semanal de Horas Trabajadas") {{
getStyle().set("font-size", "18px");
}},
semanaInfoSpan,
new Span("Horas a cumplir: 40 horas") {{
getStyle().set("font-size", "18px");
}},
new Span("Equipo: " + team.getName()) {{
getStyle().set("font-size", "18px");
}},
new Span("Team Lead: " + teamLeadName) {{
getStyle().set("font-size", "18px");
}}
);
}
}
private void generateExcelDownloadLink(final List<Map<String, Object>> data, final int weekNumber) {
try {
List<String> headers = List.of("Empleado",
"Horas Trabajadas", "Horas Pendientes", "Observaciones");
String selectedTeam = equipoComboBox.getValue().getName();
byte[] excelBytes = reportService.writeAsExcel(
"hours_worked_report", headers, data, selectedTeam, weekNumber, currentYear);
StreamResource excelResource = new StreamResource("hours_worked_report.xlsx",
() -> new ByteArrayInputStream(excelBytes));
if (downloadLink == null) {
downloadLink = new Anchor(excelResource, "Descargar Reporte en Excel");
downloadLink.getElement().setAttribute("download", true);
add(downloadLink);
} else {
downloadLink.setHref(excelResource);
}
} catch (Exception e) {
Notification.show("Error al generar el reporte de horas trabajadas en Excel.",
3000, Notification.Position.MIDDLE);
}
}
private int getWeekOfYear(final LocalDate date) {
return date.get(WeekFields.of(Locale.getDefault()).weekOfWeekBasedYear());
}
}

View File

@ -0,0 +1,423 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.*;
import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.TimeOffRequestService;
import com.primefactorsolutions.service.VacationService;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.BeforeEvent;
import com.vaadin.flow.router.HasUrlParameter;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.grid.PagingGrid;
import java.time.LocalDate;
import java.time.Period;
import java.util.*;
import java.util.stream.Collectors;
import static com.primefactorsolutions.views.Constants.PAGE_SIZE;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("RequestEmployee")
@Route(value = "/requests", layout = MainLayout.class)
public class RequestEmployeeView extends Div implements HasUrlParameter<String> {
private final TimeOffRequestService requestService;
private final EmployeeService employeeService;
private final VacationService vacationService;
private final PagingGrid<TimeOffRequest> requestGrid = new PagingGrid<>(TimeOffRequest.class);
private List<TimeOffRequest> requests = Collections.emptyList();
private ComboBox<TimeOffRequestType> categoryFilter;
private ComboBox<TimeOffRequestStatus> stateFilter;
private UUID employeeId;
private TimeOffRequest request;
public RequestEmployeeView(final TimeOffRequestService requestService,
final EmployeeService employeeService,
final VacationService vacationService) {
this.requestService = requestService;
this.employeeService = employeeService;
this.vacationService = vacationService;
}
private void initializeView() {
requestService.updateRequestStatuses();
setupFilters();
setupGrid();
add(requestGrid, createActionButtons(), createSummaryLayout());
refreshRequestGrid(null, null);
}
private void setupFilters() {
categoryFilter = createCategoryFilter();
stateFilter = createStateFilter();
add(categoryFilter, stateFilter);
}
private ComboBox<TimeOffRequestType> createCategoryFilter() {
categoryFilter = new ComboBox<>("Categoría");
categoryFilter.setItems(TimeOffRequestType.values());
categoryFilter.setValue(TimeOffRequestType.values()[0]);
categoryFilter.addValueChangeListener(event -> refreshRequestGrid(event.getValue(), stateFilter.getValue()));
return categoryFilter;
}
private ComboBox<TimeOffRequestStatus> createStateFilter() {
stateFilter = new ComboBox<>("Estado de la solicitud");
stateFilter.setItems(TimeOffRequestStatus.values());
stateFilter.setValue(TimeOffRequestStatus.values()[0]);
stateFilter.addValueChangeListener(event -> refreshRequestGrid(categoryFilter.getValue(), event.getValue()));
return stateFilter;
}
private void setupGrid() {
requestGrid.setColumns(
"category",
"state",
"startDate",
"endDate",
"daysToBeTake");
requestGrid.getColumnByKey("category").setHeader("Categoría");
requestGrid.getColumnByKey("state").setHeader("Estado");
requestGrid.getColumnByKey("startDate").setHeader("Fecha de Inicio");
requestGrid.getColumnByKey("endDate").setHeader("Fecha de Fin");
requestGrid.getColumnByKey("daysToBeTake").setHeader("Días a Tomar");
requestGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
requestGrid.setPageSize(PAGE_SIZE);
requestGrid.asSingleSelect().addValueChangeListener(event -> {
TimeOffRequest selectedRequest = event.getValue();
if (selectedRequest != null) {
request = selectedRequest;
}
});
}
private Set<TimeOffRequestType> getStandardExclusions() {
return Set.of(
TimeOffRequestType.MATERNIDAD,
TimeOffRequestType.PATERNIDAD,
TimeOffRequestType.MATRIMONIO,
TimeOffRequestType.DUELO_1ER_GRADO,
TimeOffRequestType.DUELO_2ER_GRADO,
TimeOffRequestType.DIA_DEL_PADRE,
TimeOffRequestType.DIA_DE_LA_MADRE
);
}
private Set<TimeOffRequestType> getMaleSpecificExclusions() {
return Set.of(
TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL,
TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL
);
}
private VerticalLayout createSummaryLayout() {
Employee employee = employeeService.getEmployee(employeeId);
boolean isMale = employee.getGender() == Employee.Gender.MALE;
int currentYear = LocalDate.now().getYear();
List<Vacation> vacations = vacationService.findVacations();
double healthLicence = 2;
List<TimeOffRequest> healthRequests = requestService
.findByEmployeeAndCategory(employeeId, TimeOffRequestType.PERMISOS_DE_SALUD);
if (healthRequests != null && !healthRequests.isEmpty()) {
healthLicence = healthRequests.getLast().getDaysBalance();
}
double totalFixedAndMovableHolidays = calculateHolidayDays(vacations);
double totalPersonalDays = calculatePersonalDays(vacations, isMale);
List<Double> vacationDays = calculateVacationDays(employee);
double utilizedVacationCurrentDays = vacationDays.get(1);
List<TimeOffRequest> vacationCurrentRequests = requestService
.findByEmployeeAndCategory(employeeId, TimeOffRequestType.VACACION_GESTION_ACTUAL);
if (vacationCurrentRequests != null && !vacationCurrentRequests.isEmpty()) {
utilizedVacationCurrentDays = vacationCurrentRequests.getLast().getDaysBalance();
}
double totalVacationCurrentDays = vacationDays.get(1) - (vacationDays.get(1) - utilizedVacationCurrentDays);
double utilizedVacationPreviousDays = vacationDays.get(0);
List<TimeOffRequest> vacationPreviousRequests = requestService
.findByEmployeeAndCategory(employeeId, TimeOffRequestType.VACACION_GESTION_ANTERIOR);
if (vacationPreviousRequests != null && !vacationPreviousRequests.isEmpty()) {
utilizedVacationPreviousDays = vacationPreviousRequests.getLast().getDaysBalance();
}
double totalVacationPreviousDays = vacationDays.getFirst()
- (vacationDays.getFirst() - utilizedVacationPreviousDays);
double utilizedFixedAndMovableHolidays = calculateHolidayUtilizedDays(currentYear);
double utilizedPersonalDays = calculatePersonalDaysUtilized(isMale, currentYear);
double remainingHolidayDays = totalFixedAndMovableHolidays - utilizedFixedAndMovableHolidays;
double remainingPersonalDays = (totalPersonalDays - utilizedPersonalDays) + healthLicence;
double remainingVacationDays = totalVacationCurrentDays + totalVacationPreviousDays;
double totalAvailableDays = remainingHolidayDays + remainingPersonalDays + remainingVacationDays;
return new VerticalLayout(
new Span("Total feriados fijos y movibles: " + remainingHolidayDays),
new Span("Total días libres personales: " + remainingPersonalDays),
new Span("Total vacaciones pendientes de uso: " + remainingVacationDays),
new Span("TOTAL GENERAL DE DÍAS DISPONIBLES: " + totalAvailableDays)
);
}
private double calculateHolidayDays(final List<Vacation> vacations) {
return vacations.stream()
.filter(req -> req.getType() != Vacation.Type.OTHER)
.mapToDouble(Vacation::getDuration)
.sum();
}
private double calculatePersonalDays(final List<Vacation> vacations, final boolean isMale) {
return vacations.stream()
.filter(req -> req.getType() == Vacation.Type.OTHER)
.filter(req -> !getStandardExclusions().contains(req.getCategory()))
.filter(req -> !(isMale && getMaleSpecificExclusions().contains(req.getCategory())))
.filter(req -> !req.getCategory().name().startsWith("VACACION"))
.filter(req -> req.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD)
.mapToDouble(Vacation::getDuration)
.sum();
}
private List<Double> calculateVacationDays(final Employee employee) {
List<Double> vacationDays = new ArrayList<>();
if (employee.getDateOfEntry() != null) {
LocalDate entryDate = employee.getDateOfEntry();
LocalDate today = LocalDate.now();
boolean hasAnniversaryPassed = entryDate.getMonthValue() < today.getMonthValue()
|| (entryDate.getMonthValue() == today.getMonthValue() && entryDate.getDayOfMonth()
<= today.getDayOfMonth());
LocalDate previousVacationYearDate;
LocalDate currentVacationYearDate;
if (hasAnniversaryPassed) {
previousVacationYearDate = LocalDate.of(
today.getYear() - 1,
entryDate.getMonth(),
entryDate.getDayOfMonth()
);
currentVacationYearDate = LocalDate.of(
today.getYear(),
entryDate.getMonth(),
entryDate.getDayOfMonth()
);
} else {
previousVacationYearDate = LocalDate.of(
today.getYear() - 2,
entryDate.getMonth(),
entryDate.getDayOfMonth()
);
currentVacationYearDate = LocalDate.of(
today.getYear() - 1,
entryDate.getMonth(),
entryDate.getDayOfMonth()
);
}
vacationDays.add(calculateVacationDaysSinceEntry(entryDate, previousVacationYearDate));
vacationDays.add(calculateVacationDaysSinceEntry(entryDate, currentVacationYearDate));
} else {
vacationDays.add(0.0);
vacationDays.add(0.0);
}
return vacationDays;
}
private double calculateHolidayUtilizedDays(final int year) {
return requests.stream()
.filter(this::verificationIsHoliday)
.filter(req -> req.getState() == TimeOffRequestStatus.TOMADO)
.filter(req -> getStartDateYear(req) == year)
.mapToDouble(TimeOffRequest::getDaysToBeTake)
.sum();
}
private double calculatePersonalDaysUtilized(final boolean isMale, final int year) {
return requests.stream()
.filter(req -> !verificationIsHoliday(req))
.filter(req -> req.getState() == TimeOffRequestStatus.TOMADO)
.filter(req -> !getStandardExclusions().contains(req.getCategory()))
.filter(req -> !(isMale && getMaleSpecificExclusions().contains(req.getCategory())))
.filter(req -> !req.getCategory().name().startsWith("VACACION"))
.filter(req -> req.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD)
.filter(req -> getStartDateYear(req) == year)
.mapToDouble(TimeOffRequest::getDaysToBeTake)
.sum();
}
private int getStartDateYear(final TimeOffRequest request) {
if (request.getStartDate() != null) {
return request.getStartDate().getYear();
}
return 0;
}
private double calculateVacationDaysSinceEntry(final LocalDate dateOfEntry, final LocalDate date) {
int yearsOfService = dateOfEntry != null ? Period.between(dateOfEntry, date).getYears() : 0;
if (yearsOfService > 10) {
return 30;
}
if (yearsOfService > 5) {
return 20;
}
if (yearsOfService > 1) {
return 15;
}
return 0;
}
private Boolean verificationIsHoliday(final TimeOffRequest request) {
Vacation vacation = vacationService.findVacationByCategory(request.getCategory());
return vacation.getType() != Vacation.Type.OTHER;
}
private HorizontalLayout createActionButtons() {
Button viewButton = createButton("Ver", () -> navigateToViewRequest(request));
Button editButton = createButton("Editar", () -> navigateToEditRequest(request));
Button closeButton = new Button("Salir", event -> navigateToRequestsListView());
return new HorizontalLayout(viewButton, editButton, closeButton);
}
private Button createButton(final String caption, final Runnable action) {
return new Button(caption, event -> {
if (request != null) {
action.run();
} else {
Notification.show("Seleccione una solicitud.", 3000, Notification.Position.MIDDLE);
}
});
}
private void navigateToRequestsListView() {
getUI().ifPresent(ui -> ui.navigate(RequestsListView.class));
}
private void navigateToEditRequest(final TimeOffRequest request) {
navigateToRequestView(request, "edit");
}
private void navigateToViewRequest(final TimeOffRequest request) {
navigateToRequestView(request, "view");
}
private void navigateToRequestView(final TimeOffRequest request, final String action) {
getUI().ifPresent(ui -> ui.navigate(RequestView.class, request.getId().toString() + "/" + action));
}
private void refreshRequestGrid(final TimeOffRequestType category, final TimeOffRequestStatus state) {
requestGrid.setPagingDataProvider((page, pageSize) -> {
int start = (int) (page * requestGrid.getPageSize());
return fetchFilteredTimeOffRequests(start, pageSize, category, state);
});
requestGrid.getDataProvider().refreshAll();
}
private List<TimeOffRequest> fetchFilteredTimeOffRequests(final int start,
final int pageSize,
final TimeOffRequestType category,
final TimeOffRequestStatus state) {
requests = requestService.findRequestsByEmployeeId(employeeId);
generateRequests();
if (category != null && !"TODOS".equals(category.name())) {
requests = requests.stream()
.filter(req -> req.getCategory().equals(category))
.collect(Collectors.toList());
}
if (state != null && !"TODOS".equals(state.name())) {
requests = requests.stream()
.filter(req -> req.getState().equals(state))
.collect(Collectors.toList());
}
int end = Math.min(start + pageSize, requests.size());
return requests.subList(start, end);
}
public void generateRequests() {
boolean isMale = isEmployeeMale();
for (TimeOffRequestType type : TimeOffRequestType.values()) {
if (shouldIncludeRequest(type) && isValidRequestType(type, isMale)) {
TimeOffRequest request = createRequest(type);
if (isVacationExpired(request)) {
request.setState(TimeOffRequestStatus.VENCIDO);
} else {
request.setState(TimeOffRequestStatus.PENDIENTE);
}
requests.add(request);
}
}
}
private boolean isEmployeeMale() {
return employeeService.getEmployee(employeeId).getGender() == Employee.Gender.MALE;
}
private boolean isValidRequestType(final TimeOffRequestType type, final boolean isMale) {
return !getStandardExclusions().contains(type)
&& !(isMale && getMaleSpecificExclusions().contains(type))
&& type != TimeOffRequestType.TODOS;
}
private TimeOffRequest createRequest(final TimeOffRequestType type) {
TimeOffRequest request = new TimeOffRequest();
request.setCategory(type);
return request;
}
private boolean isVacationExpired(final TimeOffRequest request) {
Vacation vacation = vacationService.findVacationByCategory(request.getCategory());
if (vacation != null && vacation.getMonthOfYear() != null && vacation.getDayOfMonth() != null) {
int vacationMonth = vacation.getMonthOfYear();
int vacationDay = vacation.getDayOfMonth();
int currentMonth = LocalDate.now().getMonthValue();
int currentDay = LocalDate.now().getDayOfMonth();
return vacationMonth < currentMonth || (vacationMonth == currentMonth && vacationDay < currentDay);
}
return false;
}
private boolean shouldIncludeRequest(final TimeOffRequestType type) {
List<TimeOffRequest> existingRequest = requestService.findByEmployeeAndCategory(employeeId, type);
return existingRequest.isEmpty();
}
@Override
public void setParameter(final BeforeEvent event, final String parameter) {
employeeId = UUID.fromString(parameter);
Employee employee = employeeService.getEmployee(employeeId);
requests = requestService.findRequestsByEmployeeId(employeeId);
setViewTitle(employee.getFirstName() + " " + employee.getLastName(), employee.getTeam().getName());
requestGrid.setItems(requests);
initializeView();
}
private void setViewTitle(final String employeeName, final String employeeTeam) {
addComponentAsFirst(new H3("Nombre del empleado: " + employeeName));
addComponentAtIndex(1, new H3("Equipo: " + employeeTeam));
}
}

View File

@ -0,0 +1,511 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.*;
import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.TimeOffRequestService;
import com.primefactorsolutions.service.VacationService;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.datepicker.DatePicker;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.NumberField;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.router.*;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.UUID;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("Request")
@Route(value = "/requests/new", layout = MainLayout.class)
public class RequestRegisterView extends VerticalLayout {
private final ComboBox<Employee> employeeComboBox = new ComboBox<>("Empleado");
private final ComboBox<TimeOffRequestType> categoryComboBox = new ComboBox<>("Categoría");
private final NumberField availableDaysField = new NumberField("Días disponibles");
private final DatePicker startDatePicker = new DatePicker("Fecha de inicio");
private final DatePicker endDatePicker = new DatePicker("Fecha final");
private final NumberField daysToBeTakenField = new NumberField("Días a tomar");
private final NumberField balanceDaysField = new NumberField("Días de saldo");
private final TimeOffRequestService requestService;
private final EmployeeService employeeService;
private final VacationService vacationService;
private final Binder<TimeOffRequest> binder;
private Vacation vacation;
private Employee employee;
private LocalDate endDate;
private Button saveButton;
private Button closeButton;
public RequestRegisterView(final TimeOffRequestService requestService,
final EmployeeService employeeService,
final VacationService vacationService) {
this.requestService = requestService;
this.employeeService = employeeService;
this.vacationService = vacationService;
this.binder = new Binder<>(TimeOffRequest.class);
initializeView();
}
private void initializeView() {
requestService.updateRequestStatuses();
configureFormFields();
configureButtons();
configureBinder();
setupFormLayout();
configureInitialFieldStates();
}
private void configureInitialFieldStates() {
categoryComboBox.setEnabled(false);
startDatePicker.setEnabled(false);
endDatePicker.setEnabled(false);
availableDaysField.setReadOnly(true);
daysToBeTakenField.setReadOnly(true);
balanceDaysField.setReadOnly(true);
}
private void configureFormFields() {
employeeComboBox.setItems(employeeService.findAllEmployees());
employeeComboBox.setItemLabelGenerator(emp -> emp.getFirstName() + " " + emp.getLastName());
employeeComboBox.addValueChangeListener(event -> {
employee = event.getValue();
handleEmployeeSelection(event.getValue());
});
categoryComboBox.addValueChangeListener(event -> {
onCategoryChange(event.getValue());
handleCategorySelection(event.getValue());
});
startDatePicker.addValueChangeListener(event -> updateDatePickerMinValues());
endDatePicker.addValueChangeListener(event -> calculateDays());
}
private void configureBinder() {
binder.forField(employeeComboBox)
.bind(TimeOffRequest::getEmployee, TimeOffRequest::setEmployee);
binder.forField(categoryComboBox)
.bind(TimeOffRequest::getCategory, TimeOffRequest::setCategory);
binder.forField(availableDaysField)
.bind(TimeOffRequest::getAvailableDays, TimeOffRequest::setAvailableDays);
binder.forField(startDatePicker)
.bind(TimeOffRequest::getStartDate, TimeOffRequest::setStartDate);
binder.forField(endDatePicker)
.bind(TimeOffRequest::getEndDate, TimeOffRequest::setEndDate);
binder.forField(daysToBeTakenField)
.bind(TimeOffRequest::getDaysToBeTake, TimeOffRequest::setDaysToBeTake);
binder.forField(balanceDaysField)
.bind(TimeOffRequest::getDaysBalance, TimeOffRequest::setDaysBalance);
binder.setBean(new TimeOffRequest());
}
private void handleEmployeeSelection(final Employee selectedEmployee) {
if (selectedEmployee != null) {
categoryComboBox.clear();
availableDaysField.clear();
startDatePicker.clear();
endDatePicker.clear();
daysToBeTakenField.clear();
balanceDaysField.clear();
categoryComboBox.setEnabled(true);
startDatePicker.setEnabled(false);
endDatePicker.setEnabled(false);
filterCategories(selectedEmployee);
}
}
private void filterCategories(final Employee employee) {
categoryComboBox.clear();
List<TimeOffRequest> employeeRequests = requestService.findRequestsByEmployeeId(employee.getId());
List<TimeOffRequestType> allCategories = Arrays.asList(TimeOffRequestType.values());
List<TimeOffRequestType> availableCategories = allCategories.stream()
.filter(category -> isCategoryAvailable(employeeRequests, category))
.filter(category -> isCategoryAllowedByGender(category, employee.getGender()))
.filter(category -> category != TimeOffRequestType.TODOS)
.filter(category -> shouldIncludeVacationGestionActual(employeeRequests, category))
.filter(category -> shouldIncludeVacationGestionAnterior(employeeRequests, category))
.toList();
categoryComboBox.setItems(availableCategories);
}
private boolean shouldIncludeVacationGestionActual(final List<TimeOffRequest> employeeRequests,
final TimeOffRequestType category) {
if (category != TimeOffRequestType.VACACION_GESTION_ACTUAL) {
return true;
}
return employeeRequests.stream()
.anyMatch(request -> request.getCategory() == TimeOffRequestType.VACACION_GESTION_ANTERIOR
&& request.getDaysBalance() == 0
&& request.getState() == TimeOffRequestStatus.TOMADO);
}
private boolean shouldIncludeVacationGestionAnterior(final List<TimeOffRequest> employeeRequests,
final TimeOffRequestType category) {
if (category != TimeOffRequestType.VACACION_GESTION_ANTERIOR) {
return true;
}
return employeeRequests.stream()
.noneMatch(request -> request.getCategory() == TimeOffRequestType.VACACION_GESTION_ANTERIOR
&& request.getDaysBalance() == 0);
}
private void onCategoryChange(final TimeOffRequestType selectedCategory) {
if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ACTUAL
|| selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) {
startDatePicker.setEnabled(true);
endDatePicker.setEnabled(true);
} else {
startDatePicker.setEnabled(true);
endDatePicker.setEnabled(false);
}
}
private boolean isCategoryAvailable(final List<TimeOffRequest> employeeRequests,
final TimeOffRequestType category) {
List<TimeOffRequest> requestsByCategory = employeeRequests.stream()
.filter(request -> request.getCategory() == category)
.toList();
if (requestsByCategory.isEmpty()) {
return true;
}
TimeOffRequest latestRequest = requestsByCategory.stream()
.max(Comparator.comparing(TimeOffRequest::getStartDate))
.orElse(null);
boolean isSpecialCategory = category == TimeOffRequestType.PERMISOS_DE_SALUD
|| category == TimeOffRequestType.VACACION_GESTION_ACTUAL
|| category == TimeOffRequestType.VACACION_GESTION_ANTERIOR;
if (isSpecialCategory) {
return (latestRequest.getState() == TimeOffRequestStatus.TOMADO
&& latestRequest.getDaysBalance() > 0)
|| latestRequest.getState() == TimeOffRequestStatus.RECHAZADO
|| (latestRequest.getState() == TimeOffRequestStatus.TOMADO
&& latestRequest.getDaysBalance() == 0
&& latestRequest.getExpiration().isBefore(LocalDate.now()));
} else {
return (latestRequest.getState() == TimeOffRequestStatus.TOMADO
&& latestRequest.getExpiration().isBefore(LocalDate.now()))
|| latestRequest.getState() == TimeOffRequestStatus.RECHAZADO;
}
}
private boolean isCategoryAllowedByGender(final TimeOffRequestType category, final Employee.Gender gender) {
if (gender == Employee.Gender.MALE) {
return category != TimeOffRequestType.MATERNIDAD
&& category != TimeOffRequestType.DIA_DE_LA_MADRE
&& category != TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL
&& category != TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL;
} else {
return category != TimeOffRequestType.DIA_DEL_PADRE
&& category != TimeOffRequestType.PATERNIDAD;
}
}
private void handleCategorySelection(final TimeOffRequestType selectedCategory) {
if (selectedCategory != null) {
updateAvailableDays(selectedCategory);
}
}
private void updateAvailableDays(final TimeOffRequestType selectedCategory) {
vacation = vacationService.findVacationByCategory(selectedCategory);
UUID employeeId = employeeComboBox.getValue().getId();
List<TimeOffRequest> requests = requestService.findByEmployeeAndCategory(employeeId, selectedCategory);
if (vacation != null) {
TimeOffRequest requestWithBalance = requests.stream()
.filter(request -> request.getDaysBalance() > 0
&& request.getState() != TimeOffRequestStatus.VENCIDO
&& request.getState() != TimeOffRequestStatus.RECHAZADO)
.max(Comparator.comparing(TimeOffRequest::getStartDate))
.orElse(null);
if (requestWithBalance != null) {
if (requestWithBalance.getState() == TimeOffRequestStatus.TOMADO
&& requestWithBalance.getDaysBalance() > 0) {
availableDaysField.setValue(requestWithBalance.getDaysBalance());
} else {
availableDaysField.setValue(vacation.getDuration());
}
} else if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ACTUAL
|| selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) {
LocalDate dateOfEntry = employeeComboBox.getValue().getDateOfEntry();
LocalDate currentDate = LocalDate.now();
long yearsOfService = ChronoUnit.YEARS.between(dateOfEntry, currentDate);
if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) {
yearsOfService -= 1;
}
if (yearsOfService > 10) {
availableDaysField.setValue(30.0);
} else if (yearsOfService > 5) {
availableDaysField.setValue(20.0);
} else if (yearsOfService > 1) {
availableDaysField.setValue(15.0);
} else {
availableDaysField.setValue(0.0);
}
} else {
availableDaysField.setValue(vacation.getDuration());
}
setDatePickerLimits(vacation);
}
}
private void setDatePickerLimits(final Vacation vacation) {
LocalDate startDate;
endDate = null;
UUID employeeId = employee.getId();
List<TimeOffRequest> previousRequests
= requestService.findByEmployeeAndCategory(employeeId, vacation.getCategory());
int startYear = calculateStartYear(previousRequests);
startDate = determineStartDate(vacation, startYear);
if (startDate.isBefore(LocalDate.now())) {
startDate = determineStartDate(vacation, startYear + 1);
}
if (startDate != null) {
if (vacation.getExpiration() != null) {
endDate = startDate.plusDays(vacation.getExpiration().intValue() - 1);
} else {
endDate = LocalDate.of(startDate.getYear(), 12, 31);
}
} else {
startDate = LocalDate.now();
}
setPickerValues(vacation, startDate);
setPickerLimits(startDate, endDate);
}
private int calculateStartYear(final List<TimeOffRequest> previousRequests) {
if (previousRequests.isEmpty()) {
return LocalDate.now().getYear();
}
int lastRequestYear = previousRequests.stream()
.max(Comparator.comparing(TimeOffRequest::getStartDate))
.map(request -> request.getStartDate().getYear())
.orElse(LocalDate.now().getYear());
if (previousRequests.getLast().getState() != TimeOffRequestStatus.RECHAZADO) {
lastRequestYear = lastRequestYear + 1;
}
int currentYear = LocalDate.now().getYear();
return Math.max(lastRequestYear, currentYear);
}
private LocalDate determineStartDate(final Vacation vacation, final int startYear) {
if (vacation.getCategory() == TimeOffRequestType.CUMPLEAÑOS && employee.getBirthday() != null) {
return LocalDate.of(startYear, employee.getBirthday().getMonth(), employee.getBirthday().getDayOfMonth());
}
if (vacation.getMonthOfYear() != null && vacation.getDayOfMonth() != null) {
return LocalDate.of(startYear, vacation.getMonthOfYear().intValue(), vacation.getDayOfMonth().intValue());
}
if (vacation.getCategory() == TimeOffRequestType.PERMISOS_DE_SALUD) {
return LocalDate.now();
}
return LocalDate.now();
}
private void setPickerValues(final Vacation vacation, final LocalDate startDate) {
startDatePicker.setValue(startDate);
if ((vacation.getDuration() != null && vacation.getDuration() == 0.5)
|| vacation.getCategory() == TimeOffRequestType.PERMISOS_DE_SALUD
|| vacation.getCategory() == TimeOffRequestType.CUMPLEAÑOS) {
endDatePicker.setValue(startDate);
} else {
int durationDays = (vacation.getDuration() != null ? vacation.getDuration().intValue() - 1 : 0);
endDatePicker.setValue(startDate.plusDays(durationDays));
}
}
private void setPickerLimits(final LocalDate startDate, final LocalDate endDate) {
startDatePicker.setMin(startDate);
startDatePicker.setMax(endDate);
endDatePicker.setMin(startDate);
endDatePicker.setMax(endDate);
}
private void updateDatePickerMinValues() {
LocalDate startDate = startDatePicker.getValue();
if (availableDaysField.getValue() != null) {
if (availableDaysField.getValue() == 0.5) {
endDatePicker.setValue(startDate.plusDays(0));
} else {
endDatePicker.setValue(startDate.plusDays(availableDaysField.getValue().intValue() - 1));
}
calculateDays();
}
}
private void calculateDays() {
LocalDate startDate = startDatePicker.getValue();
LocalDate endDate = endDatePicker.getValue();
Double availableDays = availableDaysField.getValue();
if (areDatesValid(startDate, endDate)) {
double daysToBeTaken = calculateDaysBetween(startDate, endDate);
setDaysToBeTakenField(daysToBeTaken);
double balanceDays = calculateBalanceDays(availableDays, daysToBeTakenField.getValue());
balanceDaysField.setValue(balanceDays);
if (balanceDays < 0.0) {
clearFields();
}
}
}
private boolean areDatesValid(final LocalDate startDate, final LocalDate endDate) {
return startDate != null && endDate != null;
}
private double calculateDaysBetween(final LocalDate startDate, final LocalDate endDate) {
return java.time.temporal.ChronoUnit.DAYS.between(startDate, endDate) + 1;
}
private void setDaysToBeTakenField(final double daysToBeTaken) {
if (vacation.getCategory() == TimeOffRequestType.PERMISOS_DE_SALUD
|| vacation.getCategory() == TimeOffRequestType.CUMPLEAÑOS
|| vacation.getCategory() == TimeOffRequestType.DIA_DEL_PADRE
|| vacation.getCategory() == TimeOffRequestType.DIA_DE_LA_MADRE
|| vacation.getCategory() == TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL
|| vacation.getCategory() == TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL) {
daysToBeTakenField.setValue(0.5);
} else {
daysToBeTakenField.setValue(daysToBeTaken);
}
}
private double calculateBalanceDays(final double availableDays, final double daysToBeTaken) {
return availableDays - daysToBeTaken;
}
private void clearFields() {
daysToBeTakenField.clear();
balanceDaysField.clear();
endDatePicker.clear();
}
private void configureButtons() {
saveButton = new Button("Guardar", event -> saveRequest());
closeButton = new Button("Salir", event -> closeForm());
}
private void setupFormLayout() {
add(
new H3("Añadir solicitud de vacaciones"),
employeeComboBox,
categoryComboBox,
availableDaysField,
startDatePicker,
endDatePicker,
daysToBeTakenField,
balanceDaysField,
new HorizontalLayout(saveButton, closeButton)
);
}
private void saveRequest() {
if (!binder.validate().isOk()) {
Notification.show("Rellene correctamente todos los campos obligatorios.");
return;
}
if (!validateForm()) {
Notification.show("Por favor, complete los campos antes de guardar");
return;
}
TimeOffRequest request = prepareRequest();
if (request.getCategory() == TimeOffRequestType.VACACION_GESTION_ACTUAL) {
handleVacationRequest(request);
} else {
handleExistingRequests(request);
}
requestService.saveTimeOffRequest(request);
Notification.show("Solicitud guardada correctamente.");
closeForm();
}
private TimeOffRequest prepareRequest() {
TimeOffRequest request = binder.getBean();
request.setStartDate(startDatePicker.getValue());
request.setAvailableDays(availableDaysField.getValue());
request.setExpiration(endDate != null ? endDate : endDatePicker.getValue());
request.setState(TimeOffRequestStatus.SOLICITADO);
return request;
}
private void handleExistingRequests(final TimeOffRequest request) {
List<TimeOffRequest> existingRequests =
requestService.findByEmployeeAndCategory(employee.getId(), request.getCategory());
int maxRequests = request.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD
&& !request.getCategory().name().startsWith("VACACION")
&& request.getCategory() != TimeOffRequestType.CUMPLEAÑOS
? 2 : 1;
if (existingRequests.size() >= maxRequests) {
existingRequests.stream()
.min(Comparator.comparing(TimeOffRequest::getStartDate))
.ifPresent(oldestRequest -> requestService.deleteTimeOffRequest(oldestRequest.getId()));
}
}
private void handleVacationRequest(final TimeOffRequest request) {
List<TimeOffRequest> existingRequests = requestService.findByEmployeeAndCategory(
employee.getId(),
TimeOffRequestType.VACACION_GESTION_ACTUAL
);
if (!existingRequests.isEmpty()) {
TimeOffRequest existingRequest = existingRequests.getFirst();
existingRequest.setCategory(TimeOffRequestType.VACACION_GESTION_ANTERIOR);
requestService.saveTimeOffRequest(existingRequest);
}
}
private boolean validateForm() {
return employeeComboBox.getValue() != null
&& categoryComboBox.getValue() != null
&& startDatePicker.getValue() != null
&& endDatePicker.getValue() != null;
}
private void closeForm() {
getUI().ifPresent(ui -> ui.navigate(RequestsListView.class));
}
}

View File

@ -0,0 +1,160 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.*;
import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.TeamService;
import com.primefactorsolutions.service.TimeOffRequestService;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.datepicker.DatePicker;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.textfield.NumberField;
import com.vaadin.flow.router.*;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.form.BeanValidationForm;
import java.util.List;
import java.util.UUID;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("Request")
@Route(value = "/requests/:requestId?/:action?", layout = MainLayout.class)
public class RequestView extends BeanValidationForm<TimeOffRequest> implements HasUrlParameter<String> {
private final ComboBox<TimeOffRequestStatus> state = new ComboBox<>("Estado de la solicitud");
private final DatePicker expiration = new DatePicker("Vencimiento");
private final DatePicker startDate = new DatePicker("Fecha de inicio");
private final DatePicker endDate = new DatePicker("Fecha de fin");
private final NumberField availableDays = new NumberField("Días disponibles");
private final NumberField daysToBeTake = new NumberField("Días a tomar");
private final TimeOffRequestService requestService;
private final EmployeeService employeeService;
private TimeOffRequest request;
private Employee employee;
private Button saveButton;
public RequestView(final TimeOffRequestService requestService,
final EmployeeService employeeService,
final TeamService teamService) {
super(TimeOffRequest.class);
this.requestService = requestService;
this.employeeService = employeeService;
state.setItems(List.of(TimeOffRequestStatus.values()));
}
@Override
public void setParameter(final BeforeEvent beforeEvent, final String action) {
final RouteParameters params = beforeEvent.getRouteParameters();
final String requestIdString = params.get("requestId").orElse(null);
if ("new".equals(action)) {
setEntityWithEnabledSave(new TimeOffRequest());
} else {
assert requestIdString != null;
UUID requestId = UUID.fromString(requestIdString);
request = requestService.findTimeOffRequest(requestId);
UUID employeeId = request.getEmployee().getId();
employee = employeeService.getEmployee(employeeId);
setEntity(request);
configureViewOrEditAction(action);
}
}
@Override
protected List<Component> getFormComponents() {
return List.of(
createEmployeeHeader(),
createTeamHeader(),
createCategoryHeader(),
state,
expiration,
startDate,
endDate,
availableDays,
daysToBeTake,
createCloseButton()
);
}
protected Button createSaveButton() {
saveButton = new Button("Guardar");
saveButton.addClickListener(event -> saveRequest());
return saveButton;
}
protected Button createCloseButton() {
Button closeButton = new Button("Salir");
closeButton.addClickListener(event -> closeForm());
return closeButton;
}
private void setFieldsReadOnly(final boolean option) {
state.setReadOnly(option);
expiration.setReadOnly(option);
startDate.setReadOnly(option);
endDate.setReadOnly(option);
availableDays.setReadOnly(option);
daysToBeTake.setReadOnly(option);
}
private void saveRequest() {
if (isFormValid()) {
TimeOffRequest request = getEntity();
setRequestFieldValues(request);
requestService.saveTimeOffRequest(request);
Notification.show("Solicitud guardada correctamente.");
closeForm();
}
}
private void setRequestFieldValues(final TimeOffRequest request) {
request.setState(state.getValue());
request.setExpiration(expiration.getValue());
request.setStartDate(startDate.getValue());
request.setEndDate(endDate.getValue());
request.setAvailableDays(availableDays.getValue());
request.setDaysToBeTake(daysToBeTake.getValue());
}
private void closeForm() {
getUI().ifPresent(ui -> ui.navigate("requests/" + employee.getId().toString()));
}
private boolean isFormValid() {
return !state.isEmpty()
&& expiration.getValue() != null
&& startDate.getValue() != null
&& endDate.getValue() != null
&& availableDays.getValue() != null
&& daysToBeTake.getValue() != null;
}
private void configureViewOrEditAction(final String action) {
if ("edit".equals(action) && !request.getId().toString().isEmpty()) {
setFieldsReadOnly(false);
} else if ("view".equals(action) && !request.getId().toString().isEmpty()) {
setFieldsReadOnly(true);
saveButton.setEnabled(false);
}
}
private H3 createEmployeeHeader() {
return new H3("Empleado: " + employee.getFirstName() + " " + employee.getLastName());
}
private H3 createTeamHeader() {
return new H3("Equipo: " + employee.getTeam().getName());
}
private H3 createCategoryHeader() {
return new H3("Categoría: " + request.getCategory());
}
}

View File

@ -1,16 +1,413 @@
package com.primefactorsolutions.views;
import com.vaadin.flow.component.html.Main;
import com.primefactorsolutions.model.*;
import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.TeamService;
import com.primefactorsolutions.service.TimeOffRequestService;
import com.primefactorsolutions.service.VacationService;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.grid.PagingGrid;
import java.time.LocalDate;
import java.time.Period;
import java.util.*;
import java.util.stream.Collectors;
import static com.primefactorsolutions.views.Constants.PAGE_SIZE;
@SpringComponent
@Scope("prototype")
@PageTitle("Requests")
@Route(value = "/requests", layout = MainLayout.class)
@PermitAll
public class RequestsListView extends Main {
}
public class RequestsListView extends BaseView {
private final TimeOffRequestService requestService;
private final EmployeeService employeeService;
private final TeamService teamService;
private final VacationService vacationService;
private final PagingGrid<Employee> requestGrid = new PagingGrid<>();
private ComboBox<Employee> employeeFilter;
private ComboBox<Team> teamFilter;
private ComboBox<Status> stateFilter;
private UUID selectedEmployeeId;
public RequestsListView(final TimeOffRequestService requestService,
final EmployeeService employeeService,
final TeamService teamService,
final VacationService vacationService) {
this.requestService = requestService;
this.employeeService = employeeService;
this.teamService = teamService;
this.vacationService = vacationService;
initializeView();
refreshGeneralRequestGrid(null, null, null);
}
private void initializeView() {
requestService.updateRequestStatuses();
setupFilters();
setupRequestGrid();
getCurrentPageLayout().add(requestGrid);
getCurrentPageLayout().add(createActionButtons());
}
private void setupFilters() {
final HorizontalLayout hl = new HorizontalLayout();
hl.add(createEmployeeFilter());
hl.add(createTeamFilter());
hl.add(createStateFilter());
getCurrentPageLayout().add(hl);
}
private void setupRequestGrid() {
requestGrid.addColumn(this::getEmployeeFullName).setHeader("Empleado");
requestGrid.addColumn(this::getTeamName).setHeader("Equipo");
requestGrid.addColumn(this::getEmployeeStatus).setHeader("Estado del empleado");
requestGrid.addColumn(this::getGeneralTotal).setHeader("Total general");
requestGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
requestGrid.setPageSize(PAGE_SIZE);
requestGrid.asSingleSelect().addValueChangeListener(event -> {
Employee selectedRequest = event.getValue();
if (selectedRequest != null) {
selectedEmployeeId = selectedRequest.getId();
}
});
}
private HorizontalLayout createActionButtons() {
Button viewButton = new Button("Ver", event -> {
if (selectedEmployeeId != null) {
navigateToTimeOffRequestView(selectedEmployeeId);
} else {
Notification.show("Seleccione una solicitud.", 3000, Notification.Position.MIDDLE);
}
});
Button closeButton = new Button("Salir", event -> navigateToMainView());
return new HorizontalLayout(viewButton, closeButton);
}
private void refreshGeneralRequestGrid(final Employee employee,
final Team team,
final Status state) {
requestGrid.setPagingDataProvider((page, pageSize) -> {
int start = (int) (page * requestGrid.getPageSize());
return fetchFilteredEmployees(start, pageSize, employee, team, state);
});
requestGrid.getDataProvider().refreshAll();
}
private List<Employee> fetchFilteredEmployees(final int start,
final int pageSize,
final Employee employee,
final Team team,
final Status state) {
List<Employee> filteredEmployees = employeeService.findAllEmployees();
if (employee != null && !"TODOS".equals(employee.getFirstName())) {
filteredEmployees = filteredEmployees.stream()
.filter(emp -> emp.getId().equals(employee.getId()))
.collect(Collectors.toList());
}
if (team != null && !"TODOS".equals(team.getName())) {
filteredEmployees = filteredEmployees.stream()
.filter(emp -> emp.getTeam() != null && emp.getTeam().getId().equals(team.getId()))
.collect(Collectors.toList());
}
if (state != null && state != Status.TODOS) {
filteredEmployees = filteredEmployees.stream()
.filter(emp -> {
Optional<TimeOffRequest> request = requestService
.findByEmployeeAndState(emp.getId(), TimeOffRequestStatus.EN_USO);
return state == Status.EN_DESCANSO ? request.isPresent() : request.isEmpty();
})
.collect(Collectors.toList());
}
int end = Math.min(start + pageSize, filteredEmployees.size());
return filteredEmployees.subList(start, end);
}
private String getEmployeeFullName(final Employee employee) {
return "TODOS".equals(employee.getFirstName())
? "TODOS" : employee.getFirstName() + " " + employee.getLastName();
}
private String getTeamName(final Employee employee) {
Team team = employee.getTeam();
return team != null ? team.getName() : "Sin asignar";
}
private String getTeamLabel(final Team team) {
return "TODOS".equals(team.getName()) ? "TODOS" : team.getName();
}
private String getEmployeeStatus(final Employee employee) {
Optional<TimeOffRequest> activeRequest = requestService
.findByEmployeeAndState(employee.getId(), TimeOffRequestStatus.EN_USO);
return activeRequest.isPresent() ? "EN_DESCANSO" : "ACTIVO";
}
private String getGeneralTotal(final Employee employee) {
List<TimeOffRequest> employeeRequests = requestService.findRequestsByEmployeeId(employee.getId());
List<Vacation> vacations = vacationService.findVacations();
List<Double> vacationDays = calculateVacationDays(employee);
double utilizedVacationCurrentDays = vacationDays.get(1);
List<TimeOffRequest> vacationCurrentRequests = requestService
.findByEmployeeAndCategory(employee.getId(), TimeOffRequestType.VACACION_GESTION_ACTUAL);
if (vacationCurrentRequests != null && !vacationCurrentRequests.isEmpty()) {
utilizedVacationCurrentDays = vacationCurrentRequests.getLast().getDaysBalance();
}
double totalVacationCurrentDays = vacationDays.get(1) - (vacationDays.get(1) - utilizedVacationCurrentDays);
double utilizedVacationPreviousDays = vacationDays.get(0);
List<TimeOffRequest> vacationPreviousRequests = requestService
.findByEmployeeAndCategory(employee.getId(), TimeOffRequestType.VACACION_GESTION_ANTERIOR);
if (vacationPreviousRequests != null && !vacationPreviousRequests.isEmpty()) {
utilizedVacationPreviousDays = vacationPreviousRequests.getLast().getDaysBalance();
}
double totalVacationPreviousDays = vacationDays.getFirst()
- (vacationDays.getFirst() - utilizedVacationPreviousDays);
double totalUtilized = calculateTotalUtilized(employeeRequests);
double totalVacations = totalVacationCurrentDays + totalVacationPreviousDays;
double totalAvailable = calculateTotalAvailable(vacations, employeeRequests, employee);
double generalTotal = totalAvailable + totalVacations - totalUtilized;
return String.valueOf(generalTotal);
}
private Set<TimeOffRequestType> getExcludedCategories() {
return Set.of(
TimeOffRequestType.MATERNIDAD,
TimeOffRequestType.PATERNIDAD,
TimeOffRequestType.MATRIMONIO,
TimeOffRequestType.DUELO_1ER_GRADO,
TimeOffRequestType.DUELO_2ER_GRADO,
TimeOffRequestType.DIA_DEL_PADRE,
TimeOffRequestType.DIA_DE_LA_MADRE
);
}
private Set<TimeOffRequestType> getGenderSpecificExclusions() {
return Set.of(
TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL,
TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL
);
}
private double calculateTotalUtilized(final List<TimeOffRequest> employeeRequests) {
int currentYear = LocalDate.now().getYear();
return employeeRequests.stream()
.filter(Objects::nonNull)
.filter(request -> request.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD)
.filter(request -> request.getCategory() != TimeOffRequestType.VACACION_GESTION_ACTUAL)
.filter(request -> request.getCategory() != TimeOffRequestType.VACACION_GESTION_ANTERIOR)
.filter(request -> request.getStartDate() != null && (
request.getStartDate().getYear() == currentYear
|| (request.getCategory().name().startsWith("VACACION")
&& request.getStartDate().getYear() == currentYear - 1)
))
.mapToDouble(request -> request.getDaysToBeTake() != null ? request.getDaysToBeTake() : 0.0)
.sum();
}
private List<Double> calculateVacationDays(final Employee employee) {
List<Double> vacationDays = new ArrayList<>();
if (employee.getDateOfEntry() != null) {
LocalDate entryDate = employee.getDateOfEntry();
LocalDate today = LocalDate.now();
boolean hasAnniversaryPassed = entryDate.getMonthValue() < today.getMonthValue()
|| (entryDate.getMonthValue() == today.getMonthValue() && entryDate.getDayOfMonth()
<= today.getDayOfMonth());
LocalDate previousVacationYearDate;
LocalDate currentVacationYearDate;
if (hasAnniversaryPassed) {
previousVacationYearDate = LocalDate.of(
today.getYear() - 1,
entryDate.getMonth(),
entryDate.getDayOfMonth()
);
currentVacationYearDate = LocalDate.of(
today.getYear(),
entryDate.getMonth(),
entryDate.getDayOfMonth()
);
} else {
previousVacationYearDate = LocalDate.of(
today.getYear() - 2,
entryDate.getMonth(),
entryDate.getDayOfMonth()
);
currentVacationYearDate = LocalDate.of(
today.getYear() - 1,
entryDate.getMonth(),
entryDate.getDayOfMonth()
);
}
vacationDays.add(calculateVacationDaysSinceEntry(entryDate, previousVacationYearDate));
vacationDays.add(calculateVacationDaysSinceEntry(entryDate, currentVacationYearDate));
} else {
vacationDays.add(0.0);
vacationDays.add(0.0);
}
return vacationDays;
}
private double calculateTotalAvailable(final List<Vacation> vacations, final List<TimeOffRequest> employeeRequests,
final Employee employee) {
Set<TimeOffRequestType> excludedCategories = getExcludedCategories();
Set<TimeOffRequestType> genderSpecificExclusions = getGenderSpecificExclusions();
Set<TimeOffRequestType> employeeRequestCategories = employeeRequests.stream()
.map(TimeOffRequest::getCategory)
.collect(Collectors.toSet());
double healthLicence = 2;
List<TimeOffRequest> healthRequests = requestService
.findByEmployeeAndCategory(employee.getId(), TimeOffRequestType.PERMISOS_DE_SALUD);
if (healthRequests != null && !healthRequests.isEmpty()) {
healthLicence = healthRequests.getLast().getDaysBalance();
}
double totalAvailable = vacations.stream()
.filter(Objects::nonNull)
.filter(vacation -> vacation.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD)
.filter(vacation -> shouldIncludeVacation(
vacation,
excludedCategories,
genderSpecificExclusions,
employee, employeeRequestCategories
))
.mapToDouble(vacation -> vacation.getDuration() != null ? vacation.getDuration() : 0.0)
.sum();
return totalAvailable + healthLicence;
}
private double calculateVacationDaysSinceEntry(final LocalDate dateOfEntry, final LocalDate date) {
int yearsOfService = dateOfEntry != null ? Period.between(dateOfEntry, date).getYears() : 0;
if (yearsOfService > 10) {
return 30;
}
if (yearsOfService > 5) {
return 20;
}
if (yearsOfService > 1) {
return 15;
}
return 0;
}
private boolean shouldIncludeVacation(final Vacation vacation,
final Set<TimeOffRequestType> excludedCategories,
final Set<TimeOffRequestType> genderSpecificExclusions,
final Employee employee,
final Set<TimeOffRequestType> employeeRequestCategories) {
if (excludedCategories.contains(vacation.getCategory())
&& !employeeRequestCategories.contains(vacation.getCategory())) {
return false;
}
return isFemale(employee) || !genderSpecificExclusions.contains(vacation.getCategory());
}
private boolean isFemale(final Employee employee) {
return employee.getGender() == Employee.Gender.FEMALE;
}
private ComboBox<Employee> createEmployeeFilter() {
employeeFilter = new ComboBox<>("Empleado");
List<Employee> employees = new ArrayList<>(employeeService.findAllEmployees());
employees.addFirst(createAllEmployeesOption());
employeeFilter.setItems(employees);
employeeFilter.setItemLabelGenerator(this::getEmployeeFullName);
employeeFilter.setValue(employees.getFirst());
employeeFilter.addValueChangeListener(event ->
refreshGeneralRequestGrid(
event.getValue(),
teamFilter.getValue(),
stateFilter.getValue()
)
);
return employeeFilter;
}
private ComboBox<Team> createTeamFilter() {
teamFilter = new ComboBox<>("Equipo");
List<Team> teams = new ArrayList<>(teamService.findAllTeams());
teams.addFirst(createAllTeamsOption());
teamFilter.setItems(teams);
teamFilter.setItemLabelGenerator(this::getTeamLabel);
teamFilter.setValue(teams.getFirst());
teamFilter.addValueChangeListener(event ->
refreshGeneralRequestGrid(
employeeFilter.getValue(),
event.getValue(),
stateFilter.getValue()
)
);
return teamFilter;
}
private ComboBox<Status> createStateFilter() {
stateFilter = new ComboBox<>("Estado del empleado");
stateFilter.setItems(Status.values());
stateFilter.setValue(Status.values()[0]);
stateFilter.addValueChangeListener(event ->
refreshGeneralRequestGrid(
employeeFilter.getValue(),
teamFilter.getValue(),
event.getValue()
)
);
return stateFilter;
}
private enum Status {
TODOS,
EN_DESCANSO,
ACTIVO
}
private Employee createAllEmployeesOption() {
Employee allEmployeesOption = new Employee();
allEmployeesOption.setFirstName("TODOS");
return allEmployeesOption;
}
private Team createAllTeamsOption() {
Team allTeamsOption = new Team();
allTeamsOption.setName("TODOS");
return allTeamsOption;
}
private void navigateToMainView() {
getUI().ifPresent(ui -> ui.navigate(MainView.class));
}
private void navigateToTimeOffRequestView(final UUID idEmployee) {
getUI().ifPresent(ui -> ui.navigate("requests/" + idEmployee.toString()));
}
}

View File

@ -0,0 +1,71 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.service.AccountService;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.PasswordField;
import com.vaadin.flow.router.*;
import com.vaadin.flow.server.auth.AnonymousAllowed;
import java.util.List;
@Route("reset-password")
@PageTitle("PFS Intra")
@AnonymousAllowed
public class ResetPasswordView extends VerticalLayout implements BeforeEnterObserver {
private String username;
private String token;
public ResetPasswordView(final AccountService accountService) {
setSizeFull();
setAlignItems(Alignment.CENTER);
setJustifyContentMode(JustifyContentMode.CENTER);
final VerticalLayout vl = new VerticalLayout();
vl.setJustifyContentMode(JustifyContentMode.CENTER);
vl.setWidth("400px");
final PasswordField password = new PasswordField("Password");
final PasswordField confirmPassword = new PasswordField("Confirm Password");
final FormLayout formLayout = new FormLayout(password, confirmPassword);
formLayout.setColspan(password, 3);
formLayout.setColspan(confirmPassword, 3);
final Button primaryButton = new Button("Submit");
primaryButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
primaryButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
accountService.resetPassword(username, password.getValue(), token);
getUI().ifPresent(ui -> ui.navigate(MainView.class));
});
final Button secondaryButton = new Button("Cancel");
secondaryButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent ->
getUI().ifPresent(ui -> ui.navigate(MainView.class)));
HorizontalLayout hl = new HorizontalLayout(secondaryButton, primaryButton);
vl.add(new H3("PFS - Reset Password"));
vl.add(formLayout);
vl.add(hl);
add(vl);
}
@Override
public void beforeEnter(final BeforeEnterEvent beforeEnterEvent) {
final Location location = beforeEnterEvent.getLocation();
final QueryParameters queryParameters = location.getQueryParameters();
this.username = queryParameters.getParameters().getOrDefault("username", List.of()).stream()
.findFirst().orElse(null);
this.token = queryParameters.getParameters().getOrDefault("token", List.of()).stream()
.findFirst().orElse(null);
}
}

View File

@ -1,173 +0,0 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Document;
import com.primefactorsolutions.model.DocumentType;
import com.primefactorsolutions.service.DocumentService;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.html.IFrame;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.upload.Upload;
import com.vaadin.flow.component.upload.receivers.MemoryBuffer;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
import java.io.IOException;
import java.util.Base64;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("WorkDocuments")
@Route(value = "/work-documents/me", layout = MainLayout.class)
public class WorkDocumentsView extends Main {
private final DocumentService documentService;
private final MemoryBuffer buffer = new MemoryBuffer();
private String lastUploadedFileName = null;
public WorkDocumentsView(final DocumentService documentService) {
this.documentService = documentService;
initializeLayout();
}
private void initializeLayout() {
add(createRow("PaySlips", DocumentType.PAY_SLIPS,
"Employment Contract", DocumentType.EMPLOYMENT_CONTRACT));
add(createRow("Work Certificates", DocumentType.WORK_CERTIFICATES,
"NDA", DocumentType.NDA));
add(createRow("Memorandums", DocumentType.MEMORANDUMS,
"Contract Approval MTEPS", DocumentType.CONTRACT_APPROVAL_MTEPS));
}
private HorizontalLayout createRow(
final String title1,
final DocumentType type1,
final String title2,
final DocumentType type2) {
HorizontalLayout row = new HorizontalLayout();
row.add(
createDocumentSection(title1, type1),
createDocumentSection(title2, type2)
);
return row;
}
private Div createDocumentSection(final String title, final DocumentType documentType) {
Div section = new Div();
section.add(new H2(title));
Upload upload = createUploadComponent();
Button viewButton = createViewButton(documentType);
Button saveButton = createSaveButton(viewButton, documentType);
section.add(createLayout(upload),
createLayout(viewButton, new Button("Edit"), saveButton),
createAdditionalButtons());
return section;
}
private Upload createUploadComponent() {
Upload upload = new Upload(buffer);
upload.setMaxFiles(1);
upload.setAcceptedFileTypes(".pdf");
upload.addSucceededListener(event -> handleUploadSuccess(event.getFileName()));
return upload;
}
private Button createViewButton(final DocumentType documentType) {
Button viewButton = new Button("View");
viewButton.setEnabled(documentExists(documentType));
viewButton.addClickListener(event -> viewDocument(documentType));
return viewButton;
}
private Button createSaveButton(final Button viewButton, final DocumentType documentType) {
Button saveButton = new Button("Save");
saveButton.addClickListener(event -> saveFile(documentType, viewButton));
return saveButton;
}
private void handleUploadSuccess(final String fileName) {
lastUploadedFileName = fileName;
Notification.show("File uploaded successfully");
}
private HorizontalLayout createLayout(final Component... components) {
if (components.length != 1 && components.length != 3) {
throw new IllegalArgumentException("This method only accepts 1 or 3 components.");
}
HorizontalLayout layout = new HorizontalLayout(components);
layout.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
return layout;
}
private HorizontalLayout createAdditionalButtons() {
return createLayout(new Button("Print"), new Button("Download"), new Button("Delete"));
}
private void saveFile(final DocumentType documentType, final Button viewButton) {
if (lastUploadedFileName == null) {
Notification.show("Please upload a file first.");
return;
}
try {
byte[] content = buffer.getInputStream().readAllBytes();
documentService.saveDocument(new Document(lastUploadedFileName, documentType, content));
Notification.show("File saved successfully.");
viewButton.setEnabled(true);
} catch (IOException e) {
Notification.show("Error saving file: " + e.getMessage());
}
}
private boolean documentExists(final DocumentType documentType) {
return documentService.getAllDocuments().stream()
.anyMatch(doc -> doc.getDocumentType() == documentType);
}
private void viewDocument(final DocumentType documentType) {
documentService.getAllDocuments().stream()
.filter(doc -> doc.getDocumentType() == documentType)
.findFirst()
.ifPresentOrElse(this::showPdfDialog, () -> Notification.show("Document not found."));
}
private void showPdfDialog(final Document document) {
Dialog dialog = createDialog(document.getFileData());
dialog.open();
}
private Dialog createDialog(final byte[] fileData) {
Dialog dialog = new Dialog();
dialog.setModal(true);
dialog.setCloseOnEsc(true);
dialog.setCloseOnOutsideClick(true);
IFrame pdfFrame = new IFrame();
pdfFrame.setSrc(createPdfResource(fileData));
pdfFrame.setWidth("800px");
pdfFrame.setHeight("600px");
Button closeButton = new Button("Close", event -> dialog.close());
VerticalLayout layout = new VerticalLayout(pdfFrame, closeButton);
layout.setAlignItems(FlexComponent.Alignment.CENTER);
dialog.add(layout);
return dialog;
}
private String createPdfResource(final byte[] fileData) {
return "data:application/pdf;base64," + Base64.getEncoder().encodeToString(fileData);
}
}

View File

@ -0,0 +1,3 @@
spring.ldap.url=ldap://localhost:8391
spring.ldap.embedded.port=8391
application.jwtSecret=test123

View File

@ -44,3 +44,5 @@ spring.sql.init.mode=${SQL_INIT:embedded}
spring.h2.console.enabled=true
spring.h2.console.settings.web-allow-others=true
application.jwtSecret=${JWT_SECRET:changeme}

View File

@ -9,19 +9,87 @@ insert into ASSESSMENT_QUESTIONS (assessment_id, question_id) values ('46b153f4-
insert into ASSESSMENT_QUESTIONS (assessment_id, question_id) values ('46b153f4-23fd-462f-8430-fbe67b83caab', '8a4b213c-ca81-4c38-b56d-d7028c2dde88');
insert into employee (id, version, user_name, first_name, last_name, status) values ('e99b7af5-7d3a-4c0f-b8bc-e8d0388d8fc4', 1, 'jperez', 'Juan', 'Perez Condori', 'INACTIVE');
insert into employee (id, version, user_name, first_name, last_name, status) values ('f6ab3c6d-7078-45f6-9b22-4e37637bfec6', 1, 'agarcia', 'Ana', 'Garcia Rojas', 'ACTIVE');
insert into employee (id, version, user_name, first_name, last_name, status) values ('2e2293b1-3f9a-4f3d-abc8-32639b0a5e15', 1, 'clopez', 'Carlos', 'Lopez Mendoza', 'INACTIVE');
insert into employee (id, version, user_name, first_name, last_name, status) values ('4b1c6c35-4627-4b35-b6e9-dc75c68b2c31', 1, 'mfernandez', 'Maria', 'Fernandez Villca', 'ACTIVE');
insert into employee (id, version, user_name, first_name, last_name, status) values ('afc5c741-f70a-4394-853b-39d51b118927', 1, 'lgutierrez', 'Luis', 'Gutierrez Mamani', 'ACTIVE');
insert into employee (id, version, user_name, first_name, last_name, status) values ('b2436b82-7b9f-4f0d-9463-f2c3173a45c3', 1, 'lmartinez', 'Laura', 'Martinez Paredes', 'INACTIVE');
insert into employee (id, version, user_name, first_name, last_name, status) values ('6e6a8a4e-9f6b-44eb-8c69-40acfdc86756', 1, 'rsantos', 'Roberto', 'Santos Escobar', 'ACTIVE');
insert into employee (id, version, user_name, first_name, last_name, status) values ('36b0d1c6-bdc0-4d98-94bb-08b9bce3f0d5', 1, 'vmorales', 'Valeria', 'Morales Ochoa', 'INACTIVE');
insert into employee (id, version, user_name, first_name, last_name, status) values ('5a1c6d80-58b3-43e3-a5a5-24b4a2d1d54a', 1, 'jramirez', 'Jorge', 'Ramirez Tapia', 'ACTIVE');
insert into employee (id, version, user_name, first_name, last_name, status) values ('9d6a5b2e-6d0b-4b89-8d6a-d3f3d1bfc047', 1, 'storres', 'Sandra', 'Torres Huanca', 'ACTIVE');
insert into employee (id, version, user_name, first_name, last_name, status) values ('f8b3e0c0-0d5a-4e5c-bf9d-207b9b5e8279', 1, 'fquispe', 'Felipe', 'Quispe Huanca', 'INACTIVE');
insert into employee (id, version, user_name, first_name, last_name, status) values ('cd80e1d0-9a08-44a6-bd63-2c63eaa003d4', 1, 'grivas', 'Gabriela', 'Rivas Arana', 'ACTIVE');
insert into employee (id, version, user_name, first_name, last_name, status) values ('62d3c1b7-815e-4e96-8d7e-f8c4236bca55', 1, 'oflores', 'Oscar', 'Flores Quiroga', 'INACTIVE');
insert into employee (id, version, user_name, first_name, last_name, status) values ('f20b7c5a-5a67-44f0-9ec1-4c1b8e80de05', 1, 'mvargas', 'Marta', 'Vargas Soria', 'ACTIVE');
insert into employee (id, version, user_name, first_name, last_name, status) values ('19b5a76e-d7b1-4b76-8b02-4d0748e85809', 1, 'aespinoza', 'Andres', 'Espinoza Chura', 'INACTIVE');
insert into employee (id, version, user_name, first_name, last_name, status) values ('5c1a7b82-832d-4f24-8377-54b77b91b6a8', 1, 'cvillanueva', 'Carla', 'Villanueva Arce', 'ACTIVE');
INSERT INTO team (id, version, name) VALUES ('b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa', 1, 'ABC');
INSERT INTO team (id, version, name) VALUES ('6d63bc15-3f8b-46f7-9cf1-7e9b0b9a2b28', 1, 'XYZ');
INSERT INTO team (id, version, name) VALUES ('c3a8a7b1-f2d9-48c0-86ea-f215c2e6b3a3', 1, 'DEF');
INSERT INTO team (id, version, name) VALUES ('8f6b61e7-efb2-4de7-b8ed-7438c9d8babe', 1, 'GHI');
INSERT INTO vacation (id, version, category, month_of_year, day_of_month, duration, expiration, type) VALUES ('123e4567-e89b-12d3-a456-426614174000', 1, 'AÑO_NUEVO', 1, 1, 1, 1, 'FIXED');
INSERT INTO vacation (id, version, category, month_of_year, day_of_month, duration, expiration, type) VALUES ('223e4567-e89b-12d3-a456-426614174001', 1, 'LUNES_CARNAVAL', 2, 12, 1, 1, 'FIXED');
INSERT INTO vacation (id, version, category, month_of_year, day_of_month, duration, expiration, type) VALUES ('323e4567-e89b-12d3-a456-426614174002', 1, 'MARTES_CARNAVAL', 2, 13, 1, 1, 'FIXED');
INSERT INTO vacation (id, version, category, month_of_year, day_of_month, duration, expiration, type) VALUES ('423e4567-e89b-12d3-a456-426614174003', 1, 'VIERNES_SANTO', 3, 29, 1, 1, 'FIXED');
INSERT INTO vacation (id, version, category, month_of_year, day_of_month, duration, expiration, type) VALUES ('523e4567-e89b-12d3-a456-426614174004', 1, 'DIA_DEL_TRABAJADOR', 5, 1, 1, 1, 'FIXED');
INSERT INTO vacation (id, version, category, month_of_year, day_of_month, duration, expiration, type) VALUES ('623e4567-e89b-12d3-a456-426614174005', 1, 'DIA_DE_LA_INDEPENDENCIA', 8, 6, 1, 1, 'FIXED');
INSERT INTO vacation (id, version, category, month_of_year, day_of_month, duration, expiration, type) VALUES ('723e4567-e89b-12d3-a456-426614174006', 1, 'NAVIDAD', 12, 25, 1, 1, 'FIXED');
INSERT INTO vacation (id, version, category, month_of_year, day_of_month, duration, expiration, type) VALUES ('823e4567-e89b-12d3-a456-426614174007', 1, 'DIA_DEL_ESTADO_PLURINACIONAL', 1, 21, 1, 30, 'MOVABLE');
INSERT INTO vacation (id, version, category, month_of_year, day_of_month, duration, expiration, type) VALUES ('923e4567-e89b-12d3-a456-426614174008', 1, 'CORPUS_CHRISTI', 5, 30, 1, 30, 'MOVABLE');
INSERT INTO vacation (id, version, category, month_of_year, day_of_month, duration, expiration, type) VALUES ('a23e4567-e89b-12d3-a456-426614174009', 1, 'AÑO_NUEVO_ANDINO', 6, 21, 1, 30, 'MOVABLE');
INSERT INTO vacation (id, version, category, month_of_year, day_of_month, duration, expiration, type) VALUES ('b23e4567-e89b-12d3-a456-42661417400a', 1, 'ANIVERSARIO_DEPARTAMENTAL', 9, 14, 1, 30, 'MOVABLE');
INSERT INTO vacation (id, version, category, month_of_year, day_of_month, duration, expiration, type) VALUES ('c23e4567-e89b-12d3-a456-42661417400b', 1, 'DIA_DE_TODOS_LOS_DIFUNTOS', 11, 2, 1, 30, 'MOVABLE');
INSERT INTO vacation (id, version, category, month_of_year, day_of_month, duration, type) VALUES ('c23e4567-e89b-12d3-a456-42661417400c', 1, 'CUMPLEAÑOS', 12, 31, 0.5, 'OTHER');
INSERT INTO vacation (id, version, category, duration, expiration, type) VALUES ('c23e4567-e89b-12d3-a456-42661417400d', 1, 'MATERNIDAD', 90, 90, 'OTHER');
INSERT INTO vacation (id, version, category, duration, expiration, type) VALUES ('c23e4567-e89b-12d3-a456-42661417400e', 1, 'PATERNIDAD', 3, 3, 'OTHER');
INSERT INTO vacation (id, version, category, duration, expiration, type) VALUES ('c23e4567-e89b-12d3-a456-42661417400f', 1, 'MATRIMONIO', 3, 3, 'OTHER');
INSERT INTO vacation (id, version, category, duration, expiration, type) VALUES ('550e8400-e29b-41d4-a716-446655440000', 1, 'DUELO_1ER_GRADO', 3, 3, 'OTHER');
INSERT INTO vacation (id, version, category, duration, expiration, type) VALUES ('c23e4567-e89b-12d3-a456-42661417400a', 1, 'DUELO_2ER_GRADO', 2, 2, 'OTHER');
INSERT INTO vacation (id, version, category, month_of_year, day_of_month, duration, expiration, type) VALUES ('c23e4567-e89b-12d3-a456-42661417401a', 1, 'DIA_DEL_PADRE', 3, 19, 0.5, 30, 'OTHER');
INSERT INTO vacation (id, version, category, month_of_year, day_of_month, duration, expiration, type) VALUES ('c23e4567-e89b-12d3-a456-42661417401b', 1, 'DIA_DE_LA_MADRE', 5, 27, 0.5, 30, 'OTHER');
INSERT INTO vacation (id, version, category, month_of_year, day_of_month, duration, expiration, type) VALUES ('c23e4567-e89b-12d3-a456-42661417401c', 1, 'DIA_DE_LA_MUJER_INTERNACIONAL', 3, 8, 0.5, 30, 'OTHER');
INSERT INTO vacation (id, version, category, month_of_year, day_of_month, duration, expiration, type) VALUES ('c23e4567-e89b-12d3-a456-42661417401d', 1, 'DIA_DE_LA_MUJER_NACIONAL', 10, 11, 0.5, 30, 'OTHER');
INSERT INTO vacation (id, version, category, duration, type) VALUES ('c23e4567-e89b-12d3-a456-42661417401e', 1, 'PERMISOS_DE_SALUD', 2, 'OTHER');
INSERT INTO vacation (id, version, category, expiration, type) VALUES ('490e5fbe-895b-42f8-b914-95437f7b39c0', 1, 'VACACION_GESTION_ACTUAL', 360, 'OTHER');
INSERT INTO vacation (id, version, category, expiration, type) VALUES ('c23e4567-e89b-12d3-a456-4266141740ff', 1, 'VACACION_GESTION_ANTERIOR', 360, 'OTHER');
insert into employee (id, version, username, first_name, last_name, status, team_id, gender, birthday, date_of_entry) values ('5c6f11fe-c341-4be7-a9a6-bba0081ad7c6', 1, 'bob', 'Bob', 'Test', 'ACTIVE','b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa', 'MALE', '2024-02-20', '2013-10-22');
insert into employee (id, version, username, first_name, last_name, status, team_id, gender, date_of_entry) values ('cba3efb7-32bc-44be-9fdc-fc5e4f211254', 1, 'ben', 'Ben', 'Test', 'ACTIVE', '6d63bc15-3f8b-46f7-9cf1-7e9b0b9a2b28', 'MALE', '2016-10-23');
insert into employee (id, version, username, first_name, last_name, status, team_id, gender, date_of_entry) values ('e99b7af5-7d3a-4c0f-b8bc-e8d0388d8fc4', 1, 'jperez', 'Juan', 'Perez Condori', 'INACTIVE', 'c3a8a7b1-f2d9-48c0-86ea-f215c2e6b3a3', 'MALE', '2022-10-22');
insert into employee (id, version, username, first_name, last_name, status, team_id, gender, date_of_entry) values ('f6ab3c6d-7078-45f6-9b22-4e37637bfec6', 1, 'agarcia', 'Ana', 'Garcia Rojas', 'ACTIVE', '8f6b61e7-efb2-4de7-b8ed-7438c9d8babe', 'FEMALE', '2024-10-24');
insert into employee (id, version, username, first_name, last_name, status, team_id, gender) values ('2e2293b1-3f9a-4f3d-abc8-32639b0a5e15', 1, 'clopez', 'Carlos', 'Lopez Mendoza', 'INACTIVE', 'b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa', 'MALE');
insert into employee (id, version, username, first_name, last_name, status, team_id, gender) values ('4b1c6c35-4627-4b35-b6e9-dc75c68b2c31', 1, 'mfernandez', 'Maria', 'Fernandez Villca', 'ACTIVE', '6d63bc15-3f8b-46f7-9cf1-7e9b0b9a2b28', 'FEMALE');
insert into employee (id, version, username, first_name, last_name, status, team_id, gender) values ('afc5c741-f70a-4394-853b-39d51b118927', 1, 'lgutierrez', 'Luis', 'Gutierrez Mamani', 'ACTIVE', 'c3a8a7b1-f2d9-48c0-86ea-f215c2e6b3a3', 'MALE');
insert into employee (id, version, username, first_name, last_name, status, team_id, gender) values ('b2436b82-7b9f-4f0d-9463-f2c3173a45c3', 1, 'lmartinez', 'Laura', 'Martinez Paredes', 'INACTIVE', '8f6b61e7-efb2-4de7-b8ed-7438c9d8babe', 'FEMALE');
insert into employee (id, version, username, first_name, last_name, status, team_id, gender) values ('6e6a8a4e-9f6b-44eb-8c69-40acfdc86756', 1, 'rsantos', 'Roberto', 'Santos Escobar', 'ACTIVE','b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa', 'MALE');
insert into employee (id, version, username, first_name, last_name, status, team_id, gender) values ('36b0d1c6-bdc0-4d98-94bb-08b9bce3f0d5', 1, 'vmorales', 'Valeria', 'Morales Ochoa', 'INACTIVE', '6d63bc15-3f8b-46f7-9cf1-7e9b0b9a2b28', 'FEMALE');
insert into employee (id, version, username, first_name, last_name, status, team_id, gender) values ('5a1c6d80-58b3-43e3-a5a5-24b4a2d1d54a', 1, 'jramirez', 'Jorge', 'Ramirez Tapia', 'ACTIVE', 'c3a8a7b1-f2d9-48c0-86ea-f215c2e6b3a3', 'MALE');
insert into employee (id, version, username, first_name, last_name, status, team_id, gender) values ('9d6a5b2e-6d0b-4b89-8d6a-d3f3d1bfc047', 1, 'storres', 'Sandra', 'Torres Huanca', 'ACTIVE', '8f6b61e7-efb2-4de7-b8ed-7438c9d8babe', 'FEMALE');
insert into employee (id, version, username, first_name, last_name, status, team_id, gender) values ('f8b3e0c0-0d5a-4e5c-bf9d-207b9b5e8279', 1, 'fquispe', 'Felipe', 'Quispe Huanca', 'INACTIVE','b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa', 'MALE');
insert into employee (id, version, username, first_name, last_name, status, team_id, gender) values ('cd80e1d0-9a08-44a6-bd63-2c63eaa003d4', 1, 'grivas', 'Gabriela', 'Rivas Arana', 'ACTIVE', '6d63bc15-3f8b-46f7-9cf1-7e9b0b9a2b28', 'FEMALE');
insert into employee (id, version, username, first_name, last_name, status, team_id, gender) values ('62d3c1b7-815e-4e96-8d7e-f8c4236bca55', 1, 'oflores', 'Oscar', 'Flores Quiroga', 'INACTIVE', 'c3a8a7b1-f2d9-48c0-86ea-f215c2e6b3a3', 'MALE');
insert into employee (id, version, username, first_name, last_name, status, team_id, gender) values ('f20b7c5a-5a67-44f0-9ec1-4c1b8e80de05', 1, 'mvargas', 'Marta', 'Vargas Soria', 'ACTIVE', '8f6b61e7-efb2-4de7-b8ed-7438c9d8babe', 'FEMALE');
insert into employee (id, version, username, first_name, last_name, status, team_id, gender) values ('19b5a76e-d7b1-4b76-8b02-4d0748e85809', 1, 'aespinoza', 'Andres', 'Espinoza Chura', 'INACTIVE','b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa', 'MALE');
insert into employee (id, version, username, first_name, last_name, status, team_id, gender) values ('5c1a7b82-832d-4f24-8377-54b77b91b6a8', 1, 'cvillanueva', 'Carla', 'Villanueva Arce', 'ACTIVE', '6d63bc15-3f8b-46f7-9cf1-7e9b0b9a2b28', 'FEMALE');
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('9d6f12ba-e341-4e7a-b8a6-cab0982bd8c1', 1, '5c6f11fe-c341-4be7-a9a6-bba0081ad7c6', 'PATERNIDAD', 'APROBADO', 3, '2024-10-03', '2024-10-01', '2024-10-03', 3, 0);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('9a6f12ba-e111-4e7a-b8a6-caa0982bd8a1', 1, '5c6f11fe-c341-4be7-a9a6-bba0081ad7c6', 'AÑO_NUEVO', 'APROBADO', 1, '2024-01-01', '2024-01-01', '2024-01-01', 1, 0);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('9b6f12ba-e222-4e7a-b8a6-caa0982bd8b2', 1, '5c6f11fe-c341-4be7-a9a6-bba0081ad7c6', 'LUNES_CARNAVAL', 'APROBADO', 1, '2025-02-12', '2025-02-12', '2025-02-12', 1, 0);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('9c6f12ba-e333-4e7a-b8a6-caa0982bd8c3', 1, '5c6f11fe-c341-4be7-a9a6-bba0081ad7c6', 'MARTES_CARNAVAL', 'SOLICITADO', 1, '2025-02-13', '2025-02-13', '2025-02-13', 1, 0);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('9d6f12ba-e444-4e7a-b8a6-caa0982bd8d4', 1, '5c6f11fe-c341-4be7-a9a6-bba0081ad7c6', 'VIERNES_SANTO', 'APROBADO', 1, '2024-03-29', '2024-03-29', '2024-03-29', 1, 0);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('9e6f12ba-e555-4e7a-b8a6-caa0982bd8e5', 1, '5c6f11fe-c341-4be7-a9a6-bba0081ad7c6', 'VACACION_GESTION_ANTERIOR', 'APROBADO', 20, '2024-11-01', '2022-11-01', '2022-11-30', 20, 0);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('8c653f2a-f9a3-4d67-b3b6-12ad98fe0983', 1, 'f6ab3c6d-7078-45f6-9b22-4e37637bfec6', 'DIA_DEL_TRABAJADOR', 'APROBADO', 1, '2024-05-01', '2024-05-01', '2024-05-01', 1, 0);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('fb9d9d75-b2ab-4ea4-b8b3-0a8f89e5c123', 1, '2e2293b1-3f9a-4f3d-abc8-32639b0a5e15', 'DIA_DE_LA_INDEPENDENCIA', 'APROBADO', 1, '2024-08-06', '2024-08-06', '2024-08-06', 1, 0);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('6fdc47a8-127b-41c4-8d12-7fc12098ab12', 1, 'b2436b82-7b9f-4f0d-9463-f2c3173a45c3', 'DIA_DE_TODOS_LOS_DIFUNTOS', 'SOLICITADO', 1, '2025-12-01', '2025-11-02', '2025-11-02', 1, 0);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('12ec8b74-983d-4a17-b67e-134f45ae904c', 1, '5c1a7b82-832d-4f24-8377-54b77b91b6a8', 'AÑO_NUEVO', 'SOLICITADO', 1, '2025-01-01', '2025-01-01', '2025-01-01', 1, 0);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('89bc4b2a-943f-487c-a9f3-bacf78145e67', 1, 'cba3efb7-32bc-44be-9fdc-fc5e4f211254', 'LUNES_CARNAVAL', 'APROBADO', 1, '2024-02-12', '2024-02-12', '2024-02-12', 1, 0);
INSERT INTO HOURS_WORKED (ID, VERSION, ACTIVIDAD, DATE, HORAS_TAREAS_ESPECIFICAS, HORASPENDIENTES, HOURS, TAREAS_ESPECIFICAS, TOTAL_HOURS, WEEK_NUMBER, EMPLOYEE_ID, TEAM_ID)
VALUES ('6d6b3a71-9b11-4526-8335-b089627a8cd6', 0, 'Scrum Meeting', '2024-11-15', 0.0, 0.0, 2.0, NULL, 8, 46, '5c6f11fe-c341-4be7-a9a6-bba0081ad7c6', 'b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa');

View File

@ -0,0 +1,128 @@
<html>
<head>
<style>
@page {
size: 7in 9.25in;
margin: 27mm 16mm 27mm 16mm;
}
body {
font-family: Arial, sans-serif;
font-size: 12px;
}
h3 {
text-align: center;
font-weight: bold;
margin-bottom: 15px;
}
table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
td, th {
border: 1px solid black;
padding: 8px;
text-align: left;
}
</style>
</head>
<body>
<div style="clear:both;"></div>
<h2>FICHA DE CONTRATACIÓN</h2>
<h3>Información General</h3>
<table>
<tr>
<td>Nombre Completo:</td>
<td>${firstName!""} ${lastName!""}</td>
<td>Sexo:</td>
<td>${gender!""}</td>
</tr>
<tr>
<td>Fecha de Nacimiento:</td>
<td>${birthday!""}</td>
<td>Edad:</td>
<td>${age!""}</td>
</tr>
<tr>
<td>Ciudad y País de Nacimiento:</td>
<td>${birthCity!""}</td>
</tr>
<tr>
<td>Dirección / Domicilio Actual:</td>
<td>${residenceAddress!""}</td>
</tr>
<tr>
<td>Estado Civil:</td>
<td>${maritalStatus!""}</td>
<td>Nro. CI:</td>
<td>${ci!""}</td>
<td>Expedido en:</td>
<td>${issuedIn!""}</td>
</tr>
<tr>
<td>Nro. Hijos:</td>
<td>${numberOfChildren!""}</td>
</tr>
<tr>
<td>Nro. Celular:</td>
<td>${phoneNumber!""}</td>
</tr>
<tr>
<td>Email:</td>
<td>${personalEmail!""}</td>
</tr>
<tr>
<td>Código de Empleado:</td>
<td>${cod!""}</td>
</tr>
<tr>
<td>Cargo:</td>
<td>${position!""}</td>
</tr>
<tr>
<td>Equipo:</td>
<td>${team!""}</td>
</tr>
</table>
<h3>Información de Contratación</h3>
<table>
<tr>
<td>Fecha de Ingreso:</td>
<td>${dateOfEntry!""}</td>
</tr>
<tr>
<td>Tipo de Contrato:</td>
<td>${contractType!""}</td>
</tr>
<tr>
<td>Salario:</td>
<td>${salary!""}</td>
</tr>
</table>
<h3>Datos Bancarios</h3>
<table>
<tr>
<td>Banco:</td>
<td>${bankName!""}</td>
</tr>
<tr>
<td>Nro. Cuenta:</td>
<td>${accountNumber!""}</td>
</tr>
</table>
<h3>Datos de Gestora Pública y Seguro Social</h3>
<table>
<tr>
<td>Código Único de Asegurado (GPSS):</td>
<td>${gpss!""}</td>
</tr>
<tr>
<td>Derechohabientes:</td>
<td>${beneficiaries!""}</td>
</tr>
</table>
</body>
</html>

View File

@ -14,21 +14,19 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import com.github.mvysny.kaributesting.v10.MockVaadin;
import com.github.mvysny.kaributesting.v10.Routes;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.test.context.ActiveProfiles;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
@SpringBootTest
@ActiveProfiles(value = "test")
public class AbstractAppTests {
private static final Routes routes = new Routes().autoDiscoverViews("com.primefactorsolutions");