Compare commits

..

No commits in common. "a3474512a1d687b5ab622b102b2629214c406bfb" and "005f0235fa104caa96e16d7de7f25ff226762da1" have entirely different histories.

39 changed files with 1050 additions and 1899 deletions

Binary file not shown.

591
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@f0rce/ace-widget": "1.0.2", "@f0rce/ace-widget": "1.0.2",
"@polymer/polymer": "3.5.2", "@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.5.1", "@vaadin/bundles": "24.5.1",
"@vaadin/common-frontend": "0.0.19", "@vaadin/common-frontend": "0.0.19",
@ -19,30 +19,30 @@
"@vaadin/vaadin-usage-statistics": "2.1.3", "@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.2.1", "lit": "3.1.4",
"print-js": "1.6.0", "print-js": "1.6.0",
"proj4": "2.12.1", "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.26.2" "react-router-dom": "6.23.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/preset-react": "7.25.7", "@babel/preset-react": "7.24.7",
"@preact/signals-react-transform": "0.4.0", "@preact/signals-react-transform": "0.4.0",
"@rollup/plugin-replace": "6.0.1", "@rollup/plugin-replace": "5.0.7",
"@rollup/pluginutils": "5.1.2", "@rollup/pluginutils": "5.1.0",
"@types/react": "18.3.11", "@types/react": "18.3.3",
"@types/react-dom": "18.3.1", "@types/react-dom": "18.3.0",
"@vitejs/plugin-react": "4.3.3", "@vitejs/plugin-react": "4.3.1",
"async": "3.2.6", "async": "3.2.5",
"glob": "10.4.5", "glob": "10.4.1",
"rollup-plugin-brotli": "3.1.0", "rollup-plugin-brotli": "3.1.0",
"rollup-plugin-visualizer": "5.12.0", "rollup-plugin-visualizer": "5.12.0",
"strip-css-comments": "5.0.0", "strip-css-comments": "5.0.0",
"transform-ast": "2.4.4", "transform-ast": "2.4.4",
"typescript": "5.6.3", "typescript": "5.4.5",
"vite": "5.4.9", "vite": "5.3.3",
"vite-plugin-checker": "0.8.0", "vite-plugin-checker": "0.6.4",
"workbox-build": "7.1.1", "workbox-build": "7.1.1",
"workbox-core": "7.1.0", "workbox-core": "7.1.0",
"workbox-precaching": "7.1.0" "workbox-precaching": "7.1.0"
@ -50,7 +50,7 @@
"vaadin": { "vaadin": {
"dependencies": { "dependencies": {
"@f0rce/ace-widget": "1.0.2", "@f0rce/ace-widget": "1.0.2",
"@polymer/polymer": "3.5.2", "@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.5.1", "@vaadin/bundles": "24.5.1",
"@vaadin/common-frontend": "0.0.19", "@vaadin/common-frontend": "0.0.19",
@ -65,30 +65,30 @@
"@vaadin/vaadin-usage-statistics": "2.1.3", "@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.2.1", "lit": "3.1.4",
"print-js": "1.6.0", "print-js": "1.6.0",
"proj4": "2.12.1", "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.26.2" "react-router-dom": "6.23.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/preset-react": "7.25.7", "@babel/preset-react": "7.24.7",
"@preact/signals-react-transform": "0.4.0", "@preact/signals-react-transform": "0.4.0",
"@rollup/plugin-replace": "6.0.1", "@rollup/plugin-replace": "5.0.7",
"@rollup/pluginutils": "5.1.2", "@rollup/pluginutils": "5.1.0",
"@types/react": "18.3.11", "@types/react": "18.3.3",
"@types/react-dom": "18.3.1", "@types/react-dom": "18.3.0",
"@vitejs/plugin-react": "4.3.3", "@vitejs/plugin-react": "4.3.1",
"async": "3.2.6", "async": "3.2.5",
"glob": "10.4.5", "glob": "10.4.1",
"rollup-plugin-brotli": "3.1.0", "rollup-plugin-brotli": "3.1.0",
"rollup-plugin-visualizer": "5.12.0", "rollup-plugin-visualizer": "5.12.0",
"strip-css-comments": "5.0.0", "strip-css-comments": "5.0.0",
"transform-ast": "2.4.4", "transform-ast": "2.4.4",
"typescript": "5.6.3", "typescript": "5.4.5",
"vite": "5.4.9", "vite": "5.3.3",
"vite-plugin-checker": "0.8.0", "vite-plugin-checker": "0.6.4",
"workbox-build": "7.1.1", "workbox-build": "7.1.1",
"workbox-core": "7.1.0", "workbox-core": "7.1.0",
"workbox-precaching": "7.1.0" "workbox-precaching": "7.1.0"

33
pom.xml
View File

@ -120,11 +120,6 @@
<artifactId>commons-beanutils</artifactId> <artifactId>commons-beanutils</artifactId>
<version>1.9.4</version> <version>1.9.4</version>
</dependency> </dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.17.0</version>
</dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId> <artifactId>jackson-core</artifactId>
@ -270,39 +265,11 @@
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>3.3.1</version> <version>3.3.1</version>
</dependency> </dependency>
<dependency>
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
<version>9.0.1</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
<defaultGoal>spring-boot:run</defaultGoal> <defaultGoal>spring-boot:run</defaultGoal>
<plugins> <plugins>
<plugin>
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
<version>9.0.1</version>
<executions>
<execution>
<id>get-the-git-infos</id>
<goals>
<goal>revision</goal>
</goals>
<phase>initialize</phase>
</execution>
</executions>
<configuration>
<generateGitPropertiesFile>true</generateGitPropertiesFile>
<generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties</generateGitPropertiesFilename>
<includeOnlyProperties>
<includeOnlyProperty>^git.build.(time|version)$</includeOnlyProperty>
<includeOnlyProperty>^git.commit.id.(abbrev|full)$</includeOnlyProperty>
</includeOnlyProperties>
<commitIdGenerationMode>full</commitIdGenerationMode>
</configuration>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId> <artifactId>maven-checkstyle-plugin</artifactId>

Binary file not shown.

Binary file not shown.

View File

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

View File

@ -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,35 +1,36 @@
package com.primefactorsolutions.model; package com.primefactorsolutions.model;
public enum DocumentType { public enum DocumentType {
TODOS, All,
CARNET_DE_IDENTIDAD, ID_CARD,
RECIBOS_DE_PAGO, PAY_STUB,
CONTRATO_DE_TRABAJO, PAY_SLIPS,
CERTIFICADO_DE_TRABAJO, EMPLOYMENT_CONTRACT,
WORK_CERTIFICATES,
NDA, NDA,
MEMORÁNDUMS, MEMORANDUMS,
APROBACIÓN_DE_CONTRATO_MTEPS, CONTRACT_APPROVAL_MTEPS,
CERTIFICADO_DE_ANTECEDENTES, BACKGROUND_CHECK_CERTIFICATE,
EVALUACIÓN_PRE_EMPLEO, PRE_EMPLOYMENT_EVALUATION,
FORMULARIO_DE_INSCRIPCIÓN_AL_SEGURO, INSURANCE_REGISTRATION_FORM,
FORMULARIO_DE_CANCELACIÓN_DE_SEGURO, INSURANCE_CANCELLATION_FORM,
TÍTULO_PROFESIONAL_1, PROFESSIONAL_DEGREE_1,
CERTIFICACIÓN_PROFESIONAL_1, PROFESSIONAL_CERTIFICATE_1,
TÍTULO_PROFESIONAL_2, PROFESSIONAL_DEGREE_2,
CERTIFICACIÓN_PROFECIONAL_2, PROFESSIONAL_CERTIFICATE_2,
TÍTULO_PROFESIONAL_3, PROFESSIONAL_DEGREE_3,
CERTIFICACIÓN_PROFECIONAL_3, PROFESSIONAL_CERTIFICATE_3,
NORMATIVA_LABORAL_GENERAL, GENERAL_LABOR_REGULATIONS,
NORMAS_DE_TRABAJO_REMOTO, REMOTE_WORK_GUIDELINES,
NORMAS_DE_SEGURIDAD, SAFETY_REGULATIONS,
INSTRUCTIVOS_DE_RECURSOS_HUMANOS, HUMAN_RESOURCES_GUIDELINES,
MANUAL_DE_FUNCIONES_DE_ADMINISTRACIÓN, ADMINISTRATION_FUNCTIONS_MANUAL,
MANUAL_DE_FUNCIONES_DE_INGENIERÍA, ENGINEERING_FUNCTIONS_MANUAL,
LEY_GENERAL_DEL_TRABAJO, GENERAL_LABOR_LAW,
DECRETOS_SUPREMOS, SUPREME_DECREE,
RESOLUCIONES_O_DISPOSICIONES_REGLAMENTARIAS, REGULATORY_RESOLUTION,
NORMATIVA_COMPLEMENTARIA, COMPLEMENTARY_REGULATION,
LEY_GRAL_DE_HIGIENE_SALUD_SEGURIDAD_OCUPACIONAL_Y_BIENESTAR, HEALTH_SAFETY_LAW,
NORMATIVA_REGLAMENTARIA_PARA_DESARROLLO_DE_PASANTÍAS, INTERNSHIP_RULES,
OTROS OTHER
} }

View File

@ -28,42 +28,36 @@ public class Employee extends BaseEntity implements UserDetails {
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El apellido solo debe contener letras") @Pattern(regexp = "^[a-zA-Z ]+$", message = "El apellido solo debe contener letras")
private String lastName; private String lastName;
private LocalDate birthday; private LocalDate birthday;
@Pattern(regexp = "^[a-zA-Z ,]+$", message = "La ciudad de nacimiento solo debe contener letras, espacios o comas") @Pattern(regexp = "^[a-zA-Z ]+$", message = "La ciudad de nacimiento solo debe contener letras")
private String birthCity; private String birthCity;
private String age; private String age;
@Size(max = 50, message = "La dirección de residencia no debe exceder 50 caracteres") @Size(max = 100, message = "La dirección de residencia no debe exceder 100 caracteres")
private String residenceAddress; private String residenceAddress;
@Size(max = 30, message = "La dirección local no debe exceder 100 caracteres") @Size(max = 100, message = "La dirección local no debe exceder 100 caracteres")
@Pattern(regexp = "^[a-zA-Z -]+$", message = "La dirección local solo debe contener letras y guion")
private String localAddress; private String localAddress;
@Pattern(regexp = "^[0-9]+$", message = "El número de teléfono debe contener solo números") @Pattern(regexp = "^[0-9]+$", message = "El número de teléfono debe contener solo números")
private String phoneNumber; private String phoneNumber;
@Email(message = "El correo personal no tiene un formato válido") @Email(message = "El correo personal no tiene un formato válido")
private String personalEmail; private String personalEmail;
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El cargo solo debe contener letras") @Pattern(regexp = "^[a-zA-Z ]+$", message = "El cargo solo debe contener letras")
private String position; private String position;
@ManyToOne @ManyToOne
@JoinColumn(name = "team_id", nullable = false) @JoinColumn(name = "team_id", nullable = false)
private Team team; private Team team;
@Size(max = 100, message = "El nombre de contacto de emergencia no debe exceder 100 caracteres")
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El nombre y apellido de contacto"
+ " de emergencia solo debe contener letras")
private String emergencyCName; private String emergencyCName;
@Size(max = 100, message = "La dirección de contacto de emergencia no debe exceder 100 caracteres")
private String emergencyCAddress; private String emergencyCAddress;
@Pattern(regexp = "^[0-9]+$", message = "El teléfono de contacto de emergencia " @Pattern(regexp = "^[0-9]+$", message = "El teléfono de contacto de emergencia debe contener solo números")
+ " debe contener solo números")
private String emergencyCPhone; private String emergencyCPhone;
@Email(message = "El correo de contacto de emergencia no tiene un formato válido") @Email(message = "El correo de contacto de emergencia no tiene un formato válido")
private String emergencyCEmail; private String emergencyCEmail;
@Max(value = 10, message = "El número de hijos no puede exceder a 10")
@Pattern(regexp = "^[0-9]+$", message = "La cantidad de hijos debe contener solo números") @Pattern(regexp = "^[0-9]+$", message = "La cantidad de hijos debe contener solo números")
private String numberOfChildren; private String numberOfChildren;
@Pattern(regexp = "^[0-9]+$", message = "El CI debe contener solo números") @Pattern(regexp = "^[0-9]+$", message = "El CI debe contener solo números")
private String ci; private String ci;
private String issuedIn; private String issuedIn;
@Size(max = 100, message = "El título no debe exceder 100 caracteres")
private String pTitle1; private String pTitle1;
private String pTitle2; private String pTitle2;
private String pTitle3; private String pTitle3;
@ -76,7 +70,9 @@ public class Employee extends BaseEntity implements UserDetails {
private String certification2; private String certification2;
private String certification3; private String certification3;
private String certification4; private String certification4;
@Size(max = 255, message = "El reconocimiento no debe exceder 255 caracteres")
private String recognition; private String recognition;
@Size(max = 500, message = "Los logros no deben exceder 500 caracteres")
private String achievements; private String achievements;
private String language; private String language;

View File

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

View File

@ -10,4 +10,7 @@ public enum TimeOffRequestStatus {
VENCIDO, VENCIDO,
SOLICITADO, SOLICITADO,
EN_REVISION,
COMPLETADO,
CANCELADO,
} }

View File

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

View File

@ -2,15 +2,9 @@ package com.primefactorsolutions.repositories;
import com.primefactorsolutions.model.HoursWorked; import com.primefactorsolutions.model.HoursWorked;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.time.LocalDate; @Repository
import java.util.List; public interface HoursWorkedRepository extends JpaRepository<HoursWorked, Long> {
import java.util.UUID; // Puedes definir consultas personalizadas aquí si es necesario.
public interface HoursWorkedRepository extends JpaRepository<HoursWorked, UUID> {
List<HoursWorked> findByWeekNumber(int weekNumber);
List<HoursWorked> findByDate(LocalDate date);
List<HoursWorked> findByEmployeeIdAndWeekNumber(UUID employeeId, int weekNumber);
} }

View File

@ -48,13 +48,6 @@ public class EmployeeService {
return null; return null;
} }
public String getTeamLeadName(final UUID teamId) {
// Encuentra al empleado con el rol de lead_manager en el equipo especificado
Optional<Employee> leadManager = employeeRepository.findByTeamId(teamId);
return leadManager.map(employee -> employee.getFirstName() + " " + employee.getLastName())
.orElse("No asignado");
}
public List<Employee> findEmployees( public List<Employee> findEmployees(
final int start, final int pageSize, final String sortProperty, final boolean asc) { final int start, final int pageSize, final String sortProperty, final boolean asc) {
List<Employee> employees = employeeRepository.findAll(); List<Employee> employees = employeeRepository.findAll();
@ -122,8 +115,4 @@ public class EmployeeService {
public List<Employee> findAllEmployees() { public List<Employee> findAllEmployees() {
return employeeRepository.findAll(); return employeeRepository.findAll();
} }
public List<Employee> findEmployeesByTeam(final String teamName) {
return employeeRepository.findByTeamName(teamName);
}
} }

View File

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

View File

@ -11,7 +11,6 @@ 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.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook; 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;
@ -32,59 +31,20 @@ public class ReportService {
// Este método ahora solo crea el archivo Excel a partir de los datos que recibe. // 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, public byte[] writeAsExcel(final String reportName, final List<String> headers,
final List<Map<String, Object>> data, final String selectedTeam, final List<Map<String, Object>> data)
final int weekNumber, final int currentYear)
throws IOException { throws IOException {
return createExcelFile(reportName, headers, data, selectedTeam, weekNumber, currentYear); return createExcelFile(reportName, headers, data);
} }
private byte[] createExcelFile(final String reportName, final List<String> headers, private byte[] createExcelFile(final String reportName, final List<String> headers,
final List<Map<String, Object>> data, final String selectedTeam, final List<Map<String, Object>> data)
final int weekNumber, final int currentYear)
throws IOException { throws IOException {
try (Workbook workbook = new XSSFWorkbook(); try (Workbook workbook = new XSSFWorkbook();
ByteArrayOutputStream os = new ByteArrayOutputStream()) { ByteArrayOutputStream os = new ByteArrayOutputStream()) {
Sheet sheet = workbook.createSheet(reportName); Sheet sheet = workbook.createSheet(reportName);
// Crear encabezados // Crear encabezados
// Crear una fila para el rótulo "Reporte por equipo" Row headerRow = sheet.createRow(0);
Row titleRow = sheet.createRow(0); // Fila 0 para el rótulo
Cell titleCell = titleRow.createCell(0);
// Concatenar el nombre del equipo al rótulo
String titleText = "Informe: " + weekNumber + "/" + currentYear;
titleCell.setCellValue(titleText);
// Estilo del rótulo
CellStyle titleStyle = workbook.createCellStyle();
Font titleFont = workbook.createFont();
titleFont.setBold(true);
titleFont.setFontHeightInPoints((short) 14); // Tamaño de la fuente
titleStyle.setFont(titleFont);
titleCell.setCellStyle(titleStyle);
// Fusionar celdas para el rótulo
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, headers.size() - 1)); // Ajusta el rango de celdas
// Crear filas adicionales con la información solicitada
Row asuntoRow = sheet.createRow(1); // Fila 1: Asunto
asuntoRow.createCell(0).setCellValue("Asunto: Informe semanal de horas trabajadas");
sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, headers.size() - 1));
Row semanaRow = sheet.createRow(2); // Fila 2: Semana
semanaRow.createCell(0).setCellValue("Semana: " + weekNumber); // Puedes insertar una fecha real aquí
sheet.addMergedRegion(new CellRangeAddress(2, 2, 0, headers.size() - 1));
Row horasCumplirRow = sheet.createRow(3); // Fila 3: Horas a cumplir
horasCumplirRow.createCell(0).setCellValue("Horas a cumplir: 40 horas"); // Puedes agregar las horas reales
sheet.addMergedRegion(new CellRangeAddress(3, 3, 0, headers.size() - 1));
Row teamLeadRow = sheet.createRow(4); // Fila 4: Team Lead
teamLeadRow.createCell(0).setCellValue("Team Lead: "); // Solo texto
sheet.addMergedRegion(new CellRangeAddress(4, 4, 0, headers.size() - 1));
// Crear encabezados (fila 5)
Row headerRow = sheet.createRow(5); // Los encabezados empiezan en la fila 5
CellStyle headerStyle = workbook.createCellStyle(); CellStyle headerStyle = workbook.createCellStyle();
Font headerFont = workbook.createFont(); Font headerFont = workbook.createFont();
headerFont.setBold(true); headerFont.setBold(true);
@ -96,22 +56,20 @@ public class ReportService {
cell.setCellStyle(headerStyle); cell.setCellStyle(headerStyle);
} }
// Crear filas de datos (a partir de la fila 6) // Crear filas de datos
for (int i = 0; i < data.size(); i++) { for (int i = 0; i < data.size(); i++) {
Row dataRow = sheet.createRow(i + 6); // Los datos empiezan después de la fila de encabezados Row dataRow = sheet.createRow(i + 1);
Map<String, Object> rowData = data.get(i); Map<String, Object> rowData = data.get(i);
int cellIndex = 0; int cellIndex = 0;
for (String key : headers) { for (String key : headers) {
Cell cell = dataRow.createCell(cellIndex++); Cell cell = dataRow.createCell(cellIndex++);
Object value = rowData.get(key); Object value = rowData.get(key);
if (value != null) { switch (value) {
if (value instanceof String) { case String s -> cell.setCellValue(s);
cell.setCellValue((String) value); case Number number -> cell.setCellValue(number.doubleValue());
} else if (value instanceof Number) { case null -> cell.setCellValue(""); // Manejo de valores nulos
cell.setCellValue(((Number) value).doubleValue()); default -> {
} }
} else {
cell.setCellValue(""); // Manejo de valores nulos
} }
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -39,9 +39,9 @@ import java.io.InputStream;
@PageTitle("Document") @PageTitle("Document")
@Route(value = "/documents/:documentId?/:action?", layout = MainLayout.class) @Route(value = "/documents/:documentId?/:action?", layout = MainLayout.class)
public class DocumentView extends BeanValidationForm<Document> implements HasUrlParameter<String> { public class DocumentView extends BeanValidationForm<Document> implements HasUrlParameter<String> {
private final TextField fileName = new TextField("Nombre del documento"); private final TextField fileName = new TextField("Document Name");
private final ComboBox<DocumentType> documentType = new ComboBox<>("Tipo de documento"); private final ComboBox<DocumentType> documentType = new ComboBox<>("Document Type");
private final ComboBox<Employee> employeeComboBox = new ComboBox<>("Empleado"); private final ComboBox<Employee> employeeComboBox = new ComboBox<>("Employee");
private final MemoryBuffer buffer = new MemoryBuffer(); private final MemoryBuffer buffer = new MemoryBuffer();
private final Upload uploadButton = new Upload(buffer); private final Upload uploadButton = new Upload(buffer);
private final DocumentService documentService; private final DocumentService documentService;
@ -68,19 +68,19 @@ public class DocumentView extends BeanValidationForm<Document> implements HasUrl
} }
protected Button createSaveButton() { protected Button createSaveButton() {
saveButton = new Button("Guardar"); saveButton = new Button("Save");
saveButton.addClickListener(event -> saveDocument()); saveButton.addClickListener(event -> saveDocument());
return saveButton; return saveButton;
} }
protected Button createCloseButton() { protected Button createCloseButton() {
Button closeButton = new Button("Salir"); Button closeButton = new Button("Close");
closeButton.addClickListener(event -> closeForm()); closeButton.addClickListener(event -> closeForm());
return closeButton; return closeButton;
} }
protected Button createViewDocumentButton() { protected Button createViewDocumentButton() {
viewDocumentButton = new Button("Ver documento"); viewDocumentButton = new Button("View Document");
viewDocumentButton.setEnabled(false); viewDocumentButton.setEnabled(false);
viewDocumentButton.addClickListener(event -> viewDocument()); viewDocumentButton.addClickListener(event -> viewDocument());
return viewDocumentButton; return viewDocumentButton;
@ -130,7 +130,7 @@ public class DocumentView extends BeanValidationForm<Document> implements HasUrl
ui.getPage().open(registration.getResourceUri().toString()); ui.getPage().open(registration.getResourceUri().toString());
}); });
} catch (IOException e) { } catch (IOException e) {
Notification.show("Error al leer el archivo."); Notification.show("Error reading file.");
} }
} }
@ -148,10 +148,10 @@ public class DocumentView extends BeanValidationForm<Document> implements HasUrl
setDocumentCreator(document); setDocumentCreator(document);
documentService.saveDocument(document); documentService.saveDocument(document);
Notification.show("Archivo guardado correctamente."); Notification.show("File saved successfully.");
clearForm(); clearForm();
} else { } else {
Notification.show("Error al guardar: Por favor, complete todos los campos y cargue un archivo."); Notification.show("Save failed: Please complete all fields and upload a file.");
} }
} }
@ -179,7 +179,7 @@ public class DocumentView extends BeanValidationForm<Document> implements HasUrl
try { try {
return buffer.getInputStream().readAllBytes(); return buffer.getInputStream().readAllBytes();
} catch (IOException e) { } catch (IOException e) {
Notification.show("Error al leer los datos del archivo."); Notification.show("Error reading file data.");
return new byte[0]; return new byte[0];
} }
} }
@ -220,13 +220,13 @@ public class DocumentView extends BeanValidationForm<Document> implements HasUrl
uploadButton.setAcceptedFileTypes(".pdf"); uploadButton.setAcceptedFileTypes(".pdf");
uploadButton.addSucceededListener(event -> { uploadButton.addSucceededListener(event -> {
fileUploaded = true; fileUploaded = true;
Notification.show("Archivo cargado correctamente."); Notification.show("File uploaded successfully.");
viewDocumentButton.setEnabled(true); viewDocumentButton.setEnabled(true);
updateSaveButtonState(); updateSaveButtonState();
}); });
uploadButton.getElement().addEventListener("file-remove", event -> { uploadButton.getElement().addEventListener("file-remove", event -> {
fileUploaded = false; fileUploaded = false;
Notification.show("Archivo eliminado."); Notification.show("File removed.");
viewDocumentButton.setEnabled(false); viewDocumentButton.setEnabled(false);
updateSaveButtonState(); updateSaveButtonState();
}); });

View File

@ -9,8 +9,8 @@ import com.vaadin.flow.component.UI;
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.GridSortOrder; import com.vaadin.flow.component.grid.GridSortOrder;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.html.Span; import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.data.provider.SortDirection; import com.vaadin.flow.data.provider.SortDirection;
import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
@ -24,14 +24,13 @@ import org.vaadin.firitin.components.grid.PagingGrid;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.util.List; import java.util.List;
import static com.primefactorsolutions.views.Constants.PAGE_SIZE;
@SpringComponent @SpringComponent
@Scope("prototype") @Scope("prototype")
@PageTitle("Documents") @PageTitle("Documents")
@Route(value = "/documents", layout = MainLayout.class) @Route(value = "/documents", layout = MainLayout.class)
@PermitAll @PermitAll
public class DocumentsListView extends BaseView { public class DocumentsListView extends Main {
private final DocumentService documentService; private final DocumentService documentService;
private final EmployeeService employeeService; private final EmployeeService employeeService;
@ -47,24 +46,16 @@ public class DocumentsListView extends BaseView {
} }
private void initializeView() { private void initializeView() {
getCurrentPageLayout().add(createActionButton("Añadir documento", this::navigateToAddDocumentView));
final HorizontalLayout hl = new HorizontalLayout();
hl.add(createDocumentTypeFilter());
hl.add(createEmployeeFilter());
getCurrentPageLayout().add(hl);
configureDocumentGrid(); configureDocumentGrid();
getCurrentPageLayout().add(documentGrid); add(createActionButton("Add Document", this::navigateToAddDocumentView));
add(createDocumentTypeFilter());
add(createEmployeeFilter());
add(documentGrid);
} }
private void configureDocumentGrid() { private void configureDocumentGrid() {
documentGrid.setColumns("fileName", "documentType", "creator"); documentGrid.setColumns("fileName", "documentType", "creator");
documentGrid.getColumnByKey("fileName").setHeader("Nombre archivo"); documentGrid.addComponentColumn(this::createEmployeeSpan).setHeader("Employee");
documentGrid.getColumnByKey("documentType").setHeader("Tipo");
documentGrid.getColumnByKey("creator").setHeader("Creador");
documentGrid.addComponentColumn(this::createEmployeeSpan).setHeader("Empleado");
addActionColumns(); addActionColumns();
configurePagination(); configurePagination();
} }
@ -76,9 +67,9 @@ public class DocumentsListView extends BaseView {
} }
private void addActionColumns() { private void addActionColumns() {
addDocumentActionColumn("Ver", this::navigateToDocumentView); addDocumentActionColumn("View", this::navigateToDocumentView);
addDocumentActionColumn("Editar", this::navigateToEditDocumentView); addDocumentActionColumn("Edit", this::navigateToEditDocumentView);
addDocumentActionColumn("Descargar", this::downloadDocument); addDocumentActionColumn("Download", this::downloadDocument);
} }
private void addDocumentActionColumn(final String label, final DocumentActionHandler handler) { private void addDocumentActionColumn(final String label, final DocumentActionHandler handler) {
@ -92,7 +83,7 @@ public class DocumentsListView extends BaseView {
} }
private ComboBox<DocumentType> createDocumentTypeFilter() { private ComboBox<DocumentType> createDocumentTypeFilter() {
documentTypeFilter = new ComboBox<>("Tipo de documento"); documentTypeFilter = new ComboBox<>("Document Type");
documentTypeFilter.setItems(DocumentType.values()); documentTypeFilter.setItems(DocumentType.values());
documentTypeFilter.setValue(DocumentType.values()[0]); documentTypeFilter.setValue(DocumentType.values()[0]);
documentTypeFilter.addValueChangeListener(event -> { documentTypeFilter.addValueChangeListener(event -> {
@ -102,7 +93,7 @@ public class DocumentsListView extends BaseView {
} }
private ComboBox<Employee> createEmployeeFilter() { private ComboBox<Employee> createEmployeeFilter() {
employeeFilter = new ComboBox<>("Empleado"); employeeFilter = new ComboBox<>("Employee");
List<Employee> employees = employeeService.findAllEmployees(); List<Employee> employees = employeeService.findAllEmployees();
employees.addFirst(createAllEmployeesOption()); employees.addFirst(createAllEmployeesOption());
employeeFilter.setItems(employees); employeeFilter.setItems(employees);
@ -116,13 +107,12 @@ public class DocumentsListView extends BaseView {
private Employee createAllEmployeesOption() { private Employee createAllEmployeesOption() {
Employee allEmployeesOption = new Employee(); Employee allEmployeesOption = new Employee();
allEmployeesOption.setFirstName("TODOS"); allEmployeesOption.setFirstName("All");
return allEmployeesOption; return allEmployeesOption;
} }
private String getEmployeeLabel(final Employee employee) { private String getEmployeeLabel(final Employee employee) {
return employee.getFirstName().equals("TODOS") return employee.getFirstName().equals("All") ? "All" : employee.getFirstName() + " " + employee.getLastName();
? "TODOS" : employee.getFirstName() + " " + employee.getLastName();
} }
private void navigateToEditDocumentView(final Document document) { private void navigateToEditDocumentView(final Document document) {
@ -143,7 +133,7 @@ public class DocumentsListView extends BaseView {
private void configurePagination() { private void configurePagination() {
documentGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM); documentGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
documentGrid.setPageSize(PAGE_SIZE); documentGrid.setPageSize(5);
} }
private void updateDocumentGrid(final DocumentType documentType, final Employee employee) { private void updateDocumentGrid(final DocumentType documentType, final Employee employee) {

View File

@ -1,7 +1,5 @@
package com.primefactorsolutions.views; package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.model.Team;
import com.primefactorsolutions.model.*; import com.primefactorsolutions.model.*;
import com.primefactorsolutions.service.EmployeeService; import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.ReportService; import com.primefactorsolutions.service.ReportService;
@ -56,6 +54,7 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
private final TimeOffRequestService requestService; private final TimeOffRequestService requestService;
private final TeamService teamService; private final TeamService teamService;
// TODO: campo usado para registrar al empleado en LDAP. Este campo podria estar en otro form eventualmente. // TODO: campo usado para registrar al empleado en LDAP. Este campo podria estar en otro form eventualmente.
private final TextField username = createTextField("Username: ", 30, true); private final TextField username = createTextField("Username: ", 30, true);
private final TextField firstName = createTextField("Nombres: ", 30, true); private final TextField firstName = createTextField("Nombres: ", 30, true);
@ -64,21 +63,24 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
private final ComboBox<Employee.Gender> gender = createGenderComboBox(); private final ComboBox<Employee.Gender> gender = createGenderComboBox();
private final VDatePicker birthday = new VDatePicker("Fecha de Nacimiento"); private final VDatePicker birthday = new VDatePicker("Fecha de Nacimiento");
private final TextField age = createTextField("Edad", 3, false); private final TextField age = createTextField("Edad", 3, false);
private final TextField birthCity = createTextField("Ciudad y País de Nacimiento ejemplo: (Ciudad, País) ", private final TextField birthCity = createTextField("Ciudad y País de Nacimiento", 20, false);
30, false);
private final TextField residenceAddress = createTextField("Dirección de Residencia", 50, false); private final TextField residenceAddress = createTextField("Dirección de Residencia", 50, false);
private final TextField localAddress = createTextField("Departamento y Provincia de Residencia " private final TextField localAddress = createTextField("Dep/Provincia de Residencia", 10, false);
+ " ejemplo: (Departamento-Provincia)", 30, false);
private final ComboBox<Employee.MaritalStatus> maritalStatus = createMaritalStatusComboBox(); private final ComboBox<Employee.MaritalStatus> maritalStatus = createMaritalStatusComboBox();
private final TextField numberOfChildren = createTextField("Numero de Hijos", 2, false); private final TextField numberOfChildren = createTextField("Numero de Hijos", 3, false);
private final TextField ci = createTextField("CI", 10, false); private final TextField ci = createTextField("CI", 30, false);
private final TextField issuedIn = createTextField("Expedido en ", 10, false); private final TextField issuedIn = createTextField("Expedido en ", 30, false);
private final TextField phoneNumber = createTextField("Teléfono", 8, false); private final TextField phoneNumber = createTextField("Teléfono", 8, false);
private final EmailField personalEmail = createEmailField("E-mail ejemplo: (ejemplo@gmail.com)"); private final EmailField personalEmail = createEmailField("E-mail");
private final TextField cod = createTextField("Codigo de Empleado", 30, false);
private final TextField position = createTextField("Cargo", 30, false);
private final ComboBox<Team> team = new ComboBox<>("Equipo");
private final TextField leadManager = createTextField("Lead/Manager", 30, false);
private final TextField project = createTextField("Proyecto", 30, false);
private final TextField emergencyCName = createTextField("Nombres y Apellidos de Contacto", 50, false); private final TextField emergencyCName = createTextField("Nombres y Apellidos de Contacto", 50, false);
private final TextField emergencyCAddress = createTextField("Dirección de Contacto", 50, false); private final TextField emergencyCAddress = createTextField("Dirección de Contacto", 50, false);
private final TextField emergencyCPhone = createTextField("Teléfono de Contacto", 8, false); private final TextField emergencyCPhone = createTextField("Teléfono de Contacto", 8, false);
private final EmailField emergencyCEmail = createEmailField("Email de Contacto ejemplo: (ejemplo@gmail.com)"); private final EmailField emergencyCEmail = createEmailField("Email de Contacto");
private final MemoryBuffer buffer = new MemoryBuffer(); private final MemoryBuffer buffer = new MemoryBuffer();
private final Upload upload = new Upload(buffer); private final Upload upload = new Upload(buffer);
@ -97,15 +99,10 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
private final TextField certification4 = createTextField("Certificación 4", 30, false); private final TextField certification4 = createTextField("Certificación 4", 30, false);
private final TextField recognition = createTextField("Reconocimientos", 30, false); private final TextField recognition = createTextField("Reconocimientos", 30, false);
private final TextField achievements = createTextField("Logros Profesionales", 30, false); private final TextField achievements = createTextField("Logros Profesionales", 30, false);
private final TextField language = createTextField("Idioma", 50, false); private final TextField language = createTextField("Idioma", 30, false);
private final TextField languageLevel = createTextField("Nivel de Idioma", 30, false); private final TextField languageLevel = createTextField("Nivel de Idioma", 30, false);
//INFORMACION ADMINISTRATIVA //INFORMACION DE CONTRATACION
private final TextField cod = createTextField("Codigo de Empleado", 20, false);
private final TextField position = createTextField("Cargo", 30, false);
private final ComboBox<Team> team = new ComboBox<>("Equipo");
private final TextField leadManager = createTextField("Lead/Manager", 30, false);
private final TextField project = createTextField("Proyecto", 30, false);
private final VDatePicker dateOfEntry = new VDatePicker("Fecha de Ingreso"); private final VDatePicker dateOfEntry = new VDatePicker("Fecha de Ingreso");
private final VDatePicker dateOfExit = new VDatePicker("Fecha de Retiro"); private final VDatePicker dateOfExit = new VDatePicker("Fecha de Retiro");
private final TextField contractType = createTextField("Tipo de Contratación", 30, false); private final TextField contractType = createTextField("Tipo de Contratación", 30, false);
@ -142,7 +139,7 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
//TITULOS PARA INFORMACIÓN ADMINISTRATIVA //TITULOS PARA INFORMACIÓN ADMINISTRATIVA
private final H2 infoAdm = new H2("Información Administrativa"); private final H2 infoAdm = new H2("Información Administrativa");
private final H3 infoCont = new H3("Información de Contratación"); private final H3 infoCont = new H3("Información de Contratación");
private final H3 datBanc = new H3("Datos Bancarios"); private final H3 datBanc = new H3("Datos Bancados");
private final H3 datGest = new H3("Datos Gestora Pública y Seguro Social"); private final H3 datGest = new H3("Datos Gestora Pública y Seguro Social");
public EmployeeView(final EmployeeService employeeService, public EmployeeView(final EmployeeService employeeService,
@ -177,9 +174,6 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
editButton.setVisible(true); editButton.setVisible(true);
reportButton.setVisible(true); reportButton.setVisible(true);
birthday.addValueChangeListener(event -> calculateAge()); birthday.addValueChangeListener(event -> calculateAge());
birthday.setMax(java.time.LocalDate.now());
dateOfEntry.addValueChangeListener(event -> calculateSeniority());
dateOfEntry.addValueChangeListener(event -> calculateSeniority());
reportButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> { reportButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
var employee = getEntity(); var employee = getEntity();
@ -207,30 +201,10 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
int birthYear = birthday.getValue().getYear(); int birthYear = birthday.getValue().getYear();
int ages = currentYear - birthYear; int ages = currentYear - birthYear;
age.setValue(String.valueOf(ages)); age.setValue(String.valueOf(ages));
if (ages < 18) {
birthday.setInvalid(true);
birthday.setErrorMessage("La edad no puede ser menor a 18 años.");
Notification.show("La edad ingresada no es válida, debe ser mayor o igual a 18 años.");
} else {
birthday.setInvalid(false);
}
System.out.println(age); System.out.println(age);
} }
} }
private void calculateSeniority() {
LocalDate entryDate = dateOfEntry.getValue();
LocalDate exitDate = dateOfExit.getValue() != null ? dateOfExit.getValue() : LocalDate.now();
if (entryDate != null) {
long yearsOfService = ChronoUnit.YEARS.between(entryDate, exitDate);
String seniorityValue = yearsOfService + " años ";
seniority.setValue(seniorityValue);
} else {
seniority.setValue("No disponible");
}
}
private void configureUpload() { private void configureUpload() {
upload.setAcceptedFileTypes("image/jpeg", "image/png"); upload.setAcceptedFileTypes("image/jpeg", "image/png");
upload.setMaxFileSize(1024 * 1024); upload.setMaxFileSize(1024 * 1024);
@ -242,15 +216,11 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
getEntity().setProfileImage(base64Image); getEntity().setProfileImage(base64Image);
profileImagePreview.setSrc("data:image/png;base64," + base64Image); profileImagePreview.setSrc("data:image/jpeg;base64," + base64Image);
profileImagePreview.setMaxWidth("150px"); profileImagePreview.setMaxWidth("150px");
profileImagePreview.setMaxHeight("150px"); profileImagePreview.setMaxHeight("150px");
} catch (IOException e) { } catch (IOException e) {
Notification.show("Error al subir la imagen: " + e.getMessage()); Notification.show("Error al subir la imagen.");
e.printStackTrace();
} catch (Exception e) {
Notification.show("Error en el servidor al procesar la imagen.");
e.printStackTrace();
} }
}); });
} }
@ -421,12 +391,12 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
if (employee.getProfileImage() != null && !employee.getProfileImage().isEmpty()) { if (employee.getProfileImage() != null && !employee.getProfileImage().isEmpty()) {
profileImagePreview.setSrc("data:image/jpeg;base64," + employee.getProfileImage()); profileImagePreview.setSrc("data:image/jpeg;base64," + employee.getProfileImage());
profileImagePreview.setVisible(true); profileImagePreview.setVisible(true);
profileImagePreview.setMaxWidth("250px"); profileImagePreview.setMaxWidth("150px");
profileImagePreview.setMaxHeight("250px"); profileImagePreview.setMaxHeight("150px");
upload.setVisible(true); upload.setVisible(false);
} else { } else {
profileImagePreview.setVisible(true); profileImagePreview.setVisible(false);
upload.setVisible(true); upload.setVisible(true);
} }
} }
@ -506,7 +476,6 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
emergencyCPhone.setReadOnly(false); emergencyCPhone.setReadOnly(false);
emergencyCEmail.setReadOnly(false); emergencyCEmail.setReadOnly(false);
upload.setVisible(false); upload.setVisible(false);
profileImagePreview.setVisible(true);
age.setReadOnly(false); age.setReadOnly(false);
gender.setReadOnly(false); gender.setReadOnly(false);
status.setReadOnly(false); status.setReadOnly(false);
@ -554,6 +523,7 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
birthCity, residenceAddress, localAddress, birthCity, residenceAddress, localAddress,
maritalStatus, ci, issuedIn, numberOfChildren, maritalStatus, ci, issuedIn, numberOfChildren,
phoneNumber, personalEmail, phoneNumber, personalEmail,
cod, position, team, leadManager, project,
contEmerg, emergencyCName, emergencyCAddress, emergencyCPhone, emergencyCEmail, contEmerg, emergencyCName, emergencyCAddress, emergencyCPhone, emergencyCEmail,
infProf, infProf,
titulos, pTitle1, pTitle2, pTitle3, pStudy1, pStudy2, pStudy3, titulos, pTitle1, pTitle2, pTitle3, pStudy1, pStudy2, pStudy3,
@ -561,7 +531,6 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
logros, recognition, achievements, logros, recognition, achievements,
idioma, language, languageLevel, idioma, language, languageLevel,
infoAdm, infoAdm,
cod, position, team, leadManager, project,
infoCont, dateOfEntry, dateOfExit, contractType, seniority, salary, infoCont, dateOfEntry, dateOfExit, contractType, seniority, salary,
datBanc, bankName, accountNumber, datBanc, bankName, accountNumber,
datGest, gpss, sss, beneficiaries, datGest, gpss, sss, beneficiaries,

View File

@ -4,6 +4,7 @@ import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.service.EmployeeService; import com.primefactorsolutions.service.EmployeeService;
import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.H2; import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.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;
@ -15,12 +16,13 @@ import org.springframework.context.annotation.Scope;
import java.util.List; import java.util.List;
@SpringComponent @SpringComponent
@Scope("prototype") @Scope("prototype")
@PageTitle("Employees") @PageTitle("Employees")
@Route(value = "/employees", layout = MainLayout.class) @Route(value = "/employees", layout = MainLayout.class)
@PermitAll @PermitAll
public class EmployeesListView extends BaseView { public class EmployeesListView extends Main {
private final EmployeeService employeeService; private final EmployeeService employeeService;
private final PagingGrid<Employee> table = new PagingGrid<>(Employee.class); private final PagingGrid<Employee> table = new PagingGrid<>(Employee.class);
@ -32,10 +34,10 @@ public class EmployeesListView extends BaseView {
} }
private void setupView() { private void setupView() {
getCurrentPageLayout().add(new H2("Employee List")); add(new H2("Employee List"));
configureTable(); configureTable();
getCurrentPageLayout().add(createAddEmployeeButton()); add(createAddEmployeeButton());
getCurrentPageLayout().add(table); add(table);
} }
private void configureTable() { private void configureTable() {
@ -73,7 +75,7 @@ public class EmployeesListView extends BaseView {
private void setupPagingGrid() { private void setupPagingGrid() {
table.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM); table.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
table.setPageSize(Constants.PAGE_SIZE); table.setPageSize(5);
} }
private void refreshGrid() { private void refreshGrid() {

View File

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

View File

@ -0,0 +1,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,256 +1,270 @@
package com.primefactorsolutions.views; package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Actividad;
import com.primefactorsolutions.model.Employee; import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.model.HoursWorked; import com.primefactorsolutions.model.HoursWorked;
import com.primefactorsolutions.model.Team;
import com.primefactorsolutions.service.EmployeeService; import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.HoursWorkedService; import com.primefactorsolutions.service.HoursWorkedService;
import com.primefactorsolutions.service.TeamService; import com.vaadin.flow.component.datepicker.DatePicker;
import com.vaadin.flow.component.Component; import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.notification.Notification; import com.vaadin.flow.component.notification.Notification;
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.router.*; 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 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 org.vaadin.firitin.components.datepicker.VDatePicker; import com.vaadin.flow.component.html.Label;
import org.vaadin.firitin.form.BeanValidationForm;
import java.time.DayOfWeek;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.YearMonth;
import java.time.temporal.IsoFields; import java.time.temporal.IsoFields;
import java.time.temporal.WeekFields;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.Locale;
@SpringComponent @SpringComponent
@PermitAll @PermitAll
@Scope("prototype") @Scope("prototype")
@PageTitle("Horas Trabajadas") @PageTitle("Hours Worked")
@Route(value = "/hours-worked-list/:hours-workedId?/:action?", layout = MainLayout.class) @Route(value = "/hours-worked/me", layout = MainLayout.class)
public class HoursWorkedView extends BeanValidationForm<HoursWorked> implements HasUrlParameter<String> { public class HoursWorkedView extends VerticalLayout {
private final VDatePicker dateField = new VDatePicker("Fecha"); private final List<Actividad> actividades = new ArrayList<>();
private final ComboBox<Team> teamField = new ComboBox<>("Equipo"); private final Grid<Actividad> grid = new Grid<>(Actividad.class);
private ComboBox<Employee> employeeField;
private final ComboBox<String> tareasEspecificasDropdown = new ComboBox<>("Tarea Específica");
private final TextField tareaEspecificaInput = new TextField("Otra Tarea Específica");
private final TextField horasTareaEspecificaField = new TextField("Horas Tarea Específica");
private final TextField activityField = new TextField("Actividad");
private final TextField hoursField = new TextField("Horas");
private final H2 equipoLabel = new H2("Tareas del Cliente/Equipo"); private final ComboBox<Employee> employeeComboBox = new ComboBox<>("Employee");
private final H2 empresaLabel = new H2("Tareas de la Empresa"); private LocalDate selectedStartOfWeek;
private final Label totalCompletadoLabel = new Label(); private int weekNumber;
private final HoursWorkedService hoursWorkedService; @Autowired
private final EmployeeService employeeService; private final EmployeeService employeeService;
private final TeamService teamService;
private HoursWorked hoursWorked;
private Employee employee;
private Button saveButton; 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();
public HoursWorkedView(final HoursWorkedService hoursWorkedService, @Autowired
final EmployeeService employeeService, private final HoursWorkedService hoursWorkedService;
final TeamService teamService) { public HoursWorkedView(final EmployeeService employeeService, final HoursWorkedService hoursWorkedService) {
super(HoursWorked.class);
this.hoursWorkedService = hoursWorkedService;
this.employeeService = employeeService; this.employeeService = employeeService;
this.teamService = teamService; this.hoursWorkedService = hoursWorkedService;
configurarVista();
initializeDateField(); cargarDatos();
initializeTeamField();
initializeEmployeeField();
configureTareasEspecificas();
} }
@Override private void cargarDatos() {
public void setParameter(final BeforeEvent beforeEvent, final String action) { List<HoursWorked> listaDeHorasTrabajadas = obtenerDatos(); // Obtenemos la lista aquí
final RouteParameters params = beforeEvent.getRouteParameters(); grid.setItems(actividades);
final String s = params.get("hours-workedId").orElse(null);
if ("new".equals(action)) { double totalHoras = calcularTotalHoras(listaDeHorasTrabajadas); // Pasa la lista aquí
setEntityWithEnabledSave(new HoursWorked());
} else {
UUID hoursWorkedId = UUID.fromString(s);
var hoursWorked = hoursWorkedService.getHoursWorked(hoursWorkedId);
setEntityWithEnabledSave(hoursWorked);
if ("edit".equals(action) && !s.isEmpty()) {
saveButton.setVisible(true);
} else if ("view".equals(action) && !s.isEmpty()) {
saveButton.setVisible(false);
}
}
} }
@Override private void setEmployeeComboBoxProperties() {
protected List<Component> getFormComponents() { employeeComboBox.setWidth("250px");
return List.of( employeeComboBox.setPlaceholder("Buscar empleado...");
dateField, employeeComboBox.setItems(employeeService.findAllEmployees());
teamField, employeeComboBox.setItemLabelGenerator(employee -> employee.getFirstName() + " " + employee.getLastName());
employeeField,
equipoLabel, employeeComboBox.setAllowCustomValue(false);
activityField, employeeComboBox.addCustomValueSetListener(event ->
hoursField, Notification.show("Selecciona un empleado válido de la lista.")
empresaLabel,
tareasEspecificasDropdown,
tareaEspecificaInput,
horasTareaEspecificaField,
createCloseButton()
); );
}
private void configureTareasEspecificas() { employeeComboBox.addValueChangeListener(event -> {
tareasEspecificasDropdown.setItems("Entrevistas", "Reuniones", Employee selectedEmployee = event.getValue();
"Colaboraciones", "Aprendizajes", "Proyectos PFS", "Otros"); if (selectedEmployee != null) {
tareasEspecificasDropdown.setPlaceholder("Selecciona una tarea..."); Notification.show("Empleado seleccionado: "
+ selectedEmployee.getFirstName() + " "
tareasEspecificasDropdown.addValueChangeListener(event -> { + selectedEmployee.getLastName());
String selected = event.getValue();
boolean isOtros = "Otros".equals(selected);
tareaEspecificaInput.setVisible(isOtros);
horasTareaEspecificaField.setVisible(true);
if (!isOtros) {
tareaEspecificaInput.clear();
horasTareaEspecificaField.clear();
} }
}); });
tareaEspecificaInput.setVisible(false);
horasTareaEspecificaField.setVisible(false);
} }
protected Button createSaveButton() { private int getWeekOfYear(final LocalDate date) {
saveButton = new Button("Guardar"); return date.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
saveButton.addClickListener(event -> saveHoursWorked());
return saveButton;
} }
protected Button createCloseButton() { private void configurarVista() {
Button closeButton = new Button("Cerrar"); DatePicker fechaPicker = new DatePicker("Selecciona una fecha");
closeButton.addClickListener(event -> closeForm()); fechaPicker.addValueChangeListener(event -> {
return closeButton;
}
private void initializeTeamField() {
List<Team> teams = new ArrayList<>(teamService.findAllTeams());
teamField.setItems(teamService.findAllTeams());
teamField.setItemLabelGenerator(Team::getName);
teamField.setValue(teams.getFirst());
teamField.addValueChangeListener(event -> {
if (teams != null) {
employeeField.getValue();
event.getValue();
}
}
);
}
private ComboBox<Employee> initializeEmployeeField() {
employeeField = new ComboBox<>("Empleado");
List<Employee> employees = new ArrayList<>(employeeService.findAllEmployees());
employeeField.setItems(employees);
employeeField.setItemLabelGenerator(this::getEmployeeFullName);
employeeField.setValue(employees.getFirst());
return employeeField;
}
private String getEmployeeFullName(final Employee employee) {
return "TODOS".equals(employee.getFirstName())
? "TODOS" : employee.getFirstName() + " " + employee.getLastName();
}
private void initializeDateField() {
LocalDate today = LocalDate.now();
YearMonth currentMonth = YearMonth.of(today.getYear(), today.getMonth());
LocalDate startOfMonth = currentMonth.atDay(1);
LocalDate maxSelectableDate = today;
dateField.setMin(startOfMonth);
dateField.setMax(maxSelectableDate);
dateField.setValue(today);
dateField.addValueChangeListener(event -> {
LocalDate selectedDate = event.getValue(); LocalDate selectedDate = event.getValue();
if (selectedDate != null) { if (selectedDate != null) {
int weekNumber = selectedDate.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR); selectedStartOfWeek = getStartOfWeek(selectedDate);
Notification.show("Número de la semana: " + weekNumber, 3000, Notification.Position.BOTTOM_CENTER); LocalDate endOfWeek = selectedStartOfWeek.plusDays(6);
if (hoursWorked != null) { fechasLabel.setText("Semana del " + selectedStartOfWeek + " al " + endOfWeek);
hoursWorked.setWeekNumber(weekNumber); weekNumber = getWeekOfYear(selectedDate);
}
} }
}); });
Button verMesButton = new Button("Ver Mes", event -> {
getUI().ifPresent(ui -> ui.navigate(HoursWorkedMonthView.class));
});
ComboBox<String> equipoDropdown = new ComboBox<>("Equipo");
equipoDropdown.setItems("Equipo 1", "Equipo 2", "Equipo 3");
equipoDropdown.setWidth("250px");
setEmployeeComboBoxProperties();
HorizontalLayout filtersLayout = new HorizontalLayout(equipoDropdown, employeeComboBox);
filtersLayout.setSpacing(true);
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 saveHoursWorked() { private void configurarGrid() {
if (isFormValid()) { grid.removeAllColumns();
HoursWorked hoursWorked = getEntity(); grid.setItems(actividades);
setFieldValues(hoursWorked); grid.addColumn(Actividad::getNombre).setHeader("Actividad");
hoursWorkedService.save(hoursWorked); grid.addColumn(Actividad::getLunes).setHeader("Lunes");
Notification.show("Horas trabajadas guardadas correctamente."); grid.addColumn(Actividad::getMartes).setHeader("Martes");
closeForm(); 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 Día").setKey("totalDia");
} }
private void setFieldValues(final HoursWorked hoursWorked) { private HorizontalLayout configurarFormularioActividades() {
hoursWorked.setDate(dateField.getValue()); TextField actividadNombre = new TextField("Actividad");
hoursWorked.setTeam(teamField.getValue()); actividadNombre.setWidth("200px");
hoursWorked.setEmployee(employeeField.getValue());
hoursWorked.setActividad(activityField.getValue()); TextField lunesHoras = crearCampoHora("Lunes");
try { TextField martesHoras = crearCampoHora("Martes");
double hours = Double.parseDouble(hoursField.getValue()); TextField miercolesHoras = crearCampoHora("Miércoles");
hoursWorked.setHours(hours); TextField juevesHoras = crearCampoHora("Jueves");
} catch (NumberFormatException e) { TextField viernesHoras = crearCampoHora("Viernes");
Notification.show("Por favor, ingrese un número válido para las horas."); TextField sabadoHoras = crearCampoHora("Sábado");
} TextField domingoHoras = crearCampoHora("Domingo");
if ("Otros".equals(tareasEspecificasDropdown.getValue())) {
hoursWorked.setTareasEspecificas(tareaEspecificaInput.getValue()); Button agregarActividadButton = new Button("Agregar Actividad", e -> {
try { try {
double horasEspecifica = Double.parseDouble(horasTareaEspecificaField.getValue()); Actividad nuevaActividad = new Actividad.Builder()
hoursWorked.setHorasTareasEspecificas(horasEspecifica); .nombre(actividadNombre.getValue())
double totalHoras = hoursWorked.getHours() + horasEspecifica; .lunes(parseHoras(lunesHoras.getValue()))
hoursWorked.setTotalHours(totalHoras); .martes(parseHoras(martesHoras.getValue()))
} catch (NumberFormatException e) { .miercoles(parseHoras(miercolesHoras.getValue()))
Notification.show("Por favor, ingrese un número válido para las horas de la tarea específica."); .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);
}
private TextField crearCampoHora(final String placeholder) {
TextField field = new TextField(placeholder);
field.setWidth("80px");
field.setPlaceholder("0.0");
return field;
}
private double parseHoras(final String value) {
if (value == null || value.trim().isEmpty()) {
return 0.0;
}
return Double.parseDouble(value);
}
private LocalDate getStartOfWeek(final LocalDate date) {
WeekFields weekFields = WeekFields.of(Locale.getDefault());
return date.with(weekFields.dayOfWeek(), DayOfWeek.MONDAY.getValue());
}
private double calcularTotalPorDia(final Actividad actividad) {
return actividad.getLunes() + actividad.getMartes() + actividad.getMiercoles()
+ actividad.getJueves() + actividad.getViernes() + actividad.getSabado() + actividad.getDomingo();
}
private void actualizarTotales() {
double totalSemanaCompletada = actividades.stream()
.mapToDouble(this::calcularTotalPorDia)
.sum();
double horasPendientes = 40 - totalSemanaCompletada;
totalCompletadoLabel.setText("Total Hrs/Semana Completadas: " + totalSemanaCompletada);
horasPendientesLabel.setText("Horas Pendientes: " + horasPendientes);
}
private void guardarActividades() {
Employee selectedEmployee = employeeComboBox.getValue();
if (selectedEmployee == null) {
Notification.show("Por favor, selecciona un empleado antes de guardar.");
return;
}
double totalHorasSemana = actividades.stream()
.mapToDouble(this::calcularTotalPorDia)
.sum();
HoursWorked hoursWorked = new HoursWorked();
hoursWorked.setEmployee(selectedEmployee);
hoursWorked.setWeekNumber(weekNumber);
hoursWorked.setTotalHours(totalHorasSemana);
try {
hoursWorkedService.saveHoursWorked(hoursWorked); // Usa saveHoursWorked directamente
Notification.show("Actividades guardadas correctamente.");
} catch (Exception e) {
Notification.show("Error al guardar actividades: " + e.getMessage());
} }
} }
private void closeForm() { private double calcularTotalHoras(final List<HoursWorked> listaDeHorasTrabajadas) {
getUI().ifPresent(ui -> ui.navigate("hours-worked-list/" + hoursWorked.getId().toString())); return listaDeHorasTrabajadas.stream()
.mapToDouble(HoursWorked::getTotalHours)
.sum();
} }
private boolean isFormValid() { private List<HoursWorked> obtenerDatos() {
return dateField.getValue() != null return new ArrayList<>();
&&
teamField.getValue() != null
&&
employeeField.getValue() != null
&&
!activityField.isEmpty();
} }
private void configureViewOrEditAction(final String action) { private void closeView() {
if ("edit".equals(action) && hoursWorked != null) { getUI().ifPresent(ui -> ui.navigate(HoursWorkedView.class));
setFieldsReadOnly(false);
} else if ("view".equals(action) && hoursWorked != null) {
setFieldsReadOnly(true);
saveButton.setEnabled(false);
}
}
private void setFieldsReadOnly(final boolean readOnly) {
dateField.setReadOnly(readOnly);
teamField.setReadOnly(readOnly);
employeeField.setReadOnly(readOnly);
activityField.setReadOnly(readOnly);
} }
} }

View File

@ -13,7 +13,6 @@ import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.auth.AnonymousAllowed; import com.vaadin.flow.server.auth.AnonymousAllowed;
@SuppressWarnings("unused")
@Route("init-account") @Route("init-account")
@PageTitle("PFS Intra") @PageTitle("PFS Intra")
@AnonymousAllowed @AnonymousAllowed

View File

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

View File

@ -22,8 +22,6 @@ import com.vaadin.flow.component.sidenav.SideNavItem;
import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.spring.security.AuthenticationContext; import com.vaadin.flow.spring.security.AuthenticationContext;
import com.vaadin.flow.theme.lumo.LumoUtility; import com.vaadin.flow.theme.lumo.LumoUtility;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.vaadin.lineawesome.LineAwesomeIcon; import org.vaadin.lineawesome.LineAwesomeIcon;
@ -37,11 +35,10 @@ public class MainLayout extends AppLayout {
private H1 viewTitle; private H1 viewTitle;
public MainLayout(final AuthenticationContext authContext, public MainLayout(final AuthenticationContext authContext) {
@Autowired @Value("${git.commit.id.abbrev}") final String commitId) {
this.authContext = authContext; this.authContext = authContext;
setPrimarySection(Section.DRAWER); setPrimarySection(Section.DRAWER);
addDrawerContent(commitId); addDrawerContent();
addHeaderContent(); addHeaderContent();
} }
@ -115,12 +112,12 @@ public class MainLayout extends AppLayout {
return icon; return icon;
} }
private void addDrawerContent(final String commitId) { 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);
final Header header = new Header(appName); final Header header = new Header(appName);
final Scroller scroller = new Scroller(createNavigation()); final Scroller scroller = new Scroller(createNavigation());
addToDrawer(header, scroller, createFooter(commitId)); addToDrawer(header, scroller, createFooter());
} }
private SideNav createNavigation() { private SideNav createNavigation() {
@ -153,9 +150,7 @@ public class MainLayout extends AppLayout {
LineAwesomeIcon.LIST_ALT.create())); LineAwesomeIcon.LIST_ALT.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("Registro de Horas Trabajadas", HoursWorkedListView.class, timesheet.addItem(new SideNavItem("Hours Worked", HoursWorkedView.class,
LineAwesomeIcon.ID_CARD_SOLID.create()));
timesheet.addItem(new SideNavItem("Reporte Horas Trabajadas", ReporteView.class,
LineAwesomeIcon.ID_CARD_SOLID.create())); LineAwesomeIcon.ID_CARD_SOLID.create()));
SideNavItem profile = new SideNavItem("My Profile", ProfileView.class, SideNavItem profile = new SideNavItem("My Profile", ProfileView.class,
@ -172,8 +167,8 @@ public class MainLayout extends AppLayout {
return nav; return nav;
} }
private Footer createFooter(final String commitId) { private Footer createFooter() {
return new Footer(new Text(String.format("v.%s", commitId))); return new Footer();
} }
@Override @Override

View File

@ -1,5 +1,6 @@
package com.primefactorsolutions.views; package com.primefactorsolutions.views;
import com.primefactorsolutions.service.TimeOffRequestService;
import com.vaadin.flow.component.Text; import com.vaadin.flow.component.Text;
import com.vaadin.flow.component.html.Main; import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.PageTitle;
@ -11,7 +12,7 @@ import jakarta.annotation.security.PermitAll;
@PermitAll @PermitAll
public class MainView extends Main { public class MainView extends Main {
public MainView() { public MainView(final TimeOffRequestService requestService) {
add(new Text("Welcome")); add(new Text("Welcome"));
} }
} }

View File

@ -6,6 +6,7 @@ import com.primefactorsolutions.service.TeamService;
import com.primefactorsolutions.service.TimeOffRequestService; import com.primefactorsolutions.service.TimeOffRequestService;
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.html.Main;
import com.vaadin.flow.component.notification.Notification; 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.router.PageTitle; import com.vaadin.flow.router.PageTitle;
@ -18,31 +19,32 @@ import org.vaadin.firitin.components.grid.PagingGrid;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.primefactorsolutions.views.Constants.PAGE_SIZE;
@SpringComponent @SpringComponent
@Scope("prototype") @Scope("prototype")
@PageTitle("PendingRequests") @PageTitle("PendingRequests")
@Route(value = "/pending-requests", layout = MainLayout.class) @Route(value = "/pending-requests", layout = MainLayout.class)
@PermitAll @PermitAll
public class PendingRequestsListView extends BaseView { public class PendingRequestsListView extends Main {
private final TimeOffRequestService requestService; private final TimeOffRequestService requestService;
private final EmployeeService employeeService; private final EmployeeService employeeService;
private final TeamService teamService; private final TeamService teamService;
private final PagingGrid<TimeOffRequest> pendingRequestsGrid = new PagingGrid<>(); private final PagingGrid<TimeOffRequest> pendingRequestsGrid = new PagingGrid<>();
private List<Employee> employees = Collections.emptyList();
private ComboBox<Employee> employeeFilter; private ComboBox<Employee> employeeFilter;
private ComboBox<Team> teamFilter; private ComboBox<Team> teamFilter;
private ComboBox<TimeOffRequestType> categoryFilter; private ComboBox<TimeOffRequestType> categoryFilter;
private UUID selectedRequestId; private UUID selectedRequestId;
public PendingRequestsListView(final TimeOffRequestService requestService, public PendingRequestsListView(final TimeOffRequestService requestService,
final EmployeeService employeeService, final EmployeeService employeeService,
final TeamService teamService) { final TeamService teamService) {
this.requestService = requestService; this.requestService = requestService;
this.employeeService = employeeService; this.employeeService = employeeService;
this.teamService = teamService; this.teamService = teamService;
this.employees = employeeService.findAllEmployees();
initializeView(); initializeView();
refreshGeneralPendingRequestsGrid(null, null, null); refreshGeneralPendingRequestsGrid(null, null, null);
} }
@ -50,16 +52,14 @@ public class PendingRequestsListView extends BaseView {
private void initializeView() { private void initializeView() {
setupFilters(); setupFilters();
setupPendingRequestsGrid(); setupPendingRequestsGrid();
createActionButtons(); add(pendingRequestsGrid);
add(createActionButtons());
} }
private void setupFilters() { private void setupFilters() {
final HorizontalLayout hl = new HorizontalLayout(); add(createEmployeeFilter());
hl.add(createEmployeeFilter()); add(createTeamFilter());
hl.add(createTeamFilter()); add(createCategoryFilter());
hl.add(createCategoryFilter());
getCurrentPageLayout().add(hl);
} }
private void setupPendingRequestsGrid() { private void setupPendingRequestsGrid() {
@ -68,23 +68,20 @@ public class PendingRequestsListView extends BaseView {
pendingRequestsGrid.addColumn(this::getCategory).setHeader("Categoría"); pendingRequestsGrid.addColumn(this::getCategory).setHeader("Categoría");
pendingRequestsGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM); pendingRequestsGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
pendingRequestsGrid.setPageSize(PAGE_SIZE); pendingRequestsGrid.setPageSize(5);
pendingRequestsGrid.asSingleSelect().addValueChangeListener(event -> { pendingRequestsGrid.asSingleSelect().addValueChangeListener(event -> {
TimeOffRequest selectedRequest = event.getValue(); TimeOffRequest selectedRequest = event.getValue();
if (selectedRequest != null) { if (selectedRequest != null) {
selectedRequestId = selectedRequest.getId(); selectedRequestId = selectedRequest.getId();
} }
}); });
getCurrentPageLayout().add(pendingRequestsGrid);
} }
private void createActionButtons() { private HorizontalLayout createActionButtons() {
final Button approveButton = createActionButton("Aprobar", TimeOffRequestStatus.APROBADO); Button approveButton = createActionButton("Aprobar", TimeOffRequestStatus.APROBADO);
final Button rejectButton = createActionButton("Rechazar", TimeOffRequestStatus.RECHAZADO); Button rejectButton = createActionButton("Rechazar", TimeOffRequestStatus.RECHAZADO);
final Button closeButton = new Button("Salir", event -> navigateToMainView()); Button closeButton = new Button("Salir", event -> navigateToMainView());
return new HorizontalLayout(approveButton, rejectButton, closeButton);
getCurrentPageLayout().add(new HorizontalLayout(approveButton, rejectButton, closeButton));
} }
private Button createActionButton(final String caption, final TimeOffRequestStatus status) { private Button createActionButton(final String caption, final TimeOffRequestStatus status) {

View File

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

View File

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

View File

@ -4,7 +4,6 @@ import com.primefactorsolutions.model.*;
import com.primefactorsolutions.service.EmployeeService; import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.TimeOffRequestService; import com.primefactorsolutions.service.TimeOffRequestService;
import com.primefactorsolutions.service.VacationService; import com.primefactorsolutions.service.VacationService;
import com.vaadin.flow.component.UI;
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.html.Div; import com.vaadin.flow.component.html.Div;
@ -17,28 +16,16 @@ import com.vaadin.flow.router.BeforeEvent;
import com.vaadin.flow.router.HasUrlParameter; import com.vaadin.flow.router.HasUrlParameter;
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.server.StreamRegistration;
import com.vaadin.flow.server.StreamResource;
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.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.grid.PagingGrid; import org.vaadin.firitin.components.grid.PagingGrid;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.Period; import java.time.Period;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.primefactorsolutions.views.Constants.PAGE_SIZE;
@SpringComponent @SpringComponent
@PermitAll @PermitAll
@Scope("prototype") @Scope("prototype")
@ -109,7 +96,7 @@ public class RequestEmployeeView extends Div implements HasUrlParameter<String>
requestGrid.getColumnByKey("daysToBeTake").setHeader("Días a Tomar"); requestGrid.getColumnByKey("daysToBeTake").setHeader("Días a Tomar");
requestGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM); requestGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
requestGrid.setPageSize(PAGE_SIZE); requestGrid.setPageSize(5);
requestGrid.asSingleSelect().addValueChangeListener(event -> { requestGrid.asSingleSelect().addValueChangeListener(event -> {
TimeOffRequest selectedRequest = event.getValue(); TimeOffRequest selectedRequest = event.getValue();
if (selectedRequest != null) { if (selectedRequest != null) {
@ -141,47 +128,43 @@ public class RequestEmployeeView extends Div implements HasUrlParameter<String>
Employee employee = employeeService.getEmployee(employeeId); Employee employee = employeeService.getEmployee(employeeId);
boolean isMale = employee.getGender() == Employee.Gender.MALE; boolean isMale = employee.getGender() == Employee.Gender.MALE;
int currentYear = LocalDate.now().getYear(); int currentYear = LocalDate.now().getYear();
LocalDate currentDate = LocalDate.now();
List<Vacation> vacations = vacationService.findVacations(); List<Vacation> vacations = vacationService.findVacations();
double healthLicence = getHealthLicence(employeeId); double healthLicence = 2;
List<TimeOffRequest> healthRequests = requestService
.findByEmployeeAndCategory(employeeId, TimeOffRequestType.PERMISOS_DE_SALUD);
if (healthRequests != null && !healthRequests.isEmpty()) {
healthLicence = healthRequests.getLast().getDaysBalance();
}
double totalFixedAndMovableHolidays = calculateHolidayDays(vacations); double totalFixedAndMovableHolidays = calculateHolidayDays(vacations);
double totalPersonalDays = calculatePersonalDays(vacations, isMale); double totalPersonalDays = calculatePersonalDays(vacations, isMale);
List<Double> vacationDays = calculateVacationDays(employee); List<Double> vacationDays = calculateVacationDays(employee);
double totalVacationCurrentDays = calculateUtilizedVacationDays( double utilizedVacationCurrentDays = vacationDays.get(1);
vacationDays.get(1), List<TimeOffRequest> vacationCurrentRequests = requestService
TimeOffRequestType.VACACION_GESTION_ACTUAL .findByEmployeeAndCategory(employeeId, TimeOffRequestType.VACACION_GESTION_ACTUAL);
); if (vacationCurrentRequests != null && !vacationCurrentRequests.isEmpty()) {
double totalVacationPreviousDays = calculateUtilizedVacationDays( utilizedVacationCurrentDays = vacationCurrentRequests.getLast().getDaysBalance();
vacationDays.get(0), }
TimeOffRequestType.VACACION_GESTION_ANTERIOR double totalVacationCurrentDays = vacationDays.get(1) - (vacationDays.get(1) - utilizedVacationCurrentDays);
);
double utilizedVacationPreviousDays = vacationDays.get(0);
List<TimeOffRequest> vacationPreviousRequests = requestService
.findByEmployeeAndCategory(employeeId, TimeOffRequestType.VACACION_GESTION_ANTERIOR);
if (vacationPreviousRequests != null && !vacationPreviousRequests.isEmpty()) {
utilizedVacationPreviousDays = vacationPreviousRequests.getLast().getDaysBalance();
}
double totalVacationPreviousDays = vacationDays.getFirst()
- (vacationDays.getFirst() - utilizedVacationPreviousDays);
double utilizedFixedAndMovableHolidays = calculateHolidayUtilizedDays(currentYear); double utilizedFixedAndMovableHolidays = calculateHolidayUtilizedDays(currentYear);
double utilizedPersonalDays = calculatePersonalDaysUtilized(isMale, currentYear); double utilizedPersonalDays = calculatePersonalDaysUtilized(isMale, currentYear);
double remainingHolidayDays = calculateRemainingHolidayDays( double remainingHolidayDays = totalFixedAndMovableHolidays - utilizedFixedAndMovableHolidays;
totalFixedAndMovableHolidays, double remainingPersonalDays = (totalPersonalDays - utilizedPersonalDays) + healthLicence;
utilizedFixedAndMovableHolidays, double remainingVacationDays = totalVacationCurrentDays + totalVacationPreviousDays;
employee.getDateOfExit(),
currentDate
);
double remainingPersonalDays = calculateRemainingPersonalDays(
totalPersonalDays,
utilizedPersonalDays,
healthLicence,
employee.getDateOfExit(),
currentDate
);
double remainingVacationDays = calculateRemainingVacationDays(
totalVacationCurrentDays,
totalVacationPreviousDays,
employee.getDateOfExit(),
currentDate
);
double totalAvailableDays = remainingHolidayDays + remainingPersonalDays + remainingVacationDays; double totalAvailableDays = remainingHolidayDays + remainingPersonalDays + remainingVacationDays;
@ -193,51 +176,6 @@ public class RequestEmployeeView extends Div implements HasUrlParameter<String>
); );
} }
private double getHealthLicence(final UUID employeeId) {
List<TimeOffRequest> healthRequests = requestService
.findByEmployeeAndCategory(employeeId, TimeOffRequestType.PERMISOS_DE_SALUD);
return healthRequests != null && !healthRequests.isEmpty() ? healthRequests.getLast().getDaysBalance() : 2;
}
private double calculateUtilizedVacationDays(final double vacationDays, final TimeOffRequestType requestType) {
List<TimeOffRequest> vacationRequests = requestService.findByEmployeeAndCategory(employeeId, requestType);
if (vacationRequests != null && !vacationRequests.isEmpty()) {
return vacationRequests.getLast().getDaysBalance();
}
return vacationDays;
}
private double calculateRemainingVacationDays(final double totalVacationCurrentDays,
final double totalVacationPreviousDays,
final LocalDate exitDate,
final LocalDate currentDate) {
if (exitDate == null || exitDate.isAfter(currentDate)) {
return totalVacationCurrentDays + totalVacationPreviousDays;
}
return 0;
}
private double calculateRemainingHolidayDays(final double totalFixedAndMovableHolidays,
final double utilizedFixedAndMovableHolidays,
final LocalDate exitDate,
final LocalDate currentDate) {
if (exitDate == null || exitDate.isAfter(currentDate)) {
return totalFixedAndMovableHolidays - utilizedFixedAndMovableHolidays;
}
return 0;
}
private double calculateRemainingPersonalDays(final double totalPersonalDays,
final double utilizedPersonalDays,
final double healthLicence,
final LocalDate exitDate,
final LocalDate currentDate) {
if (exitDate == null || exitDate.isAfter(currentDate)) {
return (totalPersonalDays - utilizedPersonalDays) + healthLicence;
}
return 0;
}
private double calculateHolidayDays(final List<Vacation> vacations) { private double calculateHolidayDays(final List<Vacation> vacations) {
return vacations.stream() return vacations.stream()
.filter(req -> req.getType() != Vacation.Type.OTHER) .filter(req -> req.getType() != Vacation.Type.OTHER)
@ -306,8 +244,10 @@ public class RequestEmployeeView extends Div implements HasUrlParameter<String>
private double calculateHolidayUtilizedDays(final int year) { private double calculateHolidayUtilizedDays(final int year) {
return requests.stream() return requests.stream()
.filter(this::verificationIsHoliday) .filter(this::verificationIsHoliday)
.filter(req -> req.getState() == TimeOffRequestStatus.TOMADO .filter(this::verificationIsHoliday)
|| req.getState() == TimeOffRequestStatus.APROBADO) .filter(req -> req.getState() == TimeOffRequestStatus.APROBADO)
.filter(req -> req.getState() == TimeOffRequestStatus.EN_USO)
.filter(req -> req.getState() == TimeOffRequestStatus.TOMADO)
.filter(req -> getStartDateYear(req) == year) .filter(req -> getStartDateYear(req) == year)
.mapToDouble(TimeOffRequest::getDaysToBeTake) .mapToDouble(TimeOffRequest::getDaysToBeTake)
.sum(); .sum();
@ -316,8 +256,9 @@ public class RequestEmployeeView extends Div implements HasUrlParameter<String>
private double calculatePersonalDaysUtilized(final boolean isMale, final int year) { private double calculatePersonalDaysUtilized(final boolean isMale, final int year) {
return requests.stream() return requests.stream()
.filter(req -> !verificationIsHoliday(req)) .filter(req -> !verificationIsHoliday(req))
.filter(req -> req.getState() == TimeOffRequestStatus.TOMADO .filter(req -> req.getState() == TimeOffRequestStatus.APROBADO)
|| req.getState() == TimeOffRequestStatus.APROBADO) .filter(req -> req.getState() == TimeOffRequestStatus.EN_USO)
.filter(req -> req.getState() == TimeOffRequestStatus.TOMADO)
.filter(req -> !getStandardExclusions().contains(req.getCategory())) .filter(req -> !getStandardExclusions().contains(req.getCategory()))
.filter(req -> !(isMale && getMaleSpecificExclusions().contains(req.getCategory()))) .filter(req -> !(isMale && getMaleSpecificExclusions().contains(req.getCategory())))
.filter(req -> !req.getCategory().name().startsWith("VACACION")) .filter(req -> !req.getCategory().name().startsWith("VACACION"))
@ -356,10 +297,9 @@ public class RequestEmployeeView extends Div implements HasUrlParameter<String>
private HorizontalLayout createActionButtons() { private HorizontalLayout createActionButtons() {
Button viewButton = createButton("Ver", () -> navigateToViewRequest(request)); Button viewButton = createButton("Ver", () -> navigateToViewRequest(request));
Button editButton = createButton("Editar", () -> navigateToEditRequest(request)); Button editButton = createButton("Editar", () -> navigateToEditRequest(request));
Button downloadButton = new Button("Descargar reporte", event -> downloadReport());
Button closeButton = new Button("Salir", event -> navigateToRequestsListView()); Button closeButton = new Button("Salir", event -> navigateToRequestsListView());
return new HorizontalLayout(viewButton, editButton, downloadButton, closeButton); return new HorizontalLayout(viewButton, editButton, closeButton);
} }
private Button createButton(final String caption, final Runnable action) { private Button createButton(final String caption, final Runnable action) {
@ -469,164 +409,18 @@ public class RequestEmployeeView extends Div implements HasUrlParameter<String>
return existingRequest.isEmpty(); return existingRequest.isEmpty();
} }
private String getDateString(final LocalDate date) {
return (date != null) ? date.toString() : "";
}
private ByteArrayInputStream generatePdfReport() {
try (PDDocument document = new PDDocument(); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
PDPage page = new PDPage();
document.addPage(page);
Employee employee = employeeService.getEmployee(employeeId);
PDPageContentStream contentStream = null;
try {
contentStream = new PDPageContentStream(document, page);
contentStream.setFont(PDType1Font.TIMES_BOLD, 18);
contentStream.beginText();
contentStream.newLineAtOffset(200, 750);
contentStream.showText("Reporte de Vacaciones");
contentStream.endText();
contentStream.setFont(PDType1Font.TIMES_ROMAN, 14);
contentStream.beginText();
contentStream.newLineAtOffset(50, 700);
contentStream.showText("Empleado: " + employee.getFirstName() + " " + employee.getLastName());
contentStream.newLineAtOffset(0, -15);
contentStream.showText("Equipo: " + employee.getTeam().getName());
contentStream.endText();
float tableTopY = 650;
float margin = 50;
float cellHeight = 20;
String[] headers = {"Categoría", "Estado", "Fecha de Inicio", "Fecha de Fin", "Días a Tomar"};
int columns = headers.length;
float[] columnWidths = new float[columns];
for (int i = 0; i < columns; i++) {
columnWidths[i] = getMaxColumnWidth(headers[i], requests, i);
}
contentStream.setFont(PDType1Font.TIMES_BOLD, 10);
float currentX = margin;
for (int i = 0; i < columns; i++) {
contentStream.addRect(currentX, tableTopY, columnWidths[i], -cellHeight);
contentStream.beginText();
contentStream.newLineAtOffset(currentX + 5, tableTopY - 15);
contentStream.showText(headers[i]);
contentStream.endText();
currentX += columnWidths[i];
}
contentStream.stroke();
contentStream.setFont(PDType1Font.TIMES_ROMAN, 10);
float currentY = tableTopY - cellHeight;
for (TimeOffRequest request : requests) {
String startDate = getDateString(request.getStartDate());
String endDate = getDateString(request.getEndDate());
String[] rowData = {
request.getCategory().name() != null ? request.getCategory().name() : "",
request.getState().name() != null ? request.getState().name() : "",
startDate,
endDate,
String.valueOf(request.getDaysToBeTake() != null ? request.getDaysToBeTake() : 0)
};
currentX = margin;
for (int i = 0; i < columns; i++) {
contentStream.addRect(currentX, currentY, columnWidths[i], -cellHeight);
contentStream.beginText();
contentStream.newLineAtOffset(currentX + 5, currentY - 15);
contentStream.showText(rowData[i]);
contentStream.endText();
currentX += columnWidths[i];
}
contentStream.stroke();
currentY -= cellHeight;
if (currentY < 50) {
contentStream.close();
page = new PDPage();
document.addPage(page);
contentStream = new PDPageContentStream(document, page);
currentY = 750;
}
}
} finally {
if (contentStream != null) {
contentStream.close();
}
}
document.save(out);
return new ByteArrayInputStream(out.toByteArray());
} catch (IOException e) {
throw new UncheckedIOException("Error al generar el reporte", e);
}
}
private float getMaxColumnWidth(final String header, final List<TimeOffRequest> requests, final int columnIndex) {
float maxWidth = header.length();
for (TimeOffRequest request : requests) {
String value = switch (columnIndex) {
case 0 -> request.getCategory().name();
case 1 -> request.getState().name();
case 2 -> getDateString(request.getStartDate());
case 3 -> getDateString(request.getEndDate());
case 4 -> String.valueOf(request.getDaysToBeTake());
default -> "";
};
if (value != null) {
maxWidth = Math.max(maxWidth, value.length());
}
}
return maxWidth * 7;
}
private StreamResource generateVacationReport() {
Employee employee = employeeService.getEmployee(employeeId);
String fileName = String.format("%s_%s-reporte_de_vacaciones_%s.pdf",
employee.getFirstName(),
employee.getLastName(),
LocalDate.now());
ByteArrayInputStream pdfStream = generatePdfReport();
return new StreamResource(fileName, () -> pdfStream)
.setContentType("application/pdf")
.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
}
private void downloadReport() {
StreamResource resource = generateVacationReport();
getUI().ifPresent(ui -> openDocumentStream(resource, ui));
}
private void openDocumentStream(final StreamResource resource, final UI ui) {
StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource);
ui.getPage().open(registration.getResourceUri().toString());
}
@Override @Override
public void setParameter(final BeforeEvent event, final String parameter) { public void setParameter(final BeforeEvent event, final String parameter) {
employeeId = UUID.fromString(parameter); employeeId = UUID.fromString(parameter);
Employee employee = employeeService.getEmployee(employeeId); Employee employee = employeeService.getEmployee(employeeId);
requests = requestService.findRequestsByEmployeeId(employeeId); requests = requestService.findRequestsByEmployeeId(employeeId);
setViewTitle( setViewTitle(employee.getFirstName() + " " + employee.getLastName(), employee.getTeam().getName());
employee.getFirstName() + " " + employee.getLastName(),
employee.getTeam().getName(),
employee.getDateOfExit()
);
requestGrid.setItems(requests); requestGrid.setItems(requests);
initializeView(); initializeView();
} }
private void setViewTitle(final String employeeName, final String employeeTeam, final LocalDate dateOfExit) { private void setViewTitle(final String employeeName, final String employeeTeam) {
addComponentAsFirst(new H3("Nombre del empleado: " + employeeName)); addComponentAsFirst(new H3("Nombre del empleado: " + employeeName));
addComponentAtIndex(1, new H3("Equipo: " + employeeTeam)); addComponentAtIndex(1, new H3("Equipo: " + employeeTeam));
if (dateOfExit != null) {
addComponentAtIndex(2, new H3("Descontado a cero en fecha " + dateOfExit + " por pago de finiquito."));
}
} }
} }

View File

@ -19,7 +19,6 @@ import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.DayOfWeek;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
@ -92,25 +91,8 @@ public class RequestRegisterView extends VerticalLayout {
onCategoryChange(event.getValue()); onCategoryChange(event.getValue());
handleCategorySelection(event.getValue()); handleCategorySelection(event.getValue());
}); });
startDatePicker.addValueChangeListener(event -> { startDatePicker.addValueChangeListener(event -> updateDatePickerMinValues());
LocalDate selectedDate = event.getValue(); endDatePicker.addValueChangeListener(event -> calculateDays());
if (selectedDate != null && (selectedDate.getDayOfWeek().getValue() == 6
|| selectedDate.getDayOfWeek().getValue() == 7)) {
startDatePicker.setValue(selectedDate.minusDays(1));
}
updateDatePickerMinValues();
});
endDatePicker.addValueChangeListener(event -> {
if (startDatePicker.getValue() != null) {
endDatePicker.setMin(startDatePicker.getValue());
}
LocalDate selectedDate = event.getValue();
if (selectedDate != null && (selectedDate.getDayOfWeek().getValue() == 6
|| selectedDate.getDayOfWeek().getValue() == 7)) {
endDatePicker.setValue(selectedDate.minusDays(1));
}
calculateDays();
});
} }
private void configureBinder() { private void configureBinder() {
@ -185,7 +167,7 @@ public class RequestRegisterView extends VerticalLayout {
private void onCategoryChange(final TimeOffRequestType selectedCategory) { private void onCategoryChange(final TimeOffRequestType selectedCategory) {
if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ACTUAL if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ACTUAL
|| selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) { || selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) {
startDatePicker.setEnabled(true); startDatePicker.setEnabled(true);
endDatePicker.setEnabled(true); endDatePicker.setEnabled(true);
} else { } else {
@ -196,10 +178,6 @@ public class RequestRegisterView extends VerticalLayout {
private boolean isCategoryAvailable(final List<TimeOffRequest> employeeRequests, private boolean isCategoryAvailable(final List<TimeOffRequest> employeeRequests,
final TimeOffRequestType category) { final TimeOffRequestType category) {
if (category == TimeOffRequestType.CUMPLEAÑOS && employee.getBirthday() == null) {
return false;
}
List<TimeOffRequest> requestsByCategory = employeeRequests.stream() List<TimeOffRequest> requestsByCategory = employeeRequests.stream()
.filter(request -> request.getCategory() == category) .filter(request -> request.getCategory() == category)
.toList(); .toList();
@ -221,8 +199,8 @@ public class RequestRegisterView extends VerticalLayout {
&& latestRequest.getDaysBalance() > 0) && latestRequest.getDaysBalance() > 0)
|| latestRequest.getState() == TimeOffRequestStatus.RECHAZADO || latestRequest.getState() == TimeOffRequestStatus.RECHAZADO
|| (latestRequest.getState() == TimeOffRequestStatus.TOMADO || (latestRequest.getState() == TimeOffRequestStatus.TOMADO
&& latestRequest.getDaysBalance() == 0 && latestRequest.getDaysBalance() == 0
&& latestRequest.getExpiration().isBefore(LocalDate.now())); && latestRequest.getExpiration().isBefore(LocalDate.now()));
} else { } else {
return (latestRequest.getState() == TimeOffRequestStatus.TOMADO return (latestRequest.getState() == TimeOffRequestStatus.TOMADO
&& latestRequest.getExpiration().isBefore(LocalDate.now())) && latestRequest.getExpiration().isBefore(LocalDate.now()))
@ -397,13 +375,6 @@ public class RequestRegisterView extends VerticalLayout {
Double availableDays = availableDaysField.getValue(); Double availableDays = availableDaysField.getValue();
if (areDatesValid(startDate, endDate)) { if (areDatesValid(startDate, endDate)) {
long workDays = countWorkDaysBetween(startDate, endDate);
daysToBeTakenField.setValue((double) workDays);
balanceDaysField.setValue(availableDaysField.getValue() - workDays);
double daysToBeTaken = calculateDaysBetween(startDate, endDate); double daysToBeTaken = calculateDaysBetween(startDate, endDate);
setDaysToBeTakenField(daysToBeTaken); setDaysToBeTakenField(daysToBeTaken);
@ -413,12 +384,6 @@ public class RequestRegisterView extends VerticalLayout {
if (balanceDays < 0.0) { if (balanceDays < 0.0) {
clearFields(); clearFields();
} }
if (daysToBeTakenField.getValue() > 10
&& (categoryComboBox.getValue() == TimeOffRequestType.VACACION_GESTION_ANTERIOR
|| categoryComboBox.getValue() == TimeOffRequestType.VACACION_GESTION_ACTUAL)) {
clearFields();
}
} }
} }
@ -426,19 +391,8 @@ public class RequestRegisterView extends VerticalLayout {
return startDate != null && endDate != null; return startDate != null && endDate != null;
} }
private long countWorkDaysBetween(final LocalDate startDate, final LocalDate endDate) {
return startDate.datesUntil(endDate.plusDays(1))
.filter(date -> date.getDayOfWeek() != DayOfWeek.SATURDAY && date.getDayOfWeek() != DayOfWeek.SUNDAY)
.count();
}
private double calculateDaysBetween(final LocalDate startDate, final LocalDate endDate) { private double calculateDaysBetween(final LocalDate startDate, final LocalDate endDate) {
return startDate.datesUntil(endDate.plusDays(1)) return java.time.temporal.ChronoUnit.DAYS.between(startDate, endDate) + 1;
.filter(date -> {
DayOfWeek day = date.getDayOfWeek();
return day != DayOfWeek.SATURDAY && day != DayOfWeek.SUNDAY;
})
.count();
} }
private void setDaysToBeTakenField(final double daysToBeTaken) { private void setDaysToBeTakenField(final double daysToBeTaken) {
@ -502,16 +456,9 @@ public class RequestRegisterView extends VerticalLayout {
handleExistingRequests(request); handleExistingRequests(request);
} }
long differentDays = ChronoUnit.DAYS.between(LocalDate.now(), request.getStartDate()); requestService.saveTimeOffRequest(request);
if (differentDays >= -15 && differentDays <= 90) { Notification.show("Solicitud guardada correctamente.");
requestService.saveTimeOffRequest(request); closeForm();
Notification.show("Solicitud guardada correctamente.");
closeForm();
} else {
Notification.show(
"La fecha de inicio debe encontrarse dentro del rango de 15 días a 3 meses de anticipación."
);
}
} }
private TimeOffRequest prepareRequest() { private TimeOffRequest prepareRequest() {

View File

@ -5,39 +5,29 @@ import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.TeamService; import com.primefactorsolutions.service.TeamService;
import com.primefactorsolutions.service.TimeOffRequestService; import com.primefactorsolutions.service.TimeOffRequestService;
import com.primefactorsolutions.service.VacationService; import com.primefactorsolutions.service.VacationService;
import com.vaadin.flow.component.UI;
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.html.Main;
import com.vaadin.flow.component.notification.Notification; 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.router.PageTitle; import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.StreamRegistration;
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.spring.annotation.SpringComponent; import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll; import jakarta.annotation.security.PermitAll;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.grid.PagingGrid; import org.vaadin.firitin.components.grid.PagingGrid;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.Period; import java.time.Period;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.primefactorsolutions.views.Constants.PAGE_SIZE;
@SpringComponent @SpringComponent
@Scope("prototype") @Scope("prototype")
@PageTitle("Requests") @PageTitle("Requests")
@Route(value = "/requests", layout = MainLayout.class) @Route(value = "/requests", layout = MainLayout.class)
@PermitAll @PermitAll
public class RequestsListView extends BaseView { public class RequestsListView extends Main {
private final TimeOffRequestService requestService; private final TimeOffRequestService requestService;
private final EmployeeService employeeService; private final EmployeeService employeeService;
@ -45,8 +35,10 @@ public class RequestsListView extends BaseView {
private final VacationService vacationService; private final VacationService vacationService;
private final PagingGrid<Employee> requestGrid = new PagingGrid<>(); private final PagingGrid<Employee> requestGrid = new PagingGrid<>();
private List<Employee> employees = Collections.emptyList();
private ComboBox<Employee> employeeFilter; private ComboBox<Employee> employeeFilter;
private ComboBox<Team> teamFilter; private ComboBox<Team> teamFilter;
private ComboBox<TimeOffRequestType> categoryFilter;
private ComboBox<Status> stateFilter; private ComboBox<Status> stateFilter;
private UUID selectedEmployeeId; private UUID selectedEmployeeId;
@ -59,6 +51,7 @@ public class RequestsListView extends BaseView {
this.employeeService = employeeService; this.employeeService = employeeService;
this.teamService = teamService; this.teamService = teamService;
this.vacationService = vacationService; this.vacationService = vacationService;
this.employees = employeeService.findAllEmployees();
initializeView(); initializeView();
refreshGeneralRequestGrid(null, null, null); refreshGeneralRequestGrid(null, null, null);
} }
@ -67,16 +60,14 @@ public class RequestsListView extends BaseView {
requestService.updateRequestStatuses(); requestService.updateRequestStatuses();
setupFilters(); setupFilters();
setupRequestGrid(); setupRequestGrid();
getCurrentPageLayout().add(requestGrid); add(requestGrid);
getCurrentPageLayout().add(createActionButtons()); add(createActionButtons());
} }
private void setupFilters() { private void setupFilters() {
final HorizontalLayout hl = new HorizontalLayout(); add(createEmployeeFilter());
hl.add(createEmployeeFilter()); add(createTeamFilter());
hl.add(createTeamFilter()); add(createStateFilter());
hl.add(createStateFilter());
getCurrentPageLayout().add(hl);
} }
private void setupRequestGrid() { private void setupRequestGrid() {
@ -86,7 +77,7 @@ public class RequestsListView extends BaseView {
requestGrid.addColumn(this::getGeneralTotal).setHeader("Total general"); requestGrid.addColumn(this::getGeneralTotal).setHeader("Total general");
requestGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM); requestGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
requestGrid.setPageSize(PAGE_SIZE); requestGrid.setPageSize(5);
requestGrid.asSingleSelect().addValueChangeListener(event -> { requestGrid.asSingleSelect().addValueChangeListener(event -> {
Employee selectedRequest = event.getValue(); Employee selectedRequest = event.getValue();
if (selectedRequest != null) { if (selectedRequest != null) {
@ -103,9 +94,8 @@ public class RequestsListView extends BaseView {
Notification.show("Seleccione una solicitud.", 3000, Notification.Position.MIDDLE); Notification.show("Seleccione una solicitud.", 3000, Notification.Position.MIDDLE);
} }
}); });
Button downloadButton = new Button("Descargar reporte", event -> downloadReport());
Button closeButton = new Button("Salir", event -> navigateToMainView()); Button closeButton = new Button("Salir", event -> navigateToMainView());
return new HorizontalLayout(viewButton, downloadButton, closeButton); return new HorizontalLayout(viewButton, closeButton);
} }
private void refreshGeneralRequestGrid(final Employee employee, private void refreshGeneralRequestGrid(final Employee employee,
@ -168,7 +158,7 @@ public class RequestsListView extends BaseView {
private String getEmployeeStatus(final Employee employee) { private String getEmployeeStatus(final Employee employee) {
Optional<TimeOffRequest> activeRequest = requestService Optional<TimeOffRequest> activeRequest = requestService
.findByEmployeeAndState(employee.getId(), TimeOffRequestStatus.EN_USO); .findByEmployeeAndState(employee.getId(), TimeOffRequestStatus.EN_USO);
return activeRequest.isPresent() ? "EN_DESCANSO" : "EN_FUNCIONES"; return activeRequest.isPresent() ? "EN_DESCANSO" : "ACTIVO";
} }
private String getGeneralTotal(final Employee employee) { private String getGeneralTotal(final Employee employee) {
@ -200,13 +190,6 @@ public class RequestsListView extends BaseView {
double totalAvailable = calculateTotalAvailable(vacations, employeeRequests, employee); double totalAvailable = calculateTotalAvailable(vacations, employeeRequests, employee);
double generalTotal = totalAvailable + totalVacations - totalUtilized; double generalTotal = totalAvailable + totalVacations - totalUtilized;
if (employee.getDateOfExit() != null
&& (employee.getDateOfExit().isBefore(LocalDate.now())
|| employee.getDateOfExit().isEqual(LocalDate.now()))) {
generalTotal = 0;
}
return String.valueOf(generalTotal); return String.valueOf(generalTotal);
} }
@ -233,8 +216,6 @@ public class RequestsListView extends BaseView {
int currentYear = LocalDate.now().getYear(); int currentYear = LocalDate.now().getYear();
return employeeRequests.stream() return employeeRequests.stream()
.filter(Objects::nonNull) .filter(Objects::nonNull)
.filter(request -> request.getState() == TimeOffRequestStatus.APROBADO
|| request.getState() == TimeOffRequestStatus.TOMADO)
.filter(request -> request.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD) .filter(request -> request.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD)
.filter(request -> request.getCategory() != TimeOffRequestType.VACACION_GESTION_ACTUAL) .filter(request -> request.getCategory() != TimeOffRequestType.VACACION_GESTION_ACTUAL)
.filter(request -> request.getCategory() != TimeOffRequestType.VACACION_GESTION_ANTERIOR) .filter(request -> request.getCategory() != TimeOffRequestType.VACACION_GESTION_ANTERIOR)
@ -347,8 +328,10 @@ public class RequestsListView extends BaseView {
&& !employeeRequestCategories.contains(vacation.getCategory())) { && !employeeRequestCategories.contains(vacation.getCategory())) {
return false; return false;
} }
if (!isFemale(employee) && genderSpecificExclusions.contains(vacation.getCategory())) {
return isFemale(employee) || !genderSpecificExclusions.contains(vacation.getCategory()); return false;
}
return true;
} }
private boolean isFemale(final Employee employee) { private boolean isFemale(final Employee employee) {
@ -406,7 +389,7 @@ public class RequestsListView extends BaseView {
private enum Status { private enum Status {
TODOS, TODOS,
EN_DESCANSO, EN_DESCANSO,
EN_FUNCIONES ACTIVO
} }
private Employee createAllEmployeesOption() { private Employee createAllEmployeesOption() {
@ -428,50 +411,4 @@ public class RequestsListView extends BaseView {
private void navigateToTimeOffRequestView(final UUID idEmployee) { private void navigateToTimeOffRequestView(final UUID idEmployee) {
getUI().ifPresent(ui -> ui.navigate("requests/" + idEmployee.toString())); getUI().ifPresent(ui -> ui.navigate("requests/" + idEmployee.toString()));
} }
private ByteArrayInputStream generateExcelReport(final List<Employee> employees) {
try (Workbook workbook = new XSSFWorkbook()) {
Sheet sheet = workbook.createSheet("REPORTE_GENERAL_DE_VACACIONES");
Row headerRow = sheet.createRow(0);
String[] headers = {"Empleado", "Equipo", "Estado", "Total Horas"};
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
}
int rowIndex = 1;
for (Employee employee : employees) {
Row row = sheet.createRow(rowIndex++);
row.createCell(0).setCellValue(getEmployeeFullName(employee));
row.createCell(1).setCellValue(getTeamName(employee));
row.createCell(2).setCellValue(getEmployeeStatus(employee));
row.createCell(3).setCellValue(getGeneralTotal(employee));
}
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
workbook.write(out);
return new ByteArrayInputStream(out.toByteArray());
}
} catch (IOException e) {
throw new UncheckedIOException("Error al generar el archivo Excel", e);
}
}
private StreamResource generateGeneralVacationReport() {
List<Employee> employees = employeeService.findAllEmployees();
ByteArrayInputStream excelStream = generateExcelReport(employees);
return new StreamResource("reporte_general_de_vacaciones_" + LocalDate.now() + ".xlsx",
() -> excelStream);
}
private void downloadReport() {
StreamResource resource = generateGeneralVacationReport();
getUI().ifPresent(ui -> openDocumentStream(resource, ui));
}
private void openDocumentStream(final StreamResource resource, final UI ui) {
StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource);
ui.getPage().open(registration.getResourceUri().toString());
}
} }

View File

@ -90,6 +90,3 @@ insert into time_off_request (id, version, employee_id, category, state, availab
values ('12ec8b74-983d-4a17-b67e-134f45ae904c', 1, '5c1a7b82-832d-4f24-8377-54b77b91b6a8', 'AÑO_NUEVO', 'SOLICITADO', 1, '2025-01-01', '2025-01-01', '2025-01-01', 1, 0); values ('12ec8b74-983d-4a17-b67e-134f45ae904c', 1, '5c1a7b82-832d-4f24-8377-54b77b91b6a8', 'AÑO_NUEVO', 'SOLICITADO', 1, '2025-01-01', '2025-01-01', '2025-01-01', 1, 0);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance) insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('89bc4b2a-943f-487c-a9f3-bacf78145e67', 1, 'cba3efb7-32bc-44be-9fdc-fc5e4f211254', 'LUNES_CARNAVAL', 'APROBADO', 1, '2024-02-12', '2024-02-12', '2024-02-12', 1, 0); values ('89bc4b2a-943f-487c-a9f3-bacf78145e67', 1, 'cba3efb7-32bc-44be-9fdc-fc5e4f211254', 'LUNES_CARNAVAL', 'APROBADO', 1, '2024-02-12', '2024-02-12', '2024-02-12', 1, 0);
INSERT INTO HOURS_WORKED (ID, VERSION, ACTIVIDAD, DATE, HORAS_TAREAS_ESPECIFICAS, HORASPENDIENTES, HOURS, TAREAS_ESPECIFICAS, TOTAL_HOURS, WEEK_NUMBER, EMPLOYEE_ID, TEAM_ID)
VALUES ('6d6b3a71-9b11-4526-8335-b089627a8cd6', 0, 'Scrum Meeting', '2024-11-15', 0.0, 0.0, 2.0, NULL, 8, 46, '5c6f11fe-c341-4be7-a9a6-bba0081ad7c6', 'b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa');