#45-Registro Semanal de Horas Trabajadas corregir duplicados #70

Closed
melina.gutierrez wants to merge 12 commits from hoursworked into main
9 changed files with 321 additions and 535 deletions
Showing only changes of commit aa1f041a4f - Show all commits

View File

@ -0,0 +1,81 @@
package com.primefactorsolutions.model;
import jakarta.persistence.*;
import java.time.LocalDate;
import java.util.UUID;
@Entity
@Table(name = "Actividades_hours")
public class ActividadesHours {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
@ManyToOne
@JoinColumn(name = "employee_id", nullable = false)
private Employee employee;
@ManyToOne
@JoinColumn(name = "actividad_id", nullable = false)
private Actividad actividad;
@Column(nullable = false)
private double totalHours;
@Column(nullable = false)
private int weekNumber;
@Column(nullable = false)
private LocalDate fecha;
// Getters y Setters
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
public Actividad getActividad() {
return actividad;
}
public void setActividad(Actividad actividad) {
this.actividad = actividad;
}
public double getTotalHours() {
return totalHours;
}
public void setTotalHours(double totalHours) {
this.totalHours = totalHours;
}
public int getWeekNumber() {
return weekNumber;
}
public void setWeekNumber(int weekNumber) {
this.weekNumber = weekNumber;
}
public LocalDate getFecha() {
return fecha;
}
public void setFecha(LocalDate fecha) {
this.fecha = fecha;
}
}

View File

@ -28,36 +28,40 @@ 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") @Pattern(regexp = "^[a-zA-Z ,]+$", message = "La ciudad de nacimiento solo debe contener letras, espacios o comas")
private String birthCity; private String birthCity;
private String age; private String age;
@Size(max = 100, message = "La dirección de residencia no debe exceder 100 caracteres") @Size(max = 50, message = "La dirección de residencia no debe exceder 50 caracteres")
private String residenceAddress; private String residenceAddress;
@Size(max = 100, message = "La dirección local no debe exceder 100 caracteres") @Size(max = 30, message = "La dirección local no debe exceder 100 caracteres")
@Pattern(regexp = "^[a-zA-Z -]+$", message = "La dirección local solo debe contener letras y guion")
private String localAddress; 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 debe contener solo números") @Pattern(regexp = "^[0-9]+$", message = "El teléfono de contacto de emergencia 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;
@ -70,9 +74,7 @@ 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,68 +1,43 @@
package com.primefactorsolutions.model; package com.primefactorsolutions.model;
import jakarta.persistence.Entity; import jakarta.persistence.*;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.CascadeType;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.temporal.WeekFields;
import java.util.Locale;
import java.util.UUID; import java.util.UUID;
@Data @Data
@Entity @Entity
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class HoursWorked extends BaseEntity { public class HoursWorked extends BaseEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.UUID) @GeneratedValue(strategy = GenerationType.AUTO)
private UUID id; private UUID id;
@ManyToOne @ManyToOne
@JoinColumn(name = "employee_id", nullable = false)
private Employee employee; private Employee employee;
@ManyToOne(cascade = CascadeType.PERSIST) // Añadimos cascade para que persista la actividad automáticamente @ManyToOne
private Actividad actividad; @JoinColumn(name = "team_id", nullable = false)
private Team team;
private int weekNumber; private int weekNumber;
private LocalDate date;
private String actividad;
private double hours;
private double totalHours; private double totalHours;
private LocalDate fecha;
public HoursWorked() {}
public HoursWorked(Employee employee) {
this.employee = employee;
this.weekNumber = 0;
this.totalHours = 0;
this.fecha = LocalDate.now();
}
public Actividad getActividad() {
return this.actividad;
}
public void setActividad(Actividad actividad){
this.actividad = actividad;
}
public UUID getId() {
return id;
}
public void setId(final UUID id) {
this.id = id;
}
public Employee getEmployee() { public Employee getEmployee() {
return employee; return employee;
} }
public void setEmployee(final Employee employee) { public void setEmployee(Employee employee) {
this.employee = employee; this.employee = employee;
} }
@ -70,45 +45,48 @@ public class HoursWorked extends BaseEntity {
return weekNumber; return weekNumber;
} }
public void setWeekNumber(final int weekNumber) { public void setWeekNumber(int weekNumber) {
this.weekNumber = weekNumber; this.weekNumber = weekNumber;
} }
public LocalDate getDate() {
return date;
}
public void setDate(LocalDate date) {
this.date = date;
}
public String getActividad() {
return actividad;
}
public void setActividad(String actividad) {
this.actividad = actividad;
}
public double getHours() {
return hours;
}
public void setHours(double hours) {
this.hours = hours;
}
public double getTotalHours() { public double getTotalHours() {
return totalHours; return totalHours;
} }
public void setTotalHours(final double totalHours) { public void setTotalHours(double totalHours) {
this.totalHours = totalHours; this.totalHours = totalHours;
} }
public LocalDate getFecha() { public Team getTeam() {
return fecha; return team;
} }
public void setFecha(final LocalDate fecha) { public void setTeam(Team team) {
this.fecha = fecha; this.team = team;
// Actualiza el número de semana automáticamente al establecer la fecha
if (fecha != null) {
this.weekNumber = calculateWeekNumber(fecha);
} }
} }
// Método adicional para calcular el número de semana basado en la fecha
private int calculateWeekNumber(LocalDate date) {
WeekFields weekFields = WeekFields.of(Locale.getDefault());
return date.get(weekFields.weekOfWeekBasedYear());
}
@Override
public String toString() {
return "HoursWorked{" +
"id=" + id +
", employee=" + (employee != null ? employee.getFirstName() : "N/A") +
", actividad=" + (actividad != null ? actividad.getNombre() : "N/A") +
", weekNumber=" + weekNumber +
", totalHours=" + totalHours +
", fecha=" + fecha +
'}';
}
}

View File

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

View File

@ -2,11 +2,13 @@ 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;
import java.util.List; import java.util.List;
import java.util.UUID;
@Repository
public interface HoursWorkedRepository extends JpaRepository<HoursWorked, Long> { public interface HoursWorkedRepository extends JpaRepository<HoursWorked, UUID> {
List<HoursWorked> findByWeekNumber(int weekNumber); List<HoursWorked> findByWeekNumber(int weekNumber);
List<HoursWorked> findByDate(LocalDate date);
} }

View File

@ -5,7 +5,9 @@ import com.primefactorsolutions.repositories.HoursWorkedRepository;
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.util.List; import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
@Service @Service
public class HoursWorkedService { public class HoursWorkedService {
@ -20,6 +22,11 @@ public class HoursWorkedService {
return hoursWorkedRepository.findAll(); return hoursWorkedRepository.findAll();
} }
public HoursWorked findHoursWorked(final UUID id) {
Optional<HoursWorked> hoursWorked = hoursWorkedRepository.findById(id);
return hoursWorked.orElse(null);
}
public HoursWorked saveHoursWorked(final HoursWorked hoursWorked) { public HoursWorked saveHoursWorked(final HoursWorked hoursWorked) {
return hoursWorkedRepository.save(hoursWorked); return hoursWorkedRepository.save(hoursWorked);
} }
@ -28,13 +35,17 @@ public class HoursWorkedService {
return hoursWorkedRepository.save(hoursWorked); return hoursWorkedRepository.save(hoursWorked);
} }
public void deleteHoursWorked(final Long id) {
hoursWorkedRepository.deleteById(id);
}
public List<HoursWorked> findByWeekNumber(final int weekNumber) { public List<HoursWorked> findByWeekNumber(final int weekNumber) {
return hoursWorkedRepository.findByWeekNumber(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);
}
} }

View File

@ -56,7 +56,6 @@ 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);
@ -65,24 +64,19 @@ 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", 20, false); private final TextField birthCity = createTextField("Ciudad y País de Nacimiento ejemplo: (Ciudad, País) ", 30, false);
private final TextField residenceAddress = createTextField("Dirección de Residencia", 50, false); private final TextField residenceAddress = createTextField("Dirección de Residencia", 50, false);
private final TextField localAddress = createTextField("Dep/Provincia de Residencia", 10, false); private final TextField localAddress = createTextField("Departamento y Provincia de Residencia ejemplo: (Departamento-Provincia)", 30, false);
private final ComboBox<Employee.MaritalStatus> maritalStatus = createMaritalStatusComboBox(); private final ComboBox<Employee.MaritalStatus> maritalStatus = createMaritalStatusComboBox();
private final TextField numberOfChildren = createTextField("Numero de Hijos", 3, false); private final TextField numberOfChildren = createTextField("Numero de Hijos", 2, false);
private final TextField ci = createTextField("CI", 30, false); private final TextField ci = createTextField("CI", 10, false);
private final TextField issuedIn = createTextField("Expedido en ", 30, false); private final TextField issuedIn = createTextField("Expedido en ", 10, 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"); private final EmailField personalEmail = createEmailField("E-mail ejemplo: (ejemplo@gmail.com)");
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"); private final EmailField emergencyCEmail = createEmailField("Email de Contacto ejemplo: (ejemplo@gmail.com)");
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);
@ -101,10 +95,15 @@ 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", 30, false); private final TextField language = createTextField("Idioma", 50, false);
private final TextField languageLevel = createTextField("Nivel de Idioma", 30, false); private final TextField languageLevel = createTextField("Nivel de Idioma", 30, false);
//INFORMACION DE CONTRATACION //INFORMACION ADMINISTRATIVA
private final TextField cod = createTextField("Codigo de Empleado", 20, false);
private final TextField position = createTextField("Cargo", 30, false);
private final ComboBox<Team> team = new ComboBox<>("Equipo");
private final TextField leadManager = createTextField("Lead/Manager", 30, false);
private final TextField project = createTextField("Proyecto", 30, false);
private final VDatePicker dateOfEntry = new VDatePicker("Fecha de Ingreso"); private final VDatePicker 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);
@ -141,7 +140,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 Bancados"); private final H3 datBanc = new H3("Datos Bancarios");
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,6 +176,8 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
reportButton.setVisible(true); reportButton.setVisible(true);
birthday.addValueChangeListener(event -> calculateAge()); birthday.addValueChangeListener(event -> calculateAge());
birthday.setMax(java.time.LocalDate.now()); 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();
@ -204,10 +205,30 @@ 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);
@ -219,11 +240,15 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
getEntity().setProfileImage(base64Image); getEntity().setProfileImage(base64Image);
profileImagePreview.setSrc("data:image/jpeg;base64," + base64Image); profileImagePreview.setSrc("data:image/png;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."); Notification.show("Error al subir la imagen: " + e.getMessage());
e.printStackTrace();
} catch (Exception e) {
Notification.show("Error en el servidor al procesar la imagen.");
e.printStackTrace();
} }
}); });
} }
@ -394,12 +419,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("150px"); profileImagePreview.setMaxWidth("250px");
profileImagePreview.setMaxHeight("150px"); profileImagePreview.setMaxHeight("250px");
upload.setVisible(false); upload.setVisible(true);
} else { } else {
profileImagePreview.setVisible(false); profileImagePreview.setVisible(true);
upload.setVisible(true); upload.setVisible(true);
} }
} }
@ -479,6 +504,7 @@ 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);
@ -526,7 +552,6 @@ 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,
@ -534,6 +559,7 @@ 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

@ -1,477 +1,156 @@
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.vaadin.flow.component.grid.Grid; import com.primefactorsolutions.service.TeamService;
import com.vaadin.flow.component.html.H2; import com.primefactorsolutions.service.TimeOffRequestService;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.datepicker.DatePicker;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.notification.Notification; import com.vaadin.flow.component.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.component.orderedlayout.VerticalLayout; import com.vaadin.flow.router.*;
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 com.vaadin.flow.component.html.Label; import org.vaadin.firitin.form.BeanValidationForm;
import org.vaadin.firitin.components.datepicker.VDatePicker;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.IsoFields;
import java.time.temporal.WeekFields;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.UUID;
@SpringComponent @SpringComponent
@PermitAll @PermitAll
@Scope("prototype") @Scope("prototype")
@PageTitle("Horas Trabajadas") @PageTitle("Horas Trabajadas")
@Route(value = "/hours-worked/me", layout = MainLayout.class) @Route(value = "/timesheets/:hours-worked?/:action?", layout = MainLayout.class)
public class HoursWorkedView extends VerticalLayout { public class HoursWorkedView extends BeanValidationForm<HoursWorked> implements HasUrlParameter<String> {
private final List<Actividad> actividades = new ArrayList<>(); private final DatePicker dateField = new DatePicker("Fecha");
private final List<Actividad> actividadesEspecificas = new ArrayList<>(); // Nueva lista para tareas específicas private final ComboBox<Team> teamField = new ComboBox<>("Equipo");
private final Grid<Actividad> grid = new Grid<>(Actividad.class); private final ComboBox<Employee> employeeField = new ComboBox<>("Empleado");
private final Grid<Actividad> gridActividadesEspecificas = new Grid<>(Actividad.class); private final TextField activityField = new TextField("Actividad");
private final ComboBox<Employee> employeeComboBox = new ComboBox<>("Empleado");
private final ComboBox<String> equipoDropdown = new ComboBox<>("Equipo");
private VDatePicker fechaPicker = new VDatePicker("Selecciona una fecha");
private final ComboBox<String> tareasEspecificasDropdown = new ComboBox<>("Tareas Específicas");
private final TextField tareaEspecificaInput = new TextField("Especificar Tarea");
private TextField horasTareaInput = crearCampoHora("Horas Tareas");
private LocalDate selectedStartOfWeek;
private int weekNumber;
@Autowired
private final EmployeeService employeeService;
@Autowired
private final HoursWorkedService hoursWorkedService; private final HoursWorkedService hoursWorkedService;
private final EmployeeService employeeService;
private final TeamService teamService;
private HoursWorked request;
private Employee employee;
private final H2 equipoLabel = new H2("Tareas del Cliente/Equipo"); private Button saveButton;
private final H2 empresaLabel = new H2("Tareas de la Empresa");
private final Label totalCompletadoLabel = new Label();
private final Label horasPendientesLabel = new Label();
public HoursWorkedView(final EmployeeService employeeService, final HoursWorkedService hoursWorkedService) { public HoursWorkedView(HoursWorkedService hoursWorkedService,
this.employeeService = employeeService; EmployeeService employeeService,
TeamService teamService, TimeOffRequestService timeOffRequestService) {
super(HoursWorked.class);
this.hoursWorkedService = hoursWorkedService; this.hoursWorkedService = hoursWorkedService;
configurarVista(); this.employeeService = employeeService;
configurarGrid(); this.teamService = teamService;
configurarGridActividadesEspecificas();
cargarDatos(); // Initialize combo boxes
configurarTareasEspecificas(); initializeTeamField();
initializeEmployeeField();
} }
private void configurarTareasEspecificas() { @Override
tareasEspecificasDropdown.setItems("Entrevistas", "Reuniones", "Colaboraciones", public void setParameter(BeforeEvent beforeEvent, String action) {
"Aprendizajes", "Proyectos PFS", "Otros"); final RouteParameters params = beforeEvent.getRouteParameters();
tareasEspecificasDropdown.setPlaceholder("Selecciona una tarea..."); final String hoursWorkedId = params.get("hoursworkedId").orElse(null);
tareasEspecificasDropdown.addValueChangeListener(event -> { if ("new".equals(action)) {
String selected = event.getValue(); setEntityWithEnabledSave(new HoursWorked());
// Activa el campo de texto si la opción seleccionada es "Otros" } else if (hoursWorkedId != null) {
tareaEspecificaInput.setVisible("Otros".equals(selected)); UUID id = UUID.fromString(hoursWorkedId);
if (!"Otros".equals(selected)) { request = hoursWorkedService.findHoursWorked(id);
tareaEspecificaInput.clear(); setEntity(request);
} configureViewOrEditAction(action);
});
tareaEspecificaInput.setVisible(false);
}
private void cargarDatos() {
if (selectedStartOfWeek != null && weekNumber > 0) {
List<HoursWorked> listaDeHorasTrabajadas = obtenerDatos();
for (HoursWorked hours : listaDeHorasTrabajadas) {
LocalDate activityDate = hours.getFecha();
int activityWeek = getWeekOfYear(activityDate);
if (activityWeek == weekNumber) {
Actividad actividad = hours.getActividad();
if (actividad != null) {
DayOfWeek dayOfWeek = activityDate.getDayOfWeek();
agregarOActualizarActividad(actividad, dayOfWeek, hours.getTotalHours());
}
} }
} }
grid.setItems(actividades); @Override
gridActividadesEspecificas.setItems(actividadesEspecificas); protected List<Component> getFormComponents() {
calcularTotalHoras(listaDeHorasTrabajadas); return List.of(
} createEmployeeHeader(),
} dateField,
teamField,
private void setEmployeeComboBoxProperties() { employeeField,
employeeComboBox.setWidth("250px"); activityField,
employeeComboBox.setPlaceholder("Buscar empleado..."); createCloseButton()
employeeComboBox.setItems(employeeService.findAllEmployees());
employeeComboBox.setItemLabelGenerator(employee -> employee.getFirstName() + " " + employee.getLastName());
employeeComboBox.setAllowCustomValue(false);
employeeComboBox.addCustomValueSetListener(event ->
Notification.show("Selecciona un empleado válido de la lista.")
); );
employeeComboBox.addValueChangeListener(event -> {
Employee selectedEmployee = event.getValue();
if (selectedEmployee != null) {
Notification.show("Empleado seleccionado: "
+ selectedEmployee.getFirstName() + " "
+ selectedEmployee.getLastName());
} }
protected Button createSaveButton() {
saveButton = new Button("Guardar");
saveButton.addClickListener(event -> saveHoursWorked());
return saveButton;
}
protected Button createCloseButton() {
Button closeButton = new Button("Cerrar");
closeButton.addClickListener(event -> closeForm());
return closeButton;
}
private void initializeTeamField() {
teamField.setItems(teamService.findAllTeams());
teamField.setItemLabelGenerator(Team::getName);
teamField.addValueChangeListener(event -> {
teamField.getValue();
}); });
} }
private int getWeekOfYear(final LocalDate date) { private void initializeEmployeeField() {
return date.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR); employeeField.setItems(employeeService.findAllEmployees());
employeeField.setItemLabelGenerator(employee -> employee.getFirstName() + " " + employee.getLastName());
} }
private void configurarVista() { private void saveHoursWorked() {
fechaPicker.setMax(LocalDate.now()); if (isFormValid()) {
fechaPicker.addValueChangeListener(event -> { HoursWorked hoursWorked = getEntity();
LocalDate selectedDate = event.getValue(); setFieldValues(hoursWorked);
if (selectedDate != null) {
selectedStartOfWeek = getStartOfWeek(selectedDate);
weekNumber = getWeekOfYear(selectedDate);
cargarDatos();
}
});
Button verMesButton = new Button("Ver Mes", event -> {
getUI().ifPresent(ui -> ui.navigate(HoursWorkedMonthView.class));
});
equipoDropdown.setItems("ABC", "DEF", "XYZ");
equipoDropdown.setWidth("250px");
equipoDropdown.addValueChangeListener(event -> {
String selectedEquipo = event.getValue();
if (selectedEquipo != null) {
// Filtra la lista de empleados según el equipo seleccionado
List<Employee> filteredEmployees = employeeService.findEmployeesByTeam(selectedEquipo);
employeeComboBox.setItems(filteredEmployees);
}
});
setEmployeeComboBoxProperties();
HorizontalLayout filtersLayout = new HorizontalLayout(equipoDropdown, employeeComboBox);
filtersLayout.setSpacing(true);
HorizontalLayout actividadFormLayout = configurarFormularioActividades();
HorizontalLayout tareasEspecificasLayout = configurarTareasEspecificasLayout();
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, filtersLayout, actividadFormLayout,
equipoLabel, grid, empresaLabel, tareasEspecificasLayout,
gridActividadesEspecificas, buttonsLayout, totalesLayout);
}
private void configurarGrid() {
grid.removeAllColumns();
grid.setItems(actividades);
grid.addColumn(Actividad::getNombre).setHeader("Actividad");
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 Día").setKey("totalDia");
grid.setItems(actividades);
}
private void configurarGridActividadesEspecificas() {
gridActividadesEspecificas.removeAllColumns();
gridActividadesEspecificas.setItems(actividadesEspecificas);
gridActividadesEspecificas.addColumn(Actividad::getTarea).setHeader("Actividad");
gridActividadesEspecificas.addColumn(Actividad::getLunes).setHeader("Lunes");
gridActividadesEspecificas.addColumn(Actividad::getMartes).setHeader("Martes");
gridActividadesEspecificas.addColumn(Actividad::getMiercoles).setHeader("Miércoles");
gridActividadesEspecificas.addColumn(Actividad::getJueves).setHeader("Jueves");
gridActividadesEspecificas.addColumn(Actividad::getViernes).setHeader("Viernes");
gridActividadesEspecificas.addColumn(Actividad::getSabado).setHeader("Sábado");
gridActividadesEspecificas.addColumn(Actividad::getDomingo).setHeader("Domingo");
gridActividadesEspecificas.addColumn(this::calcularTotalPorDia).setHeader("Total Día Específico")
.setKey("totalDiaEspecifico");
}
private HorizontalLayout configurarFormularioActividades() {
TextField actividadNombre = new TextField("Actividad");
actividadNombre.setWidth("200px");
TextField horasInput = crearCampoHora("Horas");
Button agregarActividadButton = new Button("Agregar Actividad", e -> {
try {
LocalDate selectedDate = fechaPicker.getValue();
if (selectedDate == null) {
Notification.show("Por favor, selecciona una fecha.");
return;
}
int selectedWeekNumber = getWeekOfYear(selectedDate);
if (selectedWeekNumber != weekNumber) {
Notification.show("Solo puedes agregar actividades dentro de la semana actual.");
return;
}
DayOfWeek selectedDay = selectedDate.getDayOfWeek();
Actividad.Builder actividadBuilder = new Actividad.Builder()
.nombre(actividadNombre.getValue());
double horas = parseHoras(horasInput.getValue());
switch (selectedDay) {
case MONDAY -> actividadBuilder.lunes(horas);
case TUESDAY -> actividadBuilder.martes(horas);
case WEDNESDAY -> actividadBuilder.miercoles(horas);
case THURSDAY -> actividadBuilder.jueves(horas);
case FRIDAY -> actividadBuilder.viernes(horas);
case SATURDAY -> actividadBuilder.sabado(horas);
case SUNDAY -> actividadBuilder.domingo(horas);
default -> throw new IllegalArgumentException("Día seleccionado no válido: " + selectedDay);
}
String tareaSeleccionada = tareasEspecificasDropdown.getValue();
double horasTarea = parseHoras(horasTareaInput.getValue());
if (tareaSeleccionada != null && !tareaSeleccionada.isEmpty()) {
actividadBuilder.tarea(tareaSeleccionada).lunes(horasTarea);
Actividad nuevaActividadEspecifica = actividadBuilder.build();
actividadesEspecificas.add(nuevaActividadEspecifica);
gridActividadesEspecificas.setItems(actividadesEspecificas);
} else {
Actividad nuevaActividad = actividadBuilder.build();
actividades.add(nuevaActividad);
grid.setItems(actividades);
}
Actividad nuevaActividad = actividadBuilder.build();
agregarOActualizarActividad(nuevaActividad, selectedDay, horas);
actualizarTotales();
Notification.show("Actividad agregada correctamente");
actividadNombre.clear();
horasInput.clear();
tareasEspecificasDropdown.clear();
horasTareaInput.clear();
} catch (NumberFormatException ex) {
Notification.show("Error: Por favor ingresa números válidos para las horas.");
}
});
return new HorizontalLayout(actividadNombre, horasInput, agregarActividadButton);
}
private void agregarOActualizarActividad(final Actividad nuevaActividad, final DayOfWeek dia, final double horas) {
Actividad actividadExistente = actividades.stream()
.filter(a -> a.getNombre().equals(nuevaActividad.getNombre()))
.findFirst()
.orElse(null);
if (actividadExistente != null) {
// Si la actividad ya existe, actualiza las horas para el día correspondiente
switch (dia) {
case MONDAY -> actividadExistente.setLunes(horas);
case TUESDAY -> actividadExistente.setMartes(horas);
case WEDNESDAY -> actividadExistente.setMiercoles(horas);
case THURSDAY -> actividadExistente.setJueves(horas);
case FRIDAY -> actividadExistente.setViernes(horas);
case SATURDAY -> actividadExistente.setSabado(horas);
case SUNDAY -> actividadExistente.setDomingo(horas);
default -> throw new IllegalArgumentException("Día seleccionado no válido: " + dia);
}
} else {
switch (dia) {
case MONDAY -> nuevaActividad.setLunes(horas);
case TUESDAY -> nuevaActividad.setMartes(horas);
case WEDNESDAY -> nuevaActividad.setMiercoles(horas);
case THURSDAY -> nuevaActividad.setJueves(horas);
case FRIDAY -> nuevaActividad.setViernes(horas);
case SATURDAY -> nuevaActividad.setSabado(horas);
case SUNDAY -> nuevaActividad.setDomingo(horas);
default -> throw new IllegalArgumentException("Día seleccionado no válido: " + dia);
}
actividades.add(nuevaActividad);
}
grid.setItems(actividades);
guardarActividades();
}
private HorizontalLayout configurarTareasEspecificasLayout() {
Button agregarTareaButton = new Button("Agregar Tarea PFS", e -> {
try {
String tareaSeleccionada = tareasEspecificasDropdown.getValue();
String tareaNombre = "Otros"
.equals(tareaSeleccionada) ? tareaEspecificaInput
.getValue() : tareaSeleccionada;
if (tareaNombre == null || tareaNombre.isEmpty()) {
Notification.show("Por favor, especifica la tarea.");
return;
}
double horasTarea = parseHoras(horasTareaInput.getValue());
if (horasTarea <= 0) {
Notification.show("Por favor, ingresa un número válido para las horas.");
return;
}
if (tareaSeleccionada != null && !tareaSeleccionada.isEmpty() && horasTarea > 0) {
LocalDate selectedDate = fechaPicker.getValue();
if (selectedDate == null) {
Notification.show("Selecciona una fecha para asignar la tarea.");
return;
}
DayOfWeek selectedDay = selectedDate.getDayOfWeek();
Actividad.Builder actividadBuilder = new Actividad.Builder().tarea(tareaNombre);
switch (selectedDay) {
case MONDAY -> actividadBuilder.lunes(horasTarea);
case TUESDAY -> actividadBuilder.martes(horasTarea);
case WEDNESDAY -> actividadBuilder.miercoles(horasTarea);
case THURSDAY -> actividadBuilder.jueves(horasTarea);
case FRIDAY -> actividadBuilder.viernes(horasTarea);
case SATURDAY -> actividadBuilder.sabado(horasTarea);
case SUNDAY -> actividadBuilder.domingo(horasTarea);
default -> throw new IllegalArgumentException("Día seleccionado no válido: " + selectedDay);
}
Actividad nuevaActividadEspecifica = actividadBuilder.build();
actividadesEspecificas.add(nuevaActividadEspecifica);
gridActividadesEspecificas.setItems(actividadesEspecificas);
actualizarTotales();
tareasEspecificasDropdown.clear();
tareaEspecificaInput.clear();
horasTareaInput.clear();
Notification.show("Tarea específica agregada correctamente");
} else {
Notification.show("Selecciona una tarea y asegúrate de ingresar horas válidas.");
}
} catch (NumberFormatException ex) {
Notification.show("Error: Por favor ingresa un número válido para las horas.");
}
});
HorizontalLayout layout = new HorizontalLayout(tareasEspecificasDropdown,
tareaEspecificaInput, horasTareaInput, agregarTareaButton);
layout.setSpacing(true);
return layout;
}
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.FRIDAY.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 totalActividadesGenerales = actividades.stream()
.mapToDouble(this::calcularTotalPorDia)
.sum();
double totalActividadesEspecificas = actividadesEspecificas.stream()
.mapToDouble(this::calcularTotalPorDia)
.sum();
double totalFinal = totalActividadesGenerales + totalActividadesEspecificas;
double horasPendientes = 40 - totalFinal;
totalCompletadoLabel.setText("Total Hrs/Semana Completadas: " + totalFinal);
horasPendientesLabel.setText("Horas Pendientes: " + horasPendientes);
}
private void guardarActividades() {
Employee selectedEmployee = employeeComboBox.getValue();
String selectedEquipo = equipoDropdown.getValue();
if (selectedEmployee == null || selectedEquipo == null) {
Notification.show("Por favor selecciona un equipo y un empleado.");
return;
}
if (actividades.isEmpty()) {
Notification.show("No hay actividades para guardar.");
return;
}
double totalHorasSemana = actividades.stream()
.mapToDouble(this::calcularTotalPorDia)
.sum();
List<HoursWorked> savedHoursWorked = new ArrayList<>(); // Para almacenar los objetos guardados
actividades.forEach(actividad -> {
HoursWorked hoursWorked = new HoursWorked();
hoursWorked.setActividad(actividad);
hoursWorked.setFecha(fechaPicker.getValue());
hoursWorked.setEmployee(selectedEmployee);
hoursWorked.setWeekNumber(weekNumber);
hoursWorked.setTotalHours(totalHorasSemana);
hoursWorkedService.saveHoursWorked(hoursWorked); hoursWorkedService.saveHoursWorked(hoursWorked);
Notification.show("Horas trabajadas guardadas correctamente.");
}); closeForm();
Notification.show("Actividades guardadas correctamente."); }
} }
private double calcularTotalHoras(final List<HoursWorked> listaDeHorasTrabajadas) { private void setFieldValues(HoursWorked hoursWorked) {
return listaDeHorasTrabajadas.stream() hoursWorked.setDate(dateField.getValue());
.mapToDouble(HoursWorked::getTotalHours) hoursWorked.setTeam(teamField.getValue());
.sum(); hoursWorked.setEmployee(employeeField.getValue());
hoursWorked.setActividad(activityField.getValue());
} }
private List<HoursWorked> obtenerDatos() { private void closeForm() {
return new ArrayList<>(); getUI().ifPresent(ui -> ui.navigate("hoursworked"));
} }
private void closeView() { private boolean isFormValid() {
getUI().ifPresent(ui -> ui.navigate(HoursWorkedView.class)); return dateField.getValue() != null &&
teamField.getValue() != null &&
employeeField.getValue() != null &&
!activityField.isEmpty();
} }
private void configureViewOrEditAction(String action) {
if ("edit".equals(action) && request != null) {
setFieldsReadOnly(false);
} else if ("view".equals(action) && request != null) {
setFieldsReadOnly(true);
saveButton.setEnabled(false);
}
}
private void setFieldsReadOnly(boolean readOnly) {
dateField.setReadOnly(readOnly);
teamField.setReadOnly(readOnly);
employeeField.setReadOnly(readOnly);
activityField.setReadOnly(readOnly);
}
private H3 createEmployeeHeader() {
return new H3("Empleado: " + (employee != null ? employee.getFirstName() + " " + employee.getLastName() : ""));
}
} }

View File

@ -11,7 +11,7 @@ import org.springframework.context.annotation.Scope;
@PermitAll @PermitAll
@Scope("prototype") @Scope("prototype")
@PageTitle("Timesheet") @PageTitle("Timesheet")
@Route(value = "/timesheets/me", layout = MainLayout.class) @Route(value = "/timesheets", layout = MainLayout.class)
public class TimesheetView extends Main { public class TimesheetView extends Main {
} }