hoursworked #75

Closed
alex wants to merge 25 commits from hoursworked into main
6 changed files with 293 additions and 89 deletions
Showing only changes of commit d49eee8988 - Show all commits

View File

@ -15,22 +15,21 @@ import java.util.UUID;
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class HoursWorked extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
@ManyToOne
@JoinColumn(name = "employee_id", nullable = false)
@JoinColumn(name = "employee_id", nullable = true)
private Employee employee;
@ManyToOne
@JoinColumn(name = "team_id", nullable = false)
@JoinColumn(name = "team_id", nullable = true)
private Team team;
private int weekNumber;
private LocalDate date;
private String actividad;
private String tareasEspecificas;
private double hours;
private double horasTareasEspecificas;
private double horaspendientes;
private double totalHours;
public Employee getEmployee() {
@ -88,5 +87,21 @@ public class HoursWorked extends BaseEntity {
public void setTeam(Team team) {
this.team = team;
}
public String getTareasEspecificas() {
return tareasEspecificas;
}
public void setTareasEspecificas(String tareasEspecificas) {
this.tareasEspecificas = tareasEspecificas;
}
public double getHorasTareasEspecificas() {
return horasTareasEspecificas;
}
public void setHorasTareasEspecificas(double horasTareasEspecificas) {
this.tareasEspecificas = tareasEspecificas;
}
}

View File

@ -72,13 +72,8 @@ public class HoursWorkedService {
}
public HoursWorked getHoursWorked(final UUID id) {
Optional<HoursWorked> registro = hoursWorkedRepository.findById(id);
HoursWorked hw = registro.orElse(null);
if (hw == null) {
System.out.println("No se encontró el registro de horas trabajadas");
return null;
}
return hw;
final Optional<HoursWorked> hoursWorked = hoursWorkedRepository.findById(id);
return hoursWorked.get();
}
}

View File

@ -1,11 +1,19 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.HoursWorked;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.model.Team;
import com.primefactorsolutions.model.TimeOffRequest;
import com.primefactorsolutions.model.TimeOffRequestStatus;
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.grid.GridSortOrder;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.data.provider.SortDirection;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
@ -14,40 +22,125 @@ import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.grid.PagingGrid;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("HoursWorkedList")
@PageTitle("Registro de Horas Trabajadas")
@Route(value = "/hours-worked-list", layout = MainLayout.class)
public class HoursWorkedListView extends Main {
public class HoursWorkedListView extends Main {
private final HoursWorkedService hoursWorkedService;
private final PagingGrid<HoursWorked> table = new PagingGrid<>(HoursWorked.class);
private final EmployeeService employeeService;
private final TeamService teamService;
private final PagingGrid<Employee> hoursWorkedGrid = new PagingGrid<>();
private final PagingGrid<Employee> table = new PagingGrid<>(Employee.class);
private List<Employee> employees = Collections.emptyList();
private ComboBox<Employee> employeeFilter;
private ComboBox<Team> teamFilter;
private UUID selectedEmployeeId;
public HoursWorkedListView (final HoursWorkedService hoursWorkedService) {
public HoursWorkedListView(final HoursWorkedService hoursWorkedService,
final EmployeeService employeeService,
final TeamService teamService) {
this.hoursWorkedService = hoursWorkedService;
setupView();
refreshGrid();
this.employeeService = employeeService;
this.teamService = teamService;
this.employees = employeeService.findAllEmployees();
initializeView();
refreshGridListHoursWorked(null, null);
}
private void setupView() {
add(new H2("Lista de Horas trabajadas"));
configureTable();
add(createAddHoursWorkedButton());
add(table);
private void refreshGridListHoursWorked(final Employee employee,
final Team team) {
hoursWorkedGrid.setPagingDataProvider((page, pageSize) -> {
int start = (int) (page * hoursWorkedGrid.getPageSize());
return fetchFilteredEmployees(start, pageSize, employee, team);
});
hoursWorkedGrid.getDataProvider().refreshAll();
}
private void configureTable() {
table.setColumns("Actividad", "Lunes", "Martes", "Miercoles", "Jueves", "Viernes", "Sabado", "Domingo");
addEditButtonColumn("View", this::navigateToHoursWorkedView);
addEditButtonColumn("Edit", this::navigateToEditView);
setupPagingGrid();
private List<Employee> fetchFilteredEmployees(final int start,
final int pageSize,
final Employee employee,
final Team team) {
List<Employee> filteredEmployees = employeeService.findAllEmployees();
if (employee != null && !"TODOS".equals(employee.getFirstName())) {
filteredEmployees = filteredEmployees.stream()
.filter(emp -> emp.getId().equals(employee.getId()))
.collect(Collectors.toList());
}
if (team != null && !"TODOS".equals(team.getName())) {
filteredEmployees = filteredEmployees.stream()
.filter(emp -> emp.getTeam() != null && emp.getTeam().getId().equals(team.getId()))
.collect(Collectors.toList());
}
int end = Math.min(start + pageSize, filteredEmployees.size());
return filteredEmployees.subList(start, end);
}
private void addEditButtonColumn( final String label, final ButtonClickHandler handler) {
table.addComponentColumn(hoursWorked -> createButton(label, () -> handler.handle(hoursWorked)));
private void initializeView() {
setupFilters();
add(createAddHoursWorked());
setupListHoursWorkedGrid();
add(hoursWorkedGrid);
add(createActionButtons());
}
private void setupFilters() {
add(createEmployeeFilter());
add(createTeamFilter());
}
private void setupListHoursWorkedGrid() {
hoursWorkedGrid.addColumn(this::getEmployeeFullName).setHeader("Empleado");
hoursWorkedGrid.addColumn(this::getTeamName).setHeader("Equipo");
hoursWorkedGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
hoursWorkedGrid.setPageSize(5);
hoursWorkedGrid.asSingleSelect().addValueChangeListener(event -> {
Employee selectedRequest = event.getValue();
if (selectedRequest != null) {
selectedEmployeeId = selectedRequest.getId();
}
});
}
private String getTeamName(final Employee employee) {
Team team = employee.getTeam();
return team != null ? team.getName() : "Sin asignar";
}
private String getTeamLabel(final Team team) {
return "TODOS".equals(team.getName()) ? "TODOS" : team.getName();
}
private 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) {
@ -56,46 +149,66 @@ public class HoursWorkedListView extends Main {
return button;
}
private Button createAddHoursWorkedButton() { return createButton("Crear Actividad", this::navigateToAddHoursWorkedView);}
private void navigateToEditView(final HoursWorked hoursWorked) {
getUI().ifPresent(ui -> ui.navigate(HoursWorkedView.class, hoursWorked.getId().toString() + "/edit"));
private Button createAddHoursWorked() {
return createButton("Agregar Actividad", this::navigateToHours);
}
private void navigateToHoursWorkedView(final HoursWorked hoursWorked) {
getUI().ifPresent(ui -> ui.navigate(HoursWorkedView.class, hoursWorked.getId().toString() + "/view"));
private void navigateToHours() { getUI().ifPresent(ui -> ui.navigate(HoursWorkedView.class, "new"));}
private ComboBox<Employee> createEmployeeFilter() {
employeeFilter = new ComboBox<>("Empleado");
List<Employee> employees = new ArrayList<>(employeeService.findAllEmployees());
employees.addFirst(createAllEmployeesOption());
employeeFilter.setItems(employees);
employeeFilter.setItemLabelGenerator(this::getEmployeeFullName);
employeeFilter.setValue(employees.getFirst());
employeeFilter.addValueChangeListener(event ->
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 Employee createAllEmployeesOption() {
Employee allEmployeesOption = new Employee();
allEmployeesOption.setFirstName("TODOS");
return allEmployeesOption;
}
private Team createAllTeamsOption() {
Team allTeamsOption = new Team();
allTeamsOption.setName("TODOS");
return allTeamsOption;
}
private void navigateToAddHoursWorkedView() { getUI().ifPresent(ui -> ui.navigate(HoursWorkedView.class, "new"));}
private void setupPagingGrid() {
table.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
table.setPageSize(5);
}
private void refreshGrid() {
table.setPagingDataProvider((page, pageSize) -> fetchHoursWorkeds((int) page, pageSize));
}
private List<HoursWorked> fetchHoursWorkeds(final int page, final int pageSize) {
int start = page * pageSize;
if (hasSortOrder()) {
return fetchSortedHoursWorkeds(start, pageSize);
}
return hoursWorkedService.findHoursWorkeds(start, pageSize);
}
private boolean hasSortOrder() { return !table.getSortOrder().isEmpty(); }
private List<HoursWorked> fetchSortedHoursWorkeds(final int start, final int pageSize) {
GridSortOrder<HoursWorked> sortOrder = table.getSortOrder().getFirst();
return hoursWorkedService.findHoursWorkeds(start, pageSize,
sortOrder.getSorted().getKey(),
sortOrder.getDirection() == SortDirection.ASCENDING);
}
@FunctionalInterface
private interface ButtonClickHandler {
void handle(HoursWorked hoursWorked);
}
}

View File

@ -5,10 +5,9 @@ import com.primefactorsolutions.model.Team;
import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.HoursWorkedService;
import com.primefactorsolutions.service.TeamService;
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.html.H2;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.button.Button;
@ -17,9 +16,12 @@ import com.vaadin.flow.router.*;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.datepicker.VDatePicker;
import org.vaadin.firitin.form.BeanValidationForm;
import java.awt.*;
import java.time.LocalDate;
import java.time.YearMonth;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@ -27,33 +29,42 @@ import java.util.UUID;
@PermitAll
@Scope("prototype")
@PageTitle("Horas Trabajadas")
@Route(value = "/timesheets/listhw/:hours-worked?/me", layout = MainLayout.class)
@Route(value = "/hours-worked-list/:hours-workedId?/:action?", layout = MainLayout.class)
public class HoursWorkedView extends BeanValidationForm<HoursWorked> implements HasUrlParameter<String> {
private final DatePicker dateField = new DatePicker("Fecha");
private final VDatePicker dateField = new VDatePicker("Fecha");
private final ComboBox<Team> teamField = new ComboBox<>("Equipo");
private final ComboBox<Employee> employeeField = new ComboBox<>("Empleado");
private ComboBox<Employee> employeeField;
private final ComboBox<String> tareasEspecificasDropdown = new ComboBox<>("Tarea Específica");
private final TextField tareaEspecificaInput = new TextField("Otra Tarea Específica");
private final TextField horasTareaEspecificaField = new TextField("Horas Tarea Específica");
private final TextField activityField = new TextField("Actividad");
private final TextField hoursField = new TextField("Horas");
private final H2 equipoLabel = new H2("Tareas del Cliente/Equipo");
private final H2 empresaLabel = new H2("Tareas de la Empresa");
private final Label totalCompletadoLabel = new Label();
private final HoursWorkedService hoursWorkedService;
private final EmployeeService employeeService;
private final TeamService teamService;
private HoursWorked request;
private HoursWorked hoursWorked;
private Employee employee;
private Button saveButton;
public HoursWorkedView(HoursWorkedService hoursWorkedService,
EmployeeService employeeService,
TeamService teamService, TimeOffRequestService timeOffRequestService) {
TeamService teamService) {
super(HoursWorked.class);
this.hoursWorkedService = hoursWorkedService;
this.employeeService = employeeService;
this.teamService = teamService;
// Initialize combo boxes
initializeDateField();
initializeTeamField();
initializeEmployeeField();
configureTareasEspecificas();
}
@Override
@ -71,7 +82,6 @@ public class HoursWorkedView extends BeanValidationForm<HoursWorked> implements
if ("edit".equals(action) && !s.isEmpty()) {
saveButton.setVisible(true);
} else if ("view".equals(action) && !s.isEmpty()) {
saveButton.setVisible(false);
}
}
@ -83,12 +93,35 @@ public class HoursWorkedView extends BeanValidationForm<HoursWorked> implements
dateField,
teamField,
employeeField,
equipoLabel,
activityField,
hoursField,
empresaLabel,
tareasEspecificasDropdown,
tareaEspecificaInput,
horasTareaEspecificaField,
createCloseButton()
);
}
private void configureTareasEspecificas() {
tareasEspecificasDropdown.setItems("Entrevistas", "Reuniones", "Colaboraciones", "Aprendizajes", "Proyectos PFS", "Otros");
tareasEspecificasDropdown.setPlaceholder("Selecciona una tarea...");
tareasEspecificasDropdown.addValueChangeListener(event -> {
String selected = event.getValue();
boolean isOtros = "Otros".equals(selected);
tareaEspecificaInput.setVisible(isOtros);
horasTareaEspecificaField.setVisible(true);
if (!isOtros) {
tareaEspecificaInput.clear();
horasTareaEspecificaField.clear();
}
});
tareaEspecificaInput.setVisible(false);
horasTareaEspecificaField.setVisible(false);
}
protected Button createSaveButton() {
saveButton = new Button("Guardar");
saveButton.addClickListener(event -> saveHoursWorked());
@ -102,23 +135,53 @@ public class HoursWorkedView extends BeanValidationForm<HoursWorked> implements
}
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 -> {
teamField.getValue();
});
if (teams != null) {
employeeField.getValue();
event.getValue();
}
}
);
}
private void initializeEmployeeField() {
employeeField.setItems(employeeService.findAllEmployees());
employeeField.setItemLabelGenerator(employee -> employee.getFirstName() + " " + employee.getLastName());
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.setLabel("Fecha");
}
private void saveHoursWorked() {
if (isFormValid()) {
HoursWorked hoursWorked = getEntity();
setFieldValues(hoursWorked);
hoursWorkedService.saveHoursWorked(hoursWorked);
hoursWorkedService.save(hoursWorked);
Notification.show("Horas trabajadas guardadas correctamente.");
closeForm();
}
@ -129,11 +192,26 @@ public class HoursWorkedView extends BeanValidationForm<HoursWorked> implements
hoursWorked.setTeam(teamField.getValue());
hoursWorked.setEmployee(employeeField.getValue());
hoursWorked.setActividad(activityField.getValue());
//hoursWorked.setHours(hoursField.;
try {
double hours = Double.parseDouble(hoursField.getValue());
hoursWorked.setHours(hours);
} catch (NumberFormatException e) {
Notification.show("Por favor, ingrese un número válido para las horas.");
}
if ("Otros".equals(tareasEspecificasDropdown.getValue())) {
// Maneja horas y actividad específica adicional
hoursWorked.setTareasEspecificas(tareaEspecificaInput.getValue());
try {
double horasEspecifica = Double.parseDouble(horasTareaEspecificaField.getValue());
hoursWorked.setHorasTareasEspecificas(horasEspecifica);
} catch (NumberFormatException e) {
Notification.show("Por favor, ingrese un número válido para las horas de la tarea específica.");
}
}
}
private void closeForm() {
getUI().ifPresent(ui -> ui.navigate("hours-workedId"));
getUI().ifPresent(ui -> ui.navigate("hours-worked-list/" + hoursWorked.getId().toString()));
}
private boolean isFormValid() {
@ -141,12 +219,13 @@ public class HoursWorkedView extends BeanValidationForm<HoursWorked> implements
teamField.getValue() != null &&
employeeField.getValue() != null &&
!activityField.isEmpty();
}
private void configureViewOrEditAction(String action) {
if ("edit".equals(action) && request != null) {
if ("edit".equals(action) && hoursWorked != null) {
setFieldsReadOnly(false);
} else if ("view".equals(action) && request != null) {
} else if ("view".equals(action) && hoursWorked != null) {
setFieldsReadOnly(true);
saveButton.setEnabled(false);
}
@ -159,6 +238,5 @@ public class HoursWorkedView extends BeanValidationForm<HoursWorked> implements
activityField.setReadOnly(readOnly);
}
private H3 createEmployeeHeader() {
return new H3("Empleado: " + (employee != null ? employee.getFirstName() + " " + employee.getLastName() : ""));}
}

View File

@ -153,7 +153,7 @@ public class MainLayout extends AppLayout {
LineAwesomeIcon.LIST_ALT.create()));
SideNavItem timesheet = new SideNavItem("My Timesheet", TimesheetView.class,
LineAwesomeIcon.HOURGLASS_START_SOLID.create());
timesheet.addItem(new SideNavItem("Hours Worked List View", HoursWorkedListView.class,
timesheet.addItem(new SideNavItem("Registro de Horas Trabajadas", HoursWorkedListView.class,
LineAwesomeIcon.ID_CARD_SOLID.create()));
timesheet.addItem(new SideNavItem("Reporte Horas Trabajadas", ReporteView.class,
LineAwesomeIcon.ID_CARD_SOLID.create()));

View File

@ -90,3 +90,6 @@ 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);
insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('89bc4b2a-943f-487c-a9f3-bacf78145e67', 1, 'cba3efb7-32bc-44be-9fdc-fc5e4f211254', 'LUNES_CARNAVAL', 'APROBADO', 1, '2024-02-12', '2024-02-12', '2024-02-12', 1, 0);
INSERT INTO HOURS_WORKED (ID, VERSION, ACTIVIDAD, DATE, HORAS_TAREAS_ESPECIFICAS, HORASPENDIENTES, HOURS, TAREAS_ESPECIFICAS, TOTAL_HOURS, WEEK_NUMBER, EMPLOYEE_ID, TEAM_ID)
VALUES ('6d6b3a71-9b11-4526-8335-b089627a8cd6', 0, 'Scrum Meeting', '2024-11-15', 0.0, 0.0, 2.0, NULL, 0.0, 0, '5c6f11fe-c341-4be7-a9a6-bba0081ad7c6', 'b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa');