Compare commits

...

18 Commits

Author SHA1 Message Date
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
6dfa5c0ec2 updated vaadin version
All checks were successful
Builder / Build-Project (push) Successful in 2m36s
2024-10-27 14:43:45 -04:00
82232d23c7 fix build
All checks were successful
Builder / Build-Project (push) Successful in 1m28s
2024-10-23 15:25:45 -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
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
35 changed files with 2503 additions and 1118 deletions

1
.gitignore vendored
View File

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

1498
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

26
pom.xml
View File

@ -11,7 +11,8 @@
<properties> <properties>
<java.version>21</java.version> <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> </properties>
<parent> <parent>
@ -119,6 +120,10 @@
<artifactId>commons-beanutils</artifactId> <artifactId>commons-beanutils</artifactId>
<version>1.9.4</version> <version>1.9.4</version>
</dependency> </dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId> <artifactId>mockito-core</artifactId>
@ -181,6 +186,16 @@
<artifactId>viritin</artifactId> <artifactId>viritin</artifactId>
<version>2.8.22</version> <version>2.8.22</version>
</dependency> </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> <dependency>
<groupId>com.flowingcode.addons</groupId> <groupId>com.flowingcode.addons</groupId>
<artifactId>simple-timer</artifactId> <artifactId>simple-timer</artifactId>
@ -240,6 +255,11 @@
<artifactId>freemarker</artifactId> <artifactId>freemarker</artifactId>
<version>2.3.32</version> <version>2.3.32</version>
</dependency> </dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
<dependency> <dependency>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
@ -279,7 +299,7 @@
<plugin> <plugin>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-maven-plugin</artifactId> <artifactId>vaadin-maven-plugin</artifactId>
<version>${vaadin.version}</version> <version>${vaadin-maven-plugin.version}</version>
<executions> <executions>
<execution> <execution>
<goals> <goals>
@ -340,7 +360,7 @@
<plugin> <plugin>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-maven-plugin</artifactId> <artifactId>vaadin-maven-plugin</artifactId>
<version>${vaadin.version}</version> <version>${vaadin-maven-plugin.version}</version>
<executions> <executions>
<execution> <execution>
<goals> <goals>

Binary file not shown.

View File

@ -0,0 +1,111 @@
package com.primefactorsolutions.model;
public final class Actividad {
private String nombre;
private double lunes;
private double martes;
private double miercoles;
private double jueves;
private double viernes;
private double sabado;
private double domingo;
public Actividad(final Builder builder) {
this.nombre = builder.nombre;
this.lunes = builder.lunes;
this.martes = builder.martes;
this.miercoles = builder.miercoles;
this.jueves = builder.jueves;
this.viernes = builder.viernes;
this.sabado = builder.sabado;
this.domingo = builder.domingo;
}
public String getNombre() {
return nombre;
}
public double getLunes() {
return lunes;
}
public double getMartes() {
return martes;
}
public double getMiercoles() {
return miercoles;
}
public double getJueves() {
return jueves;
}
public double getViernes() {
return viernes;
}
public double getSabado() {
return sabado;
}
public double getDomingo() {
return domingo;
}
// Builder para crear instancias de Actividad
public static class Builder {
private String nombre;
private double lunes;
private double martes;
private double miercoles;
private double jueves;
private double viernes;
private double sabado;
private double domingo;
public Builder nombre(final String nombre) {
this.nombre = nombre;
return this;
}
public Builder lunes(final double horas) {
this.lunes = horas;
return this;
}
public Builder martes(final double horas) {
this.martes = horas;
return this;
}
public Builder miercoles(final double horas) {
this.miercoles = horas;
return this;
}
public Builder jueves(final double horas) {
this.jueves = horas;
return this;
}
public Builder viernes(final double horas) {
this.viernes = horas;
return this;
}
public Builder sabado(final double horas) {
this.sabado = horas;
return this;
}
public Builder domingo(final double horas) {
this.domingo = horas;
return this;
}
public Actividad build() {
return new Actividad(this);
}
}
}

View File

@ -1,22 +1,23 @@
package com.primefactorsolutions.model; package com.primefactorsolutions.model;
import com.google.common.collect.Lists;
import jakarta.persistence.*;
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;
import java.time.LocalDate; import com.google.common.collect.Lists;
import java.util.Collection; import jakarta.persistence.*;
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 import java.time.LocalDate;
@Entity import java.util.Collection;
@AllArgsConstructor
@NoArgsConstructor @Data
@EqualsAndHashCode(callSuper = true) @Entity
public class Employee extends BaseEntity implements UserDetails { @AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Employee extends BaseEntity implements UserDetails {
private String username; private String username;
private String firstName; private String firstName;
private String lastName; private String lastName;
@ -122,26 +123,22 @@
ACTIVE, ACTIVE,
INACTIVE INACTIVE
} }
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
private MaritalStatus maritalStatus; private MaritalStatus maritalStatus;
public enum MaritalStatus { public enum MaritalStatus {
SINGLE, SINGLE,
MARRIED, MARRIED,
WIDOWED, WIDOWED,
DIVORCED DIVORCED
} }
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
private Gender gender; private Gender gender;
public enum Gender { public enum Gender {
MALE, MALE,
FEMALE FEMALE
} }
}
public Status getStatus() {
return status;
}
public void setStatus(final Status status) {
this.status = status;
}
}

View File

@ -0,0 +1,56 @@
package com.primefactorsolutions.model;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import java.util.UUID;
@Entity
public class HoursWorked extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
@ManyToOne
private Employee employee;
private int weekNumber;
private double totalHours;
public HoursWorked() { }
public UUID getId() {
return id;
}
public void setId(final UUID id) {
this.id = id;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(final Employee value) {
this.employee = value;
}
public int getWeekNumber() {
return weekNumber;
}
public void setWeekNumber(final int weekNumber) {
this.weekNumber = weekNumber;
}
public double getTotalHours() {
return totalHours;
}
public void setTotalHours(final double totalHours) {
this.totalHours = totalHours;
}
}

View File

@ -2,9 +2,28 @@ package com.primefactorsolutions.model;
public enum TimeOffRequestType { public enum TimeOffRequestType {
ALL, ALL,
VACATION, NEW_YEAR,
MATERNITY, MONDAY_CARNIVAL,
TUESDAY_CARNIVAL,
GOOD_FRIDAY,
LABOR_DAY,
INDEPENDENCE_DAY,
CHRISTMAS,
PRURINATIONAL_STATE_DAY,
CORPUS_CHRISTI,
ANDEAN_NEW_YEAR,
DEPARTMENTAL_ANNIVERSARY,
ALL_SOULS_DAY,
BIRTHDAY, BIRTHDAY,
FIXED_HOLIDAY, MATERNITY,
OTHER PATERNITY,
MARRIAGE,
FATHERS_DAY,
MOTHERS_DAY,
INTERNATIONAL_WOMENS_DAY,
NATIONAL_WOMENS_DAY,
HEALTH_PERMIT,
VACATION_CURRENT_MANAGEMENT,
VACATION_PREVIOUS_MANAGEMENT,
} }

View File

@ -0,0 +1,31 @@
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;
import java.time.LocalDate;
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Vacation extends BaseEntity {
@Enumerated(EnumType.STRING)
private TimeOffRequestType category;
private LocalDate vacationDate;
private Double duration;
private Double expiration;
@Enumerated(EnumType.STRING)
private Type type;
public enum Type {
FIXED,
MOVABLE,
OTHER
}
}

View File

@ -8,4 +8,6 @@ import java.util.UUID;
public interface EmployeeRepository extends JpaRepository<Employee, UUID> { public interface EmployeeRepository extends JpaRepository<Employee, UUID> {
Optional<Employee> findByUsername(String username); Optional<Employee> findByUsername(String username);
Optional<Employee> findByPersonalEmail(String personalEmail);
} }

View File

@ -0,0 +1,10 @@
package com.primefactorsolutions.repositories;
import com.primefactorsolutions.model.HoursWorked;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface HoursWorkedRepository extends JpaRepository<HoursWorked, Long> {
// Puedes definir consultas personalizadas aquí si es necesario.
}

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

@ -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

@ -18,12 +18,18 @@ import java.util.Collections;
@Service @Service
@AllArgsConstructor @AllArgsConstructor
public class EmployeeService { 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 EmployeeRepository employeeRepository;
private final LdapTemplate ldapTemplate; private final LdapTemplate ldapTemplate;
private final EntityManager entityManager; private final EntityManager entityManager;
public static final String BASE_DN = "dc=primefactorsolutions,dc=com";
protected Name buildDn(final Employee employee) { protected Name buildDn(final Employee employee) {
return LdapNameBuilder.newInstance(BASE_DN) return LdapNameBuilder.newInstance(BASE_DN)
.add("ou", "users") .add("ou", "users")
@ -63,6 +69,10 @@ public class EmployeeService {
return employees.subList(start, end); return employees.subList(start, end);
} }
public Employee getEmployeeByPersonalEmail(final String email) {
return employeeRepository.findByPersonalEmail(email).orElse(null);
}
public Employee createOrUpdate(final Employee employee) { public Employee createOrUpdate(final Employee employee) {
if (employee.getId() == null) { if (employee.getId() == null) {
final Name dn = buildDn(employee); final Name dn = buildDn(employee);
@ -74,28 +84,29 @@ public class EmployeeService {
} }
public Employee getEmployee(final UUID id) { public Employee getEmployee(final UUID id) {
Optional<Employee> employee = employeeRepository.findById(id); final Optional<Employee> employee = employeeRepository.findById(id);
return employee.orElse(null); return employee.orElse(null);
} }
private Attributes buildAttributes(final Employee employee) { private Attributes buildAttributes(final Employee employee) {
final Attributes attrs = new BasicAttributes(); final Attributes attrs = new BasicAttributes();
final BasicAttribute ocattr = new BasicAttribute("objectclass"); final BasicAttribute ocattr = new BasicAttribute(OBJECTCLASS);
ocattr.add("top"); ocattr.add(TOP);
ocattr.add("person"); ocattr.add(PERSON);
ocattr.add("organizationalPerson"); ocattr.add(ORGANIZATIONAL_PERSON);
ocattr.add("inetOrgPerson"); ocattr.add(INET_ORG_PERSON);
attrs.put(ocattr); attrs.put(ocattr);
attrs.put("cn", String.format("%s %s", employee.getFirstName(), employee.getLastName())); attrs.put("cn", String.format("%s %s", employee.getFirstName(), employee.getLastName()));
attrs.put("sn", 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("uid", employee.getUsername());
attrs.put("userpassword", String.format("%s%s", employee.getUsername(), 123)); attrs.put(USERPASSWORD, String.format("%s%s", employee.getUsername(), 123));
return attrs; return attrs;
} }
public void updatePassword(final Employee employee) { public void updatePassword(final Employee employee, final String newPassword) {
final Attribute attr = new BasicAttribute("userpassword", employee.getUsername() + "123"); final Attribute attr = new BasicAttribute(USERPASSWORD, newPassword);
final ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr); final ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr);
ldapTemplate.modifyAttributes(buildDn(employee), new ModificationItem[] {item}); ldapTemplate.modifyAttributes(buildDn(employee), new ModificationItem[] {item});

View File

@ -0,0 +1,35 @@
package com.primefactorsolutions.service;
import com.primefactorsolutions.model.HoursWorked;
import com.primefactorsolutions.repositories.HoursWorkedRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@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 HoursWorked saveHoursWorked(final HoursWorked hoursWorked) {
return hoursWorkedRepository.save(hoursWorked);
}
public HoursWorked save(final HoursWorked hoursWorked) {
return hoursWorkedRepository.save(hoursWorked);
}
public void deleteHoursWorked(final Long id) {
hoursWorkedRepository.deleteById(id);
}
}

View File

@ -2,6 +2,7 @@ package com.primefactorsolutions.service;
import com.openhtmltopdf.pdfboxout.PdfBoxRenderer; import com.openhtmltopdf.pdfboxout.PdfBoxRenderer;
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder; import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import com.primefactorsolutions.repositories.HoursWorkedRepository;
import freemarker.template.Configuration; import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper; import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template; import freemarker.template.Template;
@ -9,15 +10,77 @@ import freemarker.template.TemplateExceptionHandler;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.apache.pdfbox.io.MemoryUsageSetting; import org.apache.pdfbox.io.MemoryUsageSetting;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.io.*; import java.io.*;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.TimeZone; import java.util.TimeZone;
@Service @Service
public class ReportService { 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)
throws IOException {
return createExcelFile(reportName, headers, data);
}
private byte[] createExcelFile(final String reportName, final List<String> headers,
final List<Map<String, Object>> data)
throws IOException {
try (Workbook workbook = new XSSFWorkbook();
ByteArrayOutputStream os = new ByteArrayOutputStream()) {
Sheet sheet = workbook.createSheet(reportName);
// Crear encabezados
Row headerRow = sheet.createRow(0);
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
for (int i = 0; i < data.size(); i++) {
Row dataRow = sheet.createRow(i + 1);
Map<String, Object> rowData = data.get(i);
int cellIndex = 0;
for (String key : headers) {
Cell cell = dataRow.createCell(cellIndex++);
Object value = rowData.get(key);
switch (value) {
case String s -> cell.setCellValue(s);
case Number number -> cell.setCellValue(number.doubleValue());
case null -> cell.setCellValue(""); // Manejo de valores nulos
default -> {
}
}
}
}
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 @SneakyThrows
public byte[] writeAsPdf(final String reportName, final Object model) { public byte[] writeAsPdf(final String reportName, final Object model) {

View File

@ -0,0 +1,18 @@
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;
@Service
@AllArgsConstructor
public class VacationService {
private final VacationRepository vacationRepository;
public Vacation findVacationByCategory(final TimeOffRequestType category) {
return vacationRepository.findByCategory(category);
}
}

View File

@ -1,6 +1,7 @@
package com.primefactorsolutions.views; package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Employee; import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.model.Team;
import com.primefactorsolutions.service.EmployeeService; import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.ReportService; import com.primefactorsolutions.service.ReportService;
import com.vaadin.componentfactory.pdfviewer.PdfViewer; import com.vaadin.componentfactory.pdfviewer.PdfViewer;
@ -22,6 +23,9 @@ import com.vaadin.flow.component.textfield.EmailField;
import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.upload.Upload; import com.vaadin.flow.component.upload.Upload;
import com.vaadin.flow.component.upload.receivers.MemoryBuffer; import com.vaadin.flow.component.upload.receivers.MemoryBuffer;
import com.vaadin.flow.data.binder.Result;
import com.vaadin.flow.data.binder.ValueContext;
import com.vaadin.flow.data.converter.Converter;
import com.vaadin.flow.data.value.ValueChangeMode; import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.router.*; import com.vaadin.flow.router.*;
import com.vaadin.flow.server.StreamResource; import com.vaadin.flow.server.StreamResource;
@ -143,6 +147,18 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
configureComponents(); configureComponents();
addClassName("main-layout"); addClassName("main-layout");
getBinder().setConverter("team", new Converter<Object, Object>() {
@Override
public Result<Object> convertToModel(final Object o, final ValueContext valueContext) {
return Result.ok(new Team((String) o));
}
@Override
public Object convertToPresentation(final Object o, final ValueContext valueContext) {
return ((Team) o).getName();
}
});
} }
private void configureComponents() { private void configureComponents() {

View File

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

View File

@ -0,0 +1,161 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.model.Actividad;
import com.primefactorsolutions.service.EmployeeService;
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.grid.Grid;
import com.vaadin.flow.component.html.Label;
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.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import java.time.LocalDate;
import java.util.List;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("Hours Worked Month")
@Route(value = "/hours-worked-month/me", layout = MainLayout.class)
public class HoursWorkedMonthView extends VerticalLayout {
private final EmployeeService employeeService;
private final ComboBox<Employee> employeeComboBox = new ComboBox<>("Empleado");
private final ComboBox<String> equipoDropdown = new ComboBox<>("Equipo");
private final Grid<Actividad> grid = new Grid<>(Actividad.class);
private final Label totalCompletadoLabel = new Label();
private final Label horasPendientesLabel = new Label();
private final Label totalAcumuladasLabel = new Label();
private final Label horasAdeudadasLabel = new Label();
private final Button actualizarButton = new Button("Actualizar");
private final Button guardarButton = new Button("Guardar");
private final Button cerrarButton = new Button("Cerrar");
private LocalDate selectedMonth;
@Autowired
public HoursWorkedMonthView(final EmployeeService employeeService) {
this.employeeService = employeeService;
configurarVista();
}
private void configurarVista() {
DatePicker monthPicker = new DatePicker("Selecciona un mes");
monthPicker.setValue(LocalDate.now());
monthPicker.addValueChangeListener(event -> {
selectedMonth = event.getValue().withDayOfMonth(1);
//cargarDatosMes(selectedMonth);
});
equipoDropdown.setItems("Equipo 1", "Equipo 2", "Equipo 3");
equipoDropdown.setWidth("250px");
setEmployeeComboBoxProperties();
configurarGrid();
actualizarButton.addClickListener(event -> actualizarDatos());
guardarButton.addClickListener(event -> guardarActividades());
cerrarButton.addClickListener(event -> closeView());
HorizontalLayout headerLayout = new HorizontalLayout(monthPicker, equipoDropdown, employeeComboBox);
add(
headerLayout, grid, totalCompletadoLabel,
horasPendientesLabel, totalAcumuladasLabel,
horasAdeudadasLabel, actualizarButton,
guardarButton, cerrarButton);
}
private void setEmployeeComboBoxProperties() {
employeeComboBox.setWidth("250px");
employeeComboBox.setPlaceholder("Buscar empleado...");
employeeComboBox.setItems(employeeService.findAllEmployees());
employeeComboBox.setItemLabelGenerator(employee -> employee.getFirstName() + " " + employee.getLastName());
}
private void configurarGrid() {
grid.removeAllColumns();
grid.addColumn(Actividad::getLunes).setHeader("Lunes");
grid.addColumn(Actividad::getMartes).setHeader("Martes");
grid.addColumn(Actividad::getMiercoles).setHeader("Miércoles");
grid.addColumn(Actividad::getJueves).setHeader("Jueves");
grid.addColumn(Actividad::getViernes).setHeader("Viernes");
grid.addColumn(Actividad::getSabado).setHeader("Sábado");
grid.addColumn(Actividad::getDomingo).setHeader("Domingo");
grid.addColumn(this::calcularTotalPorDia).setHeader("Total Semanal").setKey("totalSemanal");
}
// private void cargarDatosMes(final LocalDate month) {
// List<Actividad> actividadesDelMes = obtenerActividadesDelMes(month);
// grid.setItems(actividadesDelMes);
//
// double totalCompletado = calcularTotalCompletado(actividadesDelMes);
// double horasPendientes = calcularHorasPendientes(totalCompletado);
// double totalAcumuladas = 166;
// double horasAdeudadas = 2;
//
// totalCompletadoLabel.setText("Prom. Hrs/Semana Completadas: " + totalCompletado);
// horasPendientesLabel.setText("Prom. Hrs/Semana Pendientes: " + horasPendientes);
// totalAcumuladasLabel.setText("Total Hrs./Mes Acumuladas: " + totalAcumuladas);
// horasAdeudadasLabel.setText("Total Hrs./Mes Adeudadas: " + horasAdeudadas);
// }
// private List<Actividad> obtenerActividadesDelMes(final LocalDate month) {
// LocalDate startOfMonth = month.with(TemporalAdjusters.firstDayOfMonth());
// LocalDate endOfMonth = month.with(TemporalAdjusters.lastDayOfMonth());
//
// List<Actividad> actividadesDelMes = new ArrayList<>();
//
// for (LocalDate date = startOfMonth; date.isBefore(endOfMonth.plusDays(1)); date = date.plusDays(1)) {
// Actividad actividad = new Actividad.Builder()
// .lunes(0)
// .martes(0)
// .miercoles(0)
// .jueves(0)
// .viernes(0)
// .sabado(0)
// .domingo(0)
// .build();
// actividadesDelMes.add(actividad);
// }
//
// return actividadesDelMes;
// }
private double calcularTotalCompletado(final List<Actividad> actividades) {
return actividades.stream()
.mapToDouble(this::calcularTotalPorDia)
.sum();
}
private double calcularTotalPorDia(final Actividad actividad) {
return actividad.getLunes() + actividad.getMartes() + actividad.getMiercoles()
+ actividad.getJueves() + actividad.getViernes() + actividad.getSabado()
+ actividad.getDomingo();
}
private double calcularHorasPendientes(final double totalCompletado) {
return 40 - totalCompletado;
}
private void actualizarDatos() {
Notification.show("Datos actualizados.");
}
private void guardarActividades() {
Notification.show("Actividades guardadas correctamente.");
}
private void closeView() {
getUI().ifPresent(ui -> ui.navigate(""));
}
}

View File

@ -1,19 +1,33 @@
package com.primefactorsolutions.views; package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Actividad;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.model.HoursWorked;
import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.HoursWorkedService;
import com.vaadin.flow.component.datepicker.DatePicker; import com.vaadin.flow.component.datepicker.DatePicker;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent; import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll; import jakarta.annotation.security.PermitAll;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import com.vaadin.flow.component.html.Label;
import java.time.DayOfWeek;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.temporal.IsoFields;
import java.time.temporal.WeekFields;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@SpringComponent @SpringComponent
@PermitAll @PermitAll
@ -21,20 +35,107 @@ import java.time.LocalDate;
@PageTitle("Hours Worked") @PageTitle("Hours Worked")
@Route(value = "/hours-worked/me", layout = MainLayout.class) @Route(value = "/hours-worked/me", layout = MainLayout.class)
public class HoursWorkedView extends VerticalLayout { public class HoursWorkedView extends VerticalLayout {
public HoursWorkedView() { private final List<Actividad> actividades = new ArrayList<>();
H2 title = new H2("Registro de Horas Trabajadas"); private final Grid<Actividad> grid = new Grid<>(Actividad.class);
DatePicker datePicker = new DatePicker("Selecciona una fecha"); private final ComboBox<Employee> employeeComboBox = new ComboBox<>("Employee");
datePicker.setValue(LocalDate.now()); private LocalDate selectedStartOfWeek;
private int weekNumber;
@Autowired
private final EmployeeService employeeService;
private final Label fechasLabel = new Label("Selecciona una semana para ver las fechas.");
private final Label totalCompletadoLabel = new Label();
private final Label horasPendientesLabel = new Label();
@Autowired
private final HoursWorkedService hoursWorkedService;
public HoursWorkedView(final EmployeeService employeeService, final HoursWorkedService hoursWorkedService) {
this.employeeService = employeeService;
this.hoursWorkedService = hoursWorkedService;
configurarVista();
cargarDatos();
}
private void cargarDatos() {
List<HoursWorked> listaDeHorasTrabajadas = obtenerDatos(); // Obtenemos la lista aquí
grid.setItems(actividades);
double totalHoras = calcularTotalHoras(listaDeHorasTrabajadas); // Pasa la lista aquí
}
private void setEmployeeComboBoxProperties() {
employeeComboBox.setWidth("250px");
employeeComboBox.setPlaceholder("Buscar empleado...");
employeeComboBox.setItems(employeeService.findAllEmployees());
employeeComboBox.setItemLabelGenerator(employee -> employee.getFirstName() + " " + employee.getLastName());
employeeComboBox.setAllowCustomValue(false);
employeeComboBox.addCustomValueSetListener(event ->
Notification.show("Selecciona un empleado válido de la lista.")
);
employeeComboBox.addValueChangeListener(event -> {
Employee selectedEmployee = event.getValue();
if (selectedEmployee != null) {
Notification.show("Empleado seleccionado: "
+ selectedEmployee.getFirstName() + " "
+ selectedEmployee.getLastName());
}
});
}
private int getWeekOfYear(final LocalDate date) {
return date.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
}
private void configurarVista() {
DatePicker fechaPicker = new DatePicker("Selecciona una fecha");
fechaPicker.addValueChangeListener(event -> {
LocalDate selectedDate = event.getValue();
if (selectedDate != null) {
selectedStartOfWeek = getStartOfWeek(selectedDate);
LocalDate endOfWeek = selectedStartOfWeek.plusDays(6);
fechasLabel.setText("Semana del " + selectedStartOfWeek + " al " + endOfWeek);
weekNumber = getWeekOfYear(selectedDate);
}
});
Button verMesButton = new Button("Ver Mes", event -> {
getUI().ifPresent(ui -> ui.navigate(HoursWorkedMonthView.class));
});
ComboBox<String> equipoDropdown = new ComboBox<>("Equipo"); ComboBox<String> equipoDropdown = new ComboBox<>("Equipo");
equipoDropdown.setItems("Equipo 1", "Equipo 2", "Equipo 3"); // Ejemplo de datos equipoDropdown.setItems("Equipo 1", "Equipo 2", "Equipo 3");
equipoDropdown.setWidth("250px");
TextField empleadoSearch = new TextField("Empleado (Search)"); setEmployeeComboBoxProperties();
HorizontalLayout filtersLayout = new HorizontalLayout(equipoDropdown, empleadoSearch); HorizontalLayout filtersLayout = new HorizontalLayout(equipoDropdown, employeeComboBox);
filtersLayout.setSpacing(true);
Grid<Actividad> grid = new Grid<>(Actividad.class, false); configurarGrid();
HorizontalLayout actividadFormLayout = configurarFormularioActividades();
Button actualizarButton = new Button("Actualizar Totales", event -> actualizarTotales());
Button guardarButton = new Button("Guardar", event -> guardarActividades());
Button cerrarButton = new Button("Cerrar", event -> this.closeView());
HorizontalLayout buttonsLayout = new HorizontalLayout(actualizarButton, guardarButton,
cerrarButton, verMesButton);
VerticalLayout totalesLayout = new VerticalLayout(totalCompletadoLabel, horasPendientesLabel);
totalesLayout.setSpacing(true);
totalesLayout.setPadding(true);
add(fechaPicker, fechasLabel, filtersLayout, grid, actividadFormLayout, buttonsLayout, totalesLayout);
}
private void configurarGrid() {
grid.removeAllColumns();
grid.setItems(actividades);
grid.addColumn(Actividad::getNombre).setHeader("Actividad"); grid.addColumn(Actividad::getNombre).setHeader("Actividad");
grid.addColumn(Actividad::getLunes).setHeader("Lunes"); grid.addColumn(Actividad::getLunes).setHeader("Lunes");
grid.addColumn(Actividad::getMartes).setHeader("Martes"); grid.addColumn(Actividad::getMartes).setHeader("Martes");
@ -43,155 +144,127 @@ public class HoursWorkedView extends VerticalLayout {
grid.addColumn(Actividad::getViernes).setHeader("Viernes"); grid.addColumn(Actividad::getViernes).setHeader("Viernes");
grid.addColumn(Actividad::getSabado).setHeader("Sábado"); grid.addColumn(Actividad::getSabado).setHeader("Sábado");
grid.addColumn(Actividad::getDomingo).setHeader("Domingo"); grid.addColumn(Actividad::getDomingo).setHeader("Domingo");
grid.addColumn(this::calcularTotalPorDia).setHeader("Total Día").setKey("totalDia");
grid.setItems(
new Actividad.Builder()
.nombre("Actividad 1")
.lunes(3)
.martes(3)
.miercoles(3)
.jueves(3)
.viernes(3)
.sabado(1)
.domingo(2)
.build(),
new Actividad.Builder()
.nombre("Actividad 2")
.lunes(2)
.martes(2)
.miercoles(2)
.jueves(2)
.viernes(2)
.sabado(0)
.domingo(1)
.build(),
new Actividad.Builder()
.nombre("Meeting 1")
.lunes(0)
.martes(0.5)
.miercoles(0.5)
.jueves(0)
.viernes(0)
.sabado(0.5)
.domingo(0)
.build()
);
Button actualizarButton = new Button("Actualizar");
Button guardarButton = new Button("Guardar");
Button cerrarButton = new Button("Cerrar");
HorizontalLayout buttonsLayout = new HorizontalLayout(actualizarButton, guardarButton, cerrarButton);
add(title, datePicker, filtersLayout, grid, buttonsLayout);
} }
public static final class Actividad { private HorizontalLayout configurarFormularioActividades() {
private final String nombre; TextField actividadNombre = new TextField("Actividad");
private final double lunes; actividadNombre.setWidth("200px");
private final double martes;
private final double miercoles;
private final double jueves;
private final double viernes;
private final double sabado;
private final double domingo;
private Actividad(final Builder builder) { TextField lunesHoras = crearCampoHora("Lunes");
this.nombre = builder.nombre; TextField martesHoras = crearCampoHora("Martes");
this.lunes = builder.lunes; TextField miercolesHoras = crearCampoHora("Miércoles");
this.martes = builder.martes; TextField juevesHoras = crearCampoHora("Jueves");
this.miercoles = builder.miercoles; TextField viernesHoras = crearCampoHora("Viernes");
this.jueves = builder.jueves; TextField sabadoHoras = crearCampoHora("Sábado");
this.viernes = builder.viernes; TextField domingoHoras = crearCampoHora("Domingo");
this.sabado = builder.sabado;
this.domingo = builder.domingo; Button agregarActividadButton = new Button("Agregar Actividad", e -> {
try {
Actividad nuevaActividad = new Actividad.Builder()
.nombre(actividadNombre.getValue())
.lunes(parseHoras(lunesHoras.getValue()))
.martes(parseHoras(martesHoras.getValue()))
.miercoles(parseHoras(miercolesHoras.getValue()))
.jueves(parseHoras(juevesHoras.getValue()))
.viernes(parseHoras(viernesHoras.getValue()))
.sabado(parseHoras(sabadoHoras.getValue()))
.domingo(parseHoras(domingoHoras.getValue()))
.build();
actividades.add(nuevaActividad);
grid.setItems(actividades);
actualizarTotales();
Notification.show("Actividad agregada correctamente");
// Limpiar los campos de entrada
actividadNombre.clear();
lunesHoras.clear();
martesHoras.clear();
miercolesHoras.clear();
juevesHoras.clear();
viernesHoras.clear();
sabadoHoras.clear();
domingoHoras.clear();
} catch (NumberFormatException ex) {
Notification.show("Error: Por favor ingresa números válidos para las horas.");
}
});
return new HorizontalLayout(
actividadNombre, lunesHoras, martesHoras, miercolesHoras,
juevesHoras, viernesHoras, sabadoHoras, domingoHoras, agregarActividadButton);
} }
public static class Builder { private TextField crearCampoHora(final String placeholder) {
private String nombre; TextField field = new TextField(placeholder);
private double lunes; field.setWidth("80px");
private double martes; field.setPlaceholder("0.0");
private double miercoles; return field;
private double jueves;
private double viernes;
private double sabado;
private double domingo;
public Builder nombre(final String nombre) {
this.nombre = nombre;
return this;
} }
public Builder lunes(final double lunes) { private double parseHoras(final String value) {
this.lunes = lunes; if (value == null || value.trim().isEmpty()) {
return this; return 0.0;
}
return Double.parseDouble(value);
} }
public Builder martes(final double martes) { private LocalDate getStartOfWeek(final LocalDate date) {
this.martes = martes; WeekFields weekFields = WeekFields.of(Locale.getDefault());
return this; return date.with(weekFields.dayOfWeek(), DayOfWeek.MONDAY.getValue());
} }
public Builder miercoles(final double miercoles) { private double calcularTotalPorDia(final Actividad actividad) {
this.miercoles = miercoles; return actividad.getLunes() + actividad.getMartes() + actividad.getMiercoles()
return this; + actividad.getJueves() + actividad.getViernes() + actividad.getSabado() + actividad.getDomingo();
} }
public Builder jueves(final double jueves) { private void actualizarTotales() {
this.jueves = jueves; double totalSemanaCompletada = actividades.stream()
return this; .mapToDouble(this::calcularTotalPorDia)
.sum();
double horasPendientes = 40 - totalSemanaCompletada;
totalCompletadoLabel.setText("Total Hrs/Semana Completadas: " + totalSemanaCompletada);
horasPendientesLabel.setText("Horas Pendientes: " + horasPendientes);
} }
public Builder viernes(final double viernes) { private void guardarActividades() {
this.viernes = viernes; Employee selectedEmployee = employeeComboBox.getValue();
return this;
if (selectedEmployee == null) {
Notification.show("Por favor, selecciona un empleado antes de guardar.");
return;
} }
public Builder sabado(final double sabado) { double totalHorasSemana = actividades.stream()
this.sabado = sabado; .mapToDouble(this::calcularTotalPorDia)
return this; .sum();
}
public Builder domingo(final double domingo) { HoursWorked hoursWorked = new HoursWorked();
this.domingo = domingo; hoursWorked.setEmployee(selectedEmployee);
return this; hoursWorked.setWeekNumber(weekNumber);
} hoursWorked.setTotalHours(totalHorasSemana);
public Actividad build() { try {
return new Actividad(this); hoursWorkedService.saveHoursWorked(hoursWorked); // Usa saveHoursWorked directamente
Notification.show("Actividades guardadas correctamente.");
} catch (Exception e) {
Notification.show("Error al guardar actividades: " + e.getMessage());
} }
} }
public String getNombre() { private double calcularTotalHoras(final List<HoursWorked> listaDeHorasTrabajadas) {
return nombre; return listaDeHorasTrabajadas.stream()
.mapToDouble(HoursWorked::getTotalHours)
.sum();
} }
public double getLunes() { private List<HoursWorked> obtenerDatos() {
return lunes; return new ArrayList<>();
} }
public double getMartes() { private void closeView() {
return martes; getUI().ifPresent(ui -> ui.navigate(HoursWorkedView.class));
}
public double getMiercoles() {
return miercoles;
}
public double getJueves() {
return jueves;
}
public double getViernes() {
return viernes;
}
public double getSabado() {
return sabado;
}
public double getDomingo() {
return domingo;
}
} }
} }

View File

@ -0,0 +1,53 @@
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;
@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,5 +1,6 @@
package com.primefactorsolutions.views; package com.primefactorsolutions.views;
import com.vaadin.flow.component.html.Anchor;
import com.vaadin.flow.component.html.H1; import com.vaadin.flow.component.html.H1;
import com.vaadin.flow.component.login.LoginForm; import com.vaadin.flow.component.login.LoginForm;
import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.component.orderedlayout.VerticalLayout;
@ -23,9 +24,11 @@ public class LoginView extends VerticalLayout implements BeforeEnterObserver {
setJustifyContentMode(JustifyContentMode.CENTER); setJustifyContentMode(JustifyContentMode.CENTER);
login.setAction("login"); login.setAction("login");
login.setForgotPasswordButtonVisible(false);
add(new H1("PFS Intra")); add(new H1("PFS Intra"));
add(login); add(login);
add(new Anchor("/password-recovery", "Reset password?"));
} }
@Override @Override

View File

@ -1,6 +1,7 @@
package com.primefactorsolutions.views; package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Employee; import com.primefactorsolutions.model.Employee;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.Text; import com.vaadin.flow.component.Text;
import com.vaadin.flow.component.applayout.AppLayout; import com.vaadin.flow.component.applayout.AppLayout;
import com.vaadin.flow.component.applayout.DrawerToggle; import com.vaadin.flow.component.applayout.DrawerToggle;
@ -16,7 +17,6 @@ import com.vaadin.flow.component.menubar.MenuBarVariant;
import com.vaadin.flow.component.orderedlayout.FlexComponent; import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.Scroller; import com.vaadin.flow.component.orderedlayout.Scroller;
import com.vaadin.flow.component.shared.Tooltip;
import com.vaadin.flow.component.sidenav.SideNav; import com.vaadin.flow.component.sidenav.SideNav;
import com.vaadin.flow.component.sidenav.SideNavItem; import com.vaadin.flow.component.sidenav.SideNavItem;
import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.PageTitle;
@ -51,18 +51,6 @@ public class MainLayout extends AppLayout {
final HorizontalLayout header = authContext.getAuthenticatedUser(UserDetails.class) final HorizontalLayout header = authContext.getAuthenticatedUser(UserDetails.class)
.map(user -> { .map(user -> {
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_ICON);
final MenuItem actions = createIconItem(menuBar, VaadinIcon.ELLIPSIS_V, null, "",
false);
final SubMenu actionsSubMenu = actions.getSubMenu();
final MenuItem signOutMenuItem = createIconItem(actionsSubMenu, VaadinIcon.EXIT, "Sign-out",
null, true);
signOutMenuItem.addClickListener(c -> this.authContext.logout());
String employeeId = "N/A"; String employeeId = "N/A";
if (user instanceof Employee) { if (user instanceof Employee) {
@ -73,11 +61,19 @@ public class MainLayout extends AppLayout {
} }
} }
final Tooltip tooltip = Tooltip.forComponent(loggedUser) final Avatar loggedUser = new Avatar(user.getUsername());
.withText("Employee id: " + employeeId) loggedUser.getStyle().set("display", "block");
.withPosition(Tooltip.TooltipPosition.TOP_START); loggedUser.getElement().setAttribute("tabindex", "-1");
final HorizontalLayout hl = new HorizontalLayout(loggedUser, menuBar); 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); hl.setJustifyContentMode(FlexComponent.JustifyContentMode.END);
return hl; return hl;
@ -88,17 +84,9 @@ public class MainLayout extends AppLayout {
addToNavbar(true, toggle, viewTitle, header); addToNavbar(true, toggle, viewTitle, header);
} }
private MenuItem createIconItem(final HasMenuItems menu, final VaadinIcon iconName, private MenuItem createIconItem(final HasMenuItems menu, final Component component,
final String label, final String ariaLabel, final boolean isChild) { final String label, final String ariaLabel) {
final Icon icon = new Icon(iconName); final MenuItem item = menu.addItem(component, e -> {
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)");
}
final MenuItem item = menu.addItem(icon, e -> {
}); });
if (ariaLabel != null) { if (ariaLabel != null) {
@ -112,6 +100,18 @@ public class MainLayout extends AppLayout {
return item; 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() { private void addDrawerContent() {
final Span appName = new Span("pfs-intra"); final Span appName = new Span("pfs-intra");
appName.addClassNames(LumoUtility.FontWeight.SEMIBOLD, LumoUtility.FontSize.LARGE); appName.addClassNames(LumoUtility.FontWeight.SEMIBOLD, LumoUtility.FontSize.LARGE);
@ -124,8 +124,8 @@ public class MainLayout extends AppLayout {
final SideNav nav = new SideNav(); final SideNav nav = new SideNav();
authContext.getAuthenticatedUser(UserDetails.class).ifPresent(u -> { authContext.getAuthenticatedUser(UserDetails.class).ifPresent(u -> {
SideNavItem recruiting = new SideNavItem("Recruiting", MainView.class, SideNavItem recruiting = new SideNavItem("Recruiting");
LineAwesomeIcon.BUSINESS_TIME_SOLID.create()); recruiting.setPrefixComponent(LineAwesomeIcon.BUSINESS_TIME_SOLID.create());
recruiting.addItem(new SideNavItem("Assessments", AssessmentsListView.class, recruiting.addItem(new SideNavItem("Assessments", AssessmentsListView.class,
LineAwesomeIcon.RIBBON_SOLID.create())); LineAwesomeIcon.RIBBON_SOLID.create()));
recruiting.addItem(new SideNavItem("Candidates", CandidatesListView.class, recruiting.addItem(new SideNavItem("Candidates", CandidatesListView.class,
@ -133,8 +133,8 @@ public class MainLayout extends AppLayout {
recruiting.addItem(new SideNavItem("Questions", QuestionsListView.class, recruiting.addItem(new SideNavItem("Questions", QuestionsListView.class,
LineAwesomeIcon.QUESTION_SOLID.create())); LineAwesomeIcon.QUESTION_SOLID.create()));
SideNavItem admin = new SideNavItem("Admin", MainView.class, SideNavItem admin = new SideNavItem("Admin");
LineAwesomeIcon.BUILDING.create()); admin.setPrefixComponent(LineAwesomeIcon.BUILDING.create());
admin.addItem(new SideNavItem("Employees", EmployeesListView.class, admin.addItem(new SideNavItem("Employees", EmployeesListView.class,
LineAwesomeIcon.USER_EDIT_SOLID.create())); LineAwesomeIcon.USER_EDIT_SOLID.create()));
admin.addItem(new SideNavItem("Documents", DocumentsListView.class, admin.addItem(new SideNavItem("Documents", DocumentsListView.class,
@ -144,6 +144,8 @@ public class MainLayout extends AppLayout {
LineAwesomeIcon.PLANE_DEPARTURE_SOLID.create()); LineAwesomeIcon.PLANE_DEPARTURE_SOLID.create());
timeOff.addItem(new SideNavItem("Vacations", RequestsListView.class, timeOff.addItem(new SideNavItem("Vacations", RequestsListView.class,
LineAwesomeIcon.SUN.create())); LineAwesomeIcon.SUN.create()));
timeOff.addItem(new SideNavItem("Add Vacation", RequestRegisterView.class,
LineAwesomeIcon.SUN.create()));
SideNavItem timesheet = new SideNavItem("My Timesheet", TimesheetView.class, SideNavItem timesheet = new SideNavItem("My Timesheet", TimesheetView.class,
LineAwesomeIcon.HOURGLASS_START_SOLID.create()); LineAwesomeIcon.HOURGLASS_START_SOLID.create());
timesheet.addItem(new SideNavItem("Hours Worked", HoursWorkedView.class, timesheet.addItem(new SideNavItem("Hours Worked", HoursWorkedView.class,

View File

@ -11,6 +11,6 @@ import jakarta.annotation.security.PermitAll;
@PermitAll @PermitAll
public class MainView extends Main { public class MainView extends Main {
public MainView() { public MainView() {
add(new Text("welcome")); add(new Text("Welcome to PFS Intra."));
} }
} }

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,87 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.HoursWorked;
import com.primefactorsolutions.service.HoursWorkedService;
import com.primefactorsolutions.service.ReportService;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Anchor;
import com.vaadin.flow.component.html.H2;
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.vaadin.flow.component.notification.Notification;
import java.io.ByteArrayInputStream;
import java.util.HashMap;
import java.util.List;
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 HoursWorkedService hoursWorkedService;
private final ReportService reportService;
@Autowired
public ReporteView(final HoursWorkedService hoursWorkedService, final ReportService reportService) {
this.hoursWorkedService = hoursWorkedService;
this.reportService = reportService;
H2 title = new H2("Reporte de Horas Trabajadas");
add(title);
Button reportButton = new Button("Generar Reporte de Horas Trabajadas", event -> generateHoursWorkedReport());
add(reportButton);
}
private void generateHoursWorkedReport() {
List<HoursWorked> hoursWorkedList = hoursWorkedService.findAll(); // Obtener la lista de HoursWorked
if (hoursWorkedList.isEmpty()) {
Notification.show("No hay horas trabajadas disponibles para generar el reporte.",
3000, Notification.Position.MIDDLE);
return;
}
try {
List<String> headers = List.of("ID", "Employee ID", "Week Number", "Total Hours");
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("Week Number", hoursWorked.getWeekNumber());
map.put("Total Hours", hoursWorked.getTotalHours());
return map;
})
.collect(Collectors.toList());
byte[] excelBytes = reportService.writeAsExcel("hours_worked_report", headers, data);
StreamResource excelResource = new StreamResource("hours_worked_report.xlsx",
() -> new ByteArrayInputStream(excelBytes));
excelResource.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
excelResource.setCacheTime(0);
Anchor downloadLink = new Anchor(excelResource, "Descargar Reporte de Horas Trabajadas");
downloadLink.getElement().setAttribute("download", true);
add(downloadLink);
Notification.show("Reporte de horas trabajadas generado exitosamente.",
3000, Notification.Position.MIDDLE);
} catch (Exception e) {
Notification.show("Error al generar el reporte de horas trabajadas. Inténtalo de nuevo.",
3000, Notification.Position.MIDDLE);
e.printStackTrace();
}
}
}

View File

@ -20,6 +20,7 @@ import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll; import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import java.time.Year;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -44,7 +45,6 @@ public class RequestEmployeeView extends Div implements HasUrlParameter<String>
public RequestEmployeeView(final TimeOffRequestService requestService, final EmployeeService employeeService) { public RequestEmployeeView(final TimeOffRequestService requestService, final EmployeeService employeeService) {
this.requestService = requestService; this.requestService = requestService;
this.employeeService = employeeService; this.employeeService = employeeService;
initializeView();
} }
private void initializeView() { private void initializeView() {
@ -97,9 +97,17 @@ public class RequestEmployeeView extends Div implements HasUrlParameter<String>
} }
private VerticalLayout createSummaryLayout() { private VerticalLayout createSummaryLayout() {
int totalVacations = 15; int currentYear = Year.now().getValue();
int totalTimeOff = 2; String yearCategory = "YEAR_" + currentYear;
int totalAvailableDays = totalVacations + totalTimeOff; double totalVacations = requests.stream()
.filter(req -> req.getCategory().name().equals(yearCategory))
.mapToDouble(TimeOffRequest::getAvailableDays)
.sum();
double totalTimeOff = requests.stream()
.filter(req -> !req.getCategory().name().startsWith("YEAR"))
.mapToDouble(TimeOffRequest::getDaysBalance)
.sum();
double totalAvailableDays = totalVacations + totalTimeOff;
return new VerticalLayout( return new VerticalLayout(
new Span("TOTAL HOLIDAYS: " + totalVacations), new Span("TOTAL HOLIDAYS: " + totalVacations),
new Span("TOTAL TIME OFF: " + totalTimeOff), new Span("TOTAL TIME OFF: " + totalTimeOff),
@ -176,6 +184,7 @@ public class RequestEmployeeView extends Div implements HasUrlParameter<String>
requests = requestService.findRequestsByEmployeeId(employeeId); requests = requestService.findRequestsByEmployeeId(employeeId);
setViewTitle(employee.getFirstName() + " " + employee.getLastName(), employee.getTeam().getName()); setViewTitle(employee.getFirstName() + " " + employee.getLastName(), employee.getTeam().getName());
requestGrid.setItems(requests); requestGrid.setItems(requests);
initializeView();
} }
private void setViewTitle(final String employeeName, final String employeeTeam) { private void setViewTitle(final String employeeName, final String employeeTeam) {

View File

@ -0,0 +1,256 @@
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.util.Arrays;
import java.util.List;
@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<>("Employee");
private final ComboBox<TimeOffRequestType> categoryComboBox = new ComboBox<>("Category");
private final NumberField availableDaysField = new NumberField("Available Days");
private final DatePicker startDatePicker = new DatePicker("Start Date");
private final DatePicker endDatePicker = new DatePicker("End Date");
private final NumberField daysToBeTakenField = new NumberField("Days To Be Taken");
private final NumberField balanceDaysField = new NumberField("Balance Days");
private final TimeOffRequestService requestService;
private final EmployeeService employeeService;
private final VacationService vacationService;
private final Binder<TimeOffRequest> binder;
private Vacation vacation;
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);
configureFormFields();
configureButtons();
configureBinder();
setupFormLayout();
}
private void configureFormFields() {
employeeComboBox.setItems(employeeService.findAllEmployees());
employeeComboBox.setItemLabelGenerator(emp -> emp.getFirstName() + " " + emp.getLastName());
employeeComboBox.addValueChangeListener(event -> handleEmployeeSelection(event.getValue()));
categoryComboBox.setEnabled(false);
categoryComboBox.addValueChangeListener(event -> handleCategorySelection(event.getValue()));
startDatePicker.addValueChangeListener(event -> updateDatePickerMinValues());
endDatePicker.addValueChangeListener(event -> calculateDays());
availableDaysField.setReadOnly(true);
daysToBeTakenField.setReadOnly(true);
balanceDaysField.setReadOnly(true);
}
private void configureBinder() {
binder.forField(employeeComboBox)
.asRequired("Employee is required")
.bind(TimeOffRequest::getEmployee, TimeOffRequest::setEmployee);
binder.forField(categoryComboBox)
.asRequired("Category is required")
.bind(TimeOffRequest::getCategory, TimeOffRequest::setCategory);
binder.forField(availableDaysField)
.bind(TimeOffRequest::getAvailableDays, TimeOffRequest::setAvailableDays);
binder.forField(startDatePicker)
.asRequired("Start date is required")
.bind(TimeOffRequest::getStartDate, TimeOffRequest::setStartDate);
binder.forField(endDatePicker)
.asRequired("End date is required")
.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) {
clearForm();
if (selectedEmployee != null) {
categoryComboBox.setEnabled(true);
filterCategories(selectedEmployee);
}
}
private void filterCategories(final Employee employee) {
List<TimeOffRequest> employeeRequests = requestService.findRequestsByEmployeeId(employee.getId());
List<TimeOffRequestType> requestedCategories = employeeRequests.stream()
.map(TimeOffRequest::getCategory)
.toList();
List<TimeOffRequestType> availableCategories = Arrays.stream(TimeOffRequestType.values())
.filter(category -> !requestedCategories.contains(category))
.filter(category -> {
if (employee.getGender() == Employee.Gender.MALE) {
return category != TimeOffRequestType.MATERNITY
&& category != TimeOffRequestType.MOTHERS_DAY
&& category != TimeOffRequestType.INTERNATIONAL_WOMENS_DAY
&& category != TimeOffRequestType.NATIONAL_WOMENS_DAY;
} else {
return category != TimeOffRequestType.FATHERS_DAY
&& category != TimeOffRequestType.PATERNITY;
}
})
.toList();
categoryComboBox.setItems(availableCategories);
}
private void handleCategorySelection(final TimeOffRequestType selectedCategory) {
clearForm();
if (selectedCategory != null) {
updateAvailableDays(selectedCategory);
}
}
private void updateAvailableDays(final TimeOffRequestType selectedCategory) {
vacation = vacationService.findVacationByCategory(selectedCategory);
if (vacation != null) {
availableDaysField.setValue(vacation.getDuration());
setDatePickerLimits(vacation);
}
}
private void setDatePickerLimits(final Vacation vacation) {
LocalDate startDate;
if (vacation.getVacationDate() != null) {
startDate = vacation.getVacationDate();
endDate = startDate.plusDays(vacation.getExpiration().intValue() - 1);
} else {
startDate = LocalDate.now();
endDate = null;
}
startDatePicker.setMin(startDate);
startDatePicker.setMax(endDate);
endDatePicker.setMin(startDate);
endDatePicker.setMax(endDate);
}
private void updateDatePickerMinValues() {
LocalDate startDate = startDatePicker.getValue();
if (vacation.getVacationDate() == null) {
endDate = startDate.plusDays(vacation.getExpiration().intValue() - 1);
}
if (startDate != null) {
endDatePicker.setMin(startDate);
endDatePicker.setMax(startDate.plusDays(vacation.getExpiration().intValue() - 1));
if (vacation.getDuration() == 0.5) {
endDatePicker.setValue(startDate.plusDays(0));
} else {
endDatePicker.setValue(startDate.plusDays(vacation.getDuration().intValue() - 1));
}
calculateDays();
}
}
private void calculateDays() {
LocalDate startDate = startDatePicker.getValue();
LocalDate endDate = endDatePicker.getValue();
Double availableDays = availableDaysField.getValue();
if (startDate != null && endDate != null) {
if (startDate.isAfter(endDate)) {
endDatePicker.clear();
return;
}
double daysToBeTaken = java.time.temporal.ChronoUnit.DAYS.between(startDate, endDate) + 1;
if (daysToBeTaken == 1 && vacation.getDuration() == 0.5) {
daysToBeTaken = 0.5;
}
daysToBeTakenField.setValue(daysToBeTaken);
double balanceDays = availableDays - daysToBeTaken;
if (balanceDays < 0) {
endDatePicker.clear();
daysToBeTakenField.clear();
balanceDaysField.clear();
} else {
balanceDaysField.setValue(balanceDays);
}
}
}
private void configureButtons() {
saveButton = new Button("Save", event -> saveRequest());
closeButton = new Button("Close", event -> closeForm());
}
private void setupFormLayout() {
add(
new H3("Add Vacation Request"),
employeeComboBox,
categoryComboBox,
availableDaysField,
startDatePicker,
endDatePicker,
daysToBeTakenField,
balanceDaysField,
new HorizontalLayout(saveButton, closeButton)
);
}
private void saveRequest() {
if (binder.validate().isOk()) {
TimeOffRequest request = binder.getBean();
request.setExpiration(endDate);
request.setState(TimeOffRequestStatus.REQUESTED);
requestService.saveTimeOffRequest(request);
Notification.show("Request saved successfully.");
closeForm();
} else {
Notification.show("Please fill all required fields correctly.");
}
}
private void closeForm() {
getUI().ifPresent(ui -> ui.navigate(RequestsListView.class));
}
private void clearForm() {
availableDaysField.clear();
startDatePicker.clear();
endDatePicker.clear();
daysToBeTakenField.clear();
balanceDaysField.clear();
}
}

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,2 +1,3 @@
spring.ldap.url=ldap://localhost:8391 spring.ldap.url=ldap://localhost:8391
spring.ldap.embedded.port=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.enabled=true
spring.h2.console.settings.web-allow-others=true spring.h2.console.settings.web-allow-others=true
application.jwtSecret=${JWT_SECRET}

View File

@ -16,53 +16,82 @@ INSERT INTO team (id, version, name) VALUES ('c3a8a7b1-f2d9-48c0-86ea-f215c2e6b3
INSERT INTO team (id, version, name) VALUES ('8f6b61e7-efb2-4de7-b8ed-7438c9d8babe', 1, 'GHI'); INSERT INTO team (id, version, name) VALUES ('8f6b61e7-efb2-4de7-b8ed-7438c9d8babe', 1, 'GHI');
insert into employee (id, version, username, first_name, last_name, status, team_id) values ('5c6f11fe-c341-4be7-a9a6-bba0081ad7c6', 1, 'bob', 'Bob', 'Test', 'ACTIVE','b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa'); INSERT INTO vacation (id, version, category, vacation_date, duration, expiration, type) VALUES ('123e4567-e89b-12d3-a456-426614174000', 1, 'NEW_YEAR', '2024-01-01', 1, 1, 'FIXED');
insert into employee (id, version, username, first_name, last_name, status, team_id) values ('cba3efb7-32bc-44be-9fdc-fc5e4f211254', 1, 'ben', 'Ben', 'Test', 'ACTIVE', '6d63bc15-3f8b-46f7-9cf1-7e9b0b9a2b28'); INSERT INTO vacation (id, version, category, vacation_date, duration, expiration, type) VALUES ('223e4567-e89b-12d3-a456-426614174001', 1, 'MONDAY_CARNIVAL', '2024-02-12', 1, 1, 'FIXED');
insert into employee (id, version, username, first_name, last_name, status, team_id) values ('e99b7af5-7d3a-4c0f-b8bc-e8d0388d8fc4', 1, 'jperez', 'Juan', 'Perez Condori', 'INACTIVE', 'c3a8a7b1-f2d9-48c0-86ea-f215c2e6b3a3'); INSERT INTO vacation (id, version, category, vacation_date, duration, expiration, type) VALUES ('323e4567-e89b-12d3-a456-426614174002', 1, 'TUESDAY_CARNIVAL', '2024-02-13', 1, 1, 'FIXED');
insert into employee (id, version, username, first_name, last_name, status, team_id) values ('f6ab3c6d-7078-45f6-9b22-4e37637bfec6', 1, 'agarcia', 'Ana', 'Garcia Rojas', 'ACTIVE', '8f6b61e7-efb2-4de7-b8ed-7438c9d8babe'); INSERT INTO vacation (id, version, category, vacation_date, duration, expiration, type) VALUES ('423e4567-e89b-12d3-a456-426614174003', 1, 'GOOD_FRIDAY', '2024-03-29', 1, 1, 'FIXED');
insert into employee (id, version, username, first_name, last_name, status, team_id) values ('2e2293b1-3f9a-4f3d-abc8-32639b0a5e15', 1, 'clopez', 'Carlos', 'Lopez Mendoza', 'INACTIVE', 'b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa'); INSERT INTO vacation (id, version, category, vacation_date, duration, expiration, type) VALUES ('523e4567-e89b-12d3-a456-426614174004', 1, 'LABOR_DAY', '2024-05-01', 1, 1, 'FIXED');
insert into employee (id, version, username, first_name, last_name, status, team_id) values ('4b1c6c35-4627-4b35-b6e9-dc75c68b2c31', 1, 'mfernandez', 'Maria', 'Fernandez Villca', 'ACTIVE', '6d63bc15-3f8b-46f7-9cf1-7e9b0b9a2b28'); INSERT INTO vacation (id, version, category, vacation_date, duration, expiration, type) VALUES ('623e4567-e89b-12d3-a456-426614174005', 1, 'INDEPENDENCE_DAY', '2024-08-06', 1, 1, 'FIXED');
insert into employee (id, version, username, first_name, last_name, status, team_id) values ('afc5c741-f70a-4394-853b-39d51b118927', 1, 'lgutierrez', 'Luis', 'Gutierrez Mamani', 'ACTIVE', 'c3a8a7b1-f2d9-48c0-86ea-f215c2e6b3a3'); INSERT INTO vacation (id, version, category, vacation_date, duration, expiration, type) VALUES ('723e4567-e89b-12d3-a456-426614174006', 1, 'CHRISTMAS', '2024-12-25', 1, 1, 'FIXED');
insert into employee (id, version, username, first_name, last_name, status, team_id) values ('b2436b82-7b9f-4f0d-9463-f2c3173a45c3', 1, 'lmartinez', 'Laura', 'Martinez Paredes', 'INACTIVE', '8f6b61e7-efb2-4de7-b8ed-7438c9d8babe');
insert into employee (id, version, username, first_name, last_name, status, team_id) values ('6e6a8a4e-9f6b-44eb-8c69-40acfdc86756', 1, 'rsantos', 'Roberto', 'Santos Escobar', 'ACTIVE','b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa');
insert into employee (id, version, username, first_name, last_name, status, team_id) values ('36b0d1c6-bdc0-4d98-94bb-08b9bce3f0d5', 1, 'vmorales', 'Valeria', 'Morales Ochoa', 'INACTIVE', '6d63bc15-3f8b-46f7-9cf1-7e9b0b9a2b28'); INSERT INTO vacation (id, version, category, vacation_date, duration, expiration, type) VALUES ('823e4567-e89b-12d3-a456-426614174007', 1, 'PRURINATIONAL_STATE_DAY', '2024-01-21', 3, 30, 'MOVABLE');
insert into employee (id, version, username, first_name, last_name, status, team_id) values ('5a1c6d80-58b3-43e3-a5a5-24b4a2d1d54a', 1, 'jramirez', 'Jorge', 'Ramirez Tapia', 'ACTIVE', 'c3a8a7b1-f2d9-48c0-86ea-f215c2e6b3a3'); INSERT INTO vacation (id, version, category, vacation_date, duration, expiration, type) VALUES ('923e4567-e89b-12d3-a456-426614174008', 1, 'CORPUS_CHRISTI', '2024-05-30', 1, 30, 'MOVABLE');
insert into employee (id, version, username, first_name, last_name, status, team_id) values ('9d6a5b2e-6d0b-4b89-8d6a-d3f3d1bfc047', 1, 'storres', 'Sandra', 'Torres Huanca', 'ACTIVE', '8f6b61e7-efb2-4de7-b8ed-7438c9d8babe'); INSERT INTO vacation (id, version, category, vacation_date, duration, expiration, type) VALUES ('a23e4567-e89b-12d3-a456-426614174009', 1, 'ANDEAN_NEW_YEAR', '2024-06-21', 1, 30, 'MOVABLE');
insert into employee (id, version, username, first_name, last_name, status, team_id) values ('f8b3e0c0-0d5a-4e5c-bf9d-207b9b5e8279', 1, 'fquispe', 'Felipe', 'Quispe Huanca', 'INACTIVE','b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa'); INSERT INTO vacation (id, version, category, vacation_date, duration, expiration, type) VALUES ('b23e4567-e89b-12d3-a456-42661417400a', 1, 'DEPARTMENTAL_ANNIVERSARY', '2024-09-14', 1, 30, 'MOVABLE');
insert into employee (id, version, username, first_name, last_name, status, team_id) values ('cd80e1d0-9a08-44a6-bd63-2c63eaa003d4', 1, 'grivas', 'Gabriela', 'Rivas Arana', 'ACTIVE', '6d63bc15-3f8b-46f7-9cf1-7e9b0b9a2b28'); INSERT INTO vacation (id, version, category, vacation_date, duration, expiration, type) VALUES ('c23e4567-e89b-12d3-a456-42661417400b', 1, 'ALL_SOULS_DAY', '2024-11-02', 1, 30, 'MOVABLE');
insert into employee (id, version, username, first_name, last_name, status, team_id) values ('62d3c1b7-815e-4e96-8d7e-f8c4236bca55', 1, 'oflores', 'Oscar', 'Flores Quiroga', 'INACTIVE', 'c3a8a7b1-f2d9-48c0-86ea-f215c2e6b3a3');
insert into employee (id, version, username, first_name, last_name, status, team_id) values ('f20b7c5a-5a67-44f0-9ec1-4c1b8e80de05', 1, 'mvargas', 'Marta', 'Vargas Soria', 'ACTIVE', '8f6b61e7-efb2-4de7-b8ed-7438c9d8babe');
insert into employee (id, version, username, first_name, last_name, status, team_id) values ('19b5a76e-d7b1-4b76-8b02-4d0748e85809', 1, 'aespinoza', 'Andres', 'Espinoza Chura', 'INACTIVE','b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa'); INSERT INTO vacation (id, version, category, duration, expiration, type) VALUES ('c23e4567-e89b-12d3-a456-42661417400c', 1, 'BIRTHDAY', 0.5, 365, 'OTHER');
insert into employee (id, version, username, first_name, last_name, status, team_id) values ('5c1a7b82-832d-4f24-8377-54b77b91b6a8', 1, 'cvillanueva', 'Carla', 'Villanueva Arce', 'ACTIVE', '6d63bc15-3f8b-46f7-9cf1-7e9b0b9a2b28'); INSERT INTO vacation (id, version, category, duration, expiration, type) VALUES ('c23e4567-e89b-12d3-a456-42661417400d', 1, 'MATERNITY', 90, 90, 'OTHER');
INSERT INTO vacation (id, version, category, duration, expiration, type) VALUES ('c23e4567-e89b-12d3-a456-42661417400e', 1, 'PATERNITY', 3, 3, 'OTHER');
INSERT INTO vacation (id, version, category, duration, expiration, type) VALUES ('c23e4567-e89b-12d3-a456-42661417400f', 1, 'MARRIAGE', 3, 3, 'OTHER');
INSERT INTO vacation (id, version, category, vacation_date, duration, expiration, type) VALUES ('c23e4567-e89b-12d3-a456-42661417401a', 1, 'FATHERS_DAY', '2024-03-19', 0.5, 30, 'OTHER');
INSERT INTO vacation (id, version, category, vacation_date, duration, expiration, type) VALUES ('c23e4567-e89b-12d3-a456-42661417401b', 1, 'MOTHERS_DAY', '2024-05-27', 0.5, 30, 'OTHER');
INSERT INTO vacation (id, version, category, vacation_date, duration, expiration, type) VALUES ('c23e4567-e89b-12d3-a456-42661417401c', 1, 'INTERNATIONAL_WOMENS_DAY', '2024-03-08', 0.5, 30, 'OTHER');
INSERT INTO vacation (id, version, category, vacation_date, duration, expiration, type) VALUES ('c23e4567-e89b-12d3-a456-42661417401d', 1, 'NATIONAL_WOMENS_DAY', '2024-10-11', 0.5, 30, 'OTHER');
INSERT INTO vacation (id, version, category, duration, type) VALUES ('c23e4567-e89b-12d3-a456-42661417401e', 1, 'HEALTH_PERMIT', 2, 'OTHER');
INSERT INTO vacation (id, version, category, expiration, type) VALUES ('490e5fbe-895b-42f8-b914-95437f7b39c0', 1, 'VACATION_CURRENT_MANAGEMENT', 730, 'OTHER');
INSERT INTO vacation (id, version, category, expiration, type) VALUES ('c23e4567-e89b-12d3-a456-4266141740ff', 1, 'VACATION_PREVIOUS_MANAGEMENT', 730, 'OTHER');
insert into employee (id, version, username, first_name, last_name, status, team_id, gender) values ('5c6f11fe-c341-4be7-a9a6-bba0081ad7c6', 1, 'bob', 'Bob', 'Test', 'ACTIVE','b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa', 'MALE');
insert into employee (id, version, username, first_name, last_name, status, team_id, gender) values ('cba3efb7-32bc-44be-9fdc-fc5e4f211254', 1, 'ben', 'Ben', 'Test', 'ACTIVE', '6d63bc15-3f8b-46f7-9cf1-7e9b0b9a2b28', 'MALE');
insert into employee (id, version, username, first_name, last_name, status, team_id, gender) values ('e99b7af5-7d3a-4c0f-b8bc-e8d0388d8fc4', 1, 'jperez', 'Juan', 'Perez Condori', 'INACTIVE', 'c3a8a7b1-f2d9-48c0-86ea-f215c2e6b3a3', 'MALE');
insert into employee (id, version, username, first_name, last_name, status, team_id, gender) values ('f6ab3c6d-7078-45f6-9b22-4e37637bfec6', 1, 'agarcia', 'Ana', 'Garcia Rojas', 'ACTIVE', '8f6b61e7-efb2-4de7-b8ed-7438c9d8babe', 'FEMALE');
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) 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', 'VACATION', 'TAKEN', 15, '2025-12-31', '2024-10-01', '2024-10-10', 5, 10); values ('9d6f12ba-e341-4e7a-b8a6-cab0982bd8c1', 1, '5c6f11fe-c341-4be7-a9a6-bba0081ad7c6', 'PATERNITY', 'TAKEN', 15, '2025-12-31', '2024-10-01', '2024-10-10', 5, 10);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance) 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 ('2fa314bc-f547-4b12-a8b6-bb789feabc12', 1, '19b5a76e-d7b1-4b76-8b02-4d0748e85809', 'BIRTHDAY', 'APPROVED', 15, '2025-12-31', '2024-12-01', '2024-12-15', 7, 8); values ('2fa314bc-f547-4b12-a8b6-bb789feabc12', 1, '19b5a76e-d7b1-4b76-8b02-4d0748e85809', 'BIRTHDAY', 'APPROVED', 15, '2025-12-31', '2024-12-01', '2024-12-15', 7, 8);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance) 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 ('d5f6341a-913d-4e7f-a0b2-cfe0786acd34', 1, 'cba3efb7-32bc-44be-9fdc-fc5e4f211254', 'FIXED_HOLIDAY', 'IN_USE', 20, '2025-11-30', '2024-11-10', '2024-11-20', 10, 10); values ('d5f6341a-913d-4e7f-a0b2-cfe0786acd34', 1, 'cba3efb7-32bc-44be-9fdc-fc5e4f211254', 'HEALTH_PERMIT', 'IN_USE', 20, '2025-11-30', '2024-11-10', '2024-11-20', 10, 10);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance) 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 ('4f913b23-ff23-4527-bcd6-adfe01234567', 1, 'e99b7af5-7d3a-4c0f-b8bc-e8d0388d8fc4', 'MATERNITY', 'IN_USE', 18, '2025-06-30', '2024-07-01', '2024-07-15', 10, 8); values ('4f913b23-ff23-4527-bcd6-adfe01234567', 1, 'e99b7af5-7d3a-4c0f-b8bc-e8d0388d8fc4', 'MATERNITY', 'IN_USE', 18, '2025-06-30', '2024-07-01', '2024-07-15', 10, 8);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance) 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', 'VACATION', 'REQUESTED', 10, '2025-10-31', '2024-09-15', '2024-09-20', 5, 5); values ('8c653f2a-f9a3-4d67-b3b6-12ad98fe0983', 1, 'f6ab3c6d-7078-45f6-9b22-4e37637bfec6', 'LABOR_DAY', 'REQUESTED', 10, '2025-10-31', '2024-09-15', '2024-09-20', 5, 5);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance) 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', 'FIXED_HOLIDAY', 'IN_USE', 12, '2025-08-31', '2024-08-05', '2024-08-15', 6, 6); values ('fb9d9d75-b2ab-4ea4-b8b3-0a8f89e5c123', 1, '2e2293b1-3f9a-4f3d-abc8-32639b0a5e15', 'INDEPENDENCE_DAY', 'IN_USE', 12, '2025-08-31', '2024-08-05', '2024-08-15', 6, 6);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance) 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 ('1c913a12-46e9-47b7-9e31-ab903fedc789', 1, '4b1c6c35-4627-4b35-b6e9-dc75c68b2c31', 'BIRTHDAY', 'APPROVED', 14, '2025-12-31', '2024-10-20', '2024-10-25', 5, 9); values ('1c913a12-46e9-47b7-9e31-ab903fedc789', 1, '4b1c6c35-4627-4b35-b6e9-dc75c68b2c31', 'BIRTHDAY', 'APPROVED', 14, '2025-12-31', '2024-10-20', '2024-10-25', 5, 9);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance) 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 ('b1249d3a-cc34-4954-88d9-1e4f67fe2436', 1, 'afc5c741-f70a-4394-853b-39d51b118927', 'MATERNITY', 'APPROVED', 20, '2025-11-30', '2024-11-05', '2024-11-12', 7, 13); values ('b1249d3a-cc34-4954-88d9-1e4f67fe2436', 1, 'afc5c741-f70a-4394-853b-39d51b118927', 'MATERNITY', 'APPROVED', 20, '2025-11-30', '2024-11-05', '2024-11-12', 7, 13);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance) 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', 'VACATION', 'PENDING', 18, '2025-06-30', '2024-07-10', '2024-07-20', 8, 10); values ('6fdc47a8-127b-41c4-8d12-7fc12098ab12', 1, 'b2436b82-7b9f-4f0d-9463-f2c3173a45c3', 'ALL_SOULS_DAY', 'PENDING', 18, '2025-06-30', '2024-07-10', '2024-07-20', 8, 10);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance) 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', 'FIXED_HOLIDAY', 'PENDING', 15, '2025-12-31', '2024-09-01', '2024-09-05', 4, 11); values ('12ec8b74-983d-4a17-b67e-134f45ae904c', 1, '5c1a7b82-832d-4f24-8377-54b77b91b6a8', 'NEW_YEAR', 'PENDING', 15, '2025-12-31', '2024-09-01', '2024-09-05', 4, 11);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance) 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', 'VACATION', 'APPROVED', 20, '2025-11-30', '2024-10-25', '2024-11-05', 9, 11); values ('89bc4b2a-943f-487c-a9f3-bacf78145e67', 1, 'cba3efb7-32bc-44be-9fdc-fc5e4f211254', 'MONDAY_CARNIVAL', 'APPROVED', 20, '2025-11-30', '2024-10-25', '2024-11-05', 9, 11);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance) 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 ('37adfc2a-7463-4b2d-a7c1-fae04567cdef', 1, 'e99b7af5-7d3a-4c0f-b8bc-e8d0388d8fc4', 'BIRTHDAY', 'REQUESTED', 18, '2025-06-30', '2024-06-01', '2024-06-10', 6, 12); values ('37adfc2a-7463-4b2d-a7c1-fae04567cdef', 1, 'e99b7af5-7d3a-4c0f-b8bc-e8d0388d8fc4', 'BIRTHDAY', 'REQUESTED', 18, '2025-06-30', '2024-06-01', '2024-06-10', 6, 12);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance) 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 ('2bc138ea-12db-4b89-a0b4-78e045e34b4e', 1, 'f6ab3c6d-7078-45f6-9b22-4e37637bfec6', 'MATERNITY', 'REQUESTED', 10, '2025-10-31', '2024-10-01', '2024-10-10', 3, 7); values ('2bc138ea-12db-4b89-a0b4-78e045e34b4e', 1, 'f6ab3c6d-7078-45f6-9b22-4e37637bfec6', 'MATERNITY', 'REQUESTED', 10, '2025-10-31', '2024-10-01', '2024-10-10', 3, 7);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance) 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 ('14de1a56-6893-4e12-90f3-4faec457f002', 1, 'cd80e1d0-9a08-44a6-bd63-2c63eaa003d4', 'FIXED_HOLIDAY', 'PENDING', 22, '2025-08-31', '2024-07-15', '2024-07-25', 8, 14); values ('14de1a56-6893-4e12-90f3-4faec457f002', 1, 'cd80e1d0-9a08-44a6-bd63-2c63eaa003d4', 'HEALTH_PERMIT', 'PENDING', 22, '2025-08-31', '2024-07-15', '2024-07-25', 8, 14);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance) 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 ('fb08a6c9-cd17-42e8-b9e2-734ec834cae2', 1, '4b1c6c35-4627-4b35-b6e9-dc75c68b2c31', 'VACATION', 'PENDING', 16, '2025-12-31', '2024-09-30', '2024-10-05', 4, 12); values ('fb08a6c9-cd17-42e8-b9e2-734ec834cae2', 1, '4b1c6c35-4627-4b35-b6e9-dc75c68b2c31', 'HEALTH_PERMIT', 'PENDING', 16, '2025-12-31', '2024-09-30', '2024-10-05', 4, 12);