#45-Registro Semanal de Horas Trabajadas #69

Merged
alex merged 8 commits from hoursworked into main 2024-11-11 19:21:23 +00:00
7 changed files with 295 additions and 82 deletions
Showing only changes of commit ca2cd583a0 - Show all commits

View File

@ -1,8 +1,5 @@
package com.primefactorsolutions.model; package com.primefactorsolutions.model;
import java.util.ArrayList;
import java.util.List;
public final class Actividad { public final class Actividad {
private String nombre; private String nombre;
private double lunes; private double lunes;
@ -12,11 +9,9 @@ public final class Actividad {
private double viernes; private double viernes;
private double sabado; private double sabado;
private double domingo; private double domingo;
private String nombreActivEsp; private String tarea;
private double horas; private double horas;
private static final List<Actividad> actividadesExistentes = new ArrayList<>();
public Actividad(final Builder builder) { public Actividad(final Builder builder) {
this.nombre = builder.nombre; this.nombre = builder.nombre;
this.lunes = builder.lunes; this.lunes = builder.lunes;
@ -26,7 +21,7 @@ public final class Actividad {
this.viernes = builder.viernes; this.viernes = builder.viernes;
this.sabado = builder.sabado; this.sabado = builder.sabado;
this.domingo = builder.domingo; this.domingo = builder.domingo;
this.nombreActivEsp = builder.nombreActivEsp; // Cambié aquí también this.tarea = builder.tarea;
this.horas = builder.horas; this.horas = builder.horas;
} }
@ -62,8 +57,8 @@ public final class Actividad {
return domingo; return domingo;
} }
public String getNombreActivEsp() { // Cambié aquí también public String getTarea() { // Cambié aquí también
return nombreActivEsp; return tarea;
} }
public double getHoras() { public double getHoras() {
@ -80,17 +75,17 @@ public final class Actividad {
private double viernes; private double viernes;
private double sabado; private double sabado;
private double domingo; private double domingo;
private String nombreActivEsp; // Cambié 'tarea' por 'descripcion' private String tarea; // Cambié 'tarea' por 'descripcion'
private double horas; private double horas;
public Builder nombreActivEsp(final String nombreActivEsp, final double horas) { public Builder tarea(final String tarea, final double horas) {
this.nombreActivEsp = nombreActivEsp; this.tarea = tarea;
this.horas = horas; this.horas = horas;
return this; return this;
} }
public Builder nombreActivEsp(final String nombreActivEsp) { public Builder tarea(final String tarea) {
this.nombreActivEsp = nombreActivEsp; this.tarea = tarea;
return this; return this;
} }

View File

@ -3,6 +3,7 @@ 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;
@ -10,4 +11,7 @@ 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> findByTeamIdAndLeadManagerTrue(UUID teamId);
List<Employee> findByTeamName(String teamName);
} }

View File

@ -48,6 +48,13 @@ 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.findByTeamIdAndLeadManagerTrue(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();
@ -115,4 +122,10 @@ 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

@ -32,7 +32,7 @@ public class HoursWorkedService {
hoursWorkedRepository.deleteById(id); hoursWorkedRepository.deleteById(id);
} }
public List<HoursWorked> findByWeekNumber(int weekNumber) { public List<HoursWorked> findByWeekNumber(final int weekNumber) {
return hoursWorkedRepository.findByWeekNumber(weekNumber); return hoursWorkedRepository.findByWeekNumber(weekNumber);
} }

View File

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

View File

@ -17,8 +17,11 @@ 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.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import com.vaadin.flow.component.html.Label; import com.vaadin.flow.component.html.Label;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import java.time.DayOfWeek; import java.time.DayOfWeek;
@ -62,6 +65,11 @@ public class HoursWorkedView extends VerticalLayout {
private final Label numeroSemanaLabel = new Label("Número de la Semana: "); private final Label numeroSemanaLabel = new Label("Número de la Semana: ");
private DatePicker fechaPicker = new DatePicker("Selecciona una fecha"); private DatePicker fechaPicker = new DatePicker("Selecciona una fecha");
@Autowired
private InternalResourceViewResolver defaultViewResolver;
@Qualifier("defaultServletHandlerMapping")
@Autowired
private HandlerMapping defaultServletHandlerMapping;
public HoursWorkedView(final EmployeeService employeeService, final HoursWorkedService hoursWorkedService) { public HoursWorkedView(final EmployeeService employeeService, final HoursWorkedService hoursWorkedService) {
@ -75,7 +83,8 @@ public class HoursWorkedView extends VerticalLayout {
} }
private void configurarTareasEspecificas() { private void configurarTareasEspecificas() {
tareasEspecificasDropdown.setItems("Entrevistas", "Reuniones", "Colaboraciones", "Aprendizajes", "Proyectos PFS", "Otros"); tareasEspecificasDropdown.setItems("Entrevistas", "Reuniones", "Colaboraciones",
"Aprendizajes", "Proyectos PFS", "Otros");
tareasEspecificasDropdown.setPlaceholder("Selecciona una tarea..."); tareasEspecificasDropdown.setPlaceholder("Selecciona una tarea...");
tareasEspecificasDropdown.addValueChangeListener(event -> { tareasEspecificasDropdown.addValueChangeListener(event -> {
@ -141,8 +150,18 @@ public class HoursWorkedView extends VerticalLayout {
getUI().ifPresent(ui -> ui.navigate(HoursWorkedMonthView.class)); getUI().ifPresent(ui -> ui.navigate(HoursWorkedMonthView.class));
}); });
equipoDropdown.setItems("Equipo 1", "Equipo 2", "Equipo 3"); equipoDropdown.setItems("ABC", "DEF", "XYZ");
equipoDropdown.setWidth("250px"); 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(); setEmployeeComboBoxProperties();
HorizontalLayout filtersLayout = new HorizontalLayout(equipoDropdown, employeeComboBox); HorizontalLayout filtersLayout = new HorizontalLayout(equipoDropdown, employeeComboBox);
@ -163,7 +182,8 @@ public class HoursWorkedView extends VerticalLayout {
totalesLayout.setSpacing(true); totalesLayout.setSpacing(true);
totalesLayout.setPadding(true); totalesLayout.setPadding(true);
add(fechaPicker, fechasLabel, numeroSemanaLabel, filtersLayout, actividadFormLayout, tareasEspecificasLayout, grid,gridActividadesEspecificas, buttonsLayout, totalesLayout); add(fechaPicker, fechasLabel, numeroSemanaLabel, filtersLayout, actividadFormLayout,
tareasEspecificasLayout, grid, gridActividadesEspecificas, buttonsLayout, totalesLayout);
} }
private void configurarGrid() { private void configurarGrid() {
@ -183,7 +203,7 @@ public class HoursWorkedView extends VerticalLayout {
private void configurarGridActividadesEspecificas() { private void configurarGridActividadesEspecificas() {
gridActividadesEspecificas.removeAllColumns(); gridActividadesEspecificas.removeAllColumns();
gridActividadesEspecificas.setItems(actividadesEspecificas); gridActividadesEspecificas.setItems(actividadesEspecificas);
gridActividadesEspecificas.addColumn(Actividad::getNombreActivEsp).setHeader("Actividad"); gridActividadesEspecificas.addColumn(Actividad::getTarea).setHeader("Actividad");
gridActividadesEspecificas.addColumn(Actividad::getLunes).setHeader("Lunes"); gridActividadesEspecificas.addColumn(Actividad::getLunes).setHeader("Lunes");
gridActividadesEspecificas.addColumn(Actividad::getMartes).setHeader("Martes"); gridActividadesEspecificas.addColumn(Actividad::getMartes).setHeader("Martes");
gridActividadesEspecificas.addColumn(Actividad::getMiercoles).setHeader("Miércoles"); gridActividadesEspecificas.addColumn(Actividad::getMiercoles).setHeader("Miércoles");
@ -191,7 +211,8 @@ public class HoursWorkedView extends VerticalLayout {
gridActividadesEspecificas.addColumn(Actividad::getViernes).setHeader("Viernes"); gridActividadesEspecificas.addColumn(Actividad::getViernes).setHeader("Viernes");
gridActividadesEspecificas.addColumn(Actividad::getSabado).setHeader("Sábado"); gridActividadesEspecificas.addColumn(Actividad::getSabado).setHeader("Sábado");
gridActividadesEspecificas.addColumn(Actividad::getDomingo).setHeader("Domingo"); gridActividadesEspecificas.addColumn(Actividad::getDomingo).setHeader("Domingo");
gridActividadesEspecificas.addColumn(this::calcularTotalPorDia).setHeader("Total Día Específico").setKey("totalDiaEspecifico"); gridActividadesEspecificas.addColumn(this::calcularTotalPorDia).setHeader("Total Día Específico")
.setKey("totalDiaEspecifico");
} }
private HorizontalLayout configurarFormularioActividades() { private HorizontalLayout configurarFormularioActividades() {
@ -221,13 +242,14 @@ public class HoursWorkedView extends VerticalLayout {
case FRIDAY -> actividadBuilder.viernes(horas); case FRIDAY -> actividadBuilder.viernes(horas);
case SATURDAY -> actividadBuilder.sabado(horas); case SATURDAY -> actividadBuilder.sabado(horas);
case SUNDAY -> actividadBuilder.domingo(horas); case SUNDAY -> actividadBuilder.domingo(horas);
default -> throw new IllegalArgumentException("Día seleccionado no válido: " + selectedDay);
} }
String tareaSeleccionada = tareasEspecificasDropdown.getValue(); String tareaSeleccionada = tareasEspecificasDropdown.getValue();
double horasTarea = parseHoras(horasTareaInput.getValue()); double horasTarea = parseHoras(horasTareaInput.getValue());
if (tareaSeleccionada != null && !tareaSeleccionada.isEmpty()) { if (tareaSeleccionada != null && !tareaSeleccionada.isEmpty()) {
actividadBuilder.nombreActivEsp(tareaSeleccionada).lunes(horasTarea); actividadBuilder.tarea(tareaSeleccionada).lunes(horasTarea);
Actividad nuevaActividadEspecifica = actividadBuilder.build(); Actividad nuevaActividadEspecifica = actividadBuilder.build();
actividadesEspecificas.add(nuevaActividadEspecifica); actividadesEspecificas.add(nuevaActividadEspecifica);
gridActividadesEspecificas.setItems(actividadesEspecificas); gridActividadesEspecificas.setItems(actividadesEspecificas);
@ -255,7 +277,9 @@ public class HoursWorkedView extends VerticalLayout {
Button agregarTareaButton = new Button("Agregar Tarea PFS", e -> { Button agregarTareaButton = new Button("Agregar Tarea PFS", e -> {
try { try {
String tareaSeleccionada = tareasEspecificasDropdown.getValue(); String tareaSeleccionada = tareasEspecificasDropdown.getValue();
String tareaNombre = "Otros".equals(tareaSeleccionada) ? tareaEspecificaInput.getValue() : tareaSeleccionada; String tareaNombre = "Otros"
.equals(tareaSeleccionada) ? tareaEspecificaInput
.getValue() : tareaSeleccionada;
if (tareaNombre == null || tareaNombre.isEmpty()) { if (tareaNombre == null || tareaNombre.isEmpty()) {
Notification.show("Por favor, especifica la tarea."); Notification.show("Por favor, especifica la tarea.");
@ -276,7 +300,7 @@ public class HoursWorkedView extends VerticalLayout {
} }
DayOfWeek selectedDay = selectedDate.getDayOfWeek(); DayOfWeek selectedDay = selectedDate.getDayOfWeek();
Actividad.Builder actividadBuilder = new Actividad.Builder().nombreActivEsp(tareaNombre); Actividad.Builder actividadBuilder = new Actividad.Builder().tarea(tareaNombre);
switch (selectedDay) { switch (selectedDay) {
case MONDAY -> actividadBuilder.lunes(horasTarea); case MONDAY -> actividadBuilder.lunes(horasTarea);
@ -286,6 +310,7 @@ public class HoursWorkedView extends VerticalLayout {
case FRIDAY -> actividadBuilder.viernes(horasTarea); case FRIDAY -> actividadBuilder.viernes(horasTarea);
case SATURDAY -> actividadBuilder.sabado(horasTarea); case SATURDAY -> actividadBuilder.sabado(horasTarea);
case SUNDAY -> actividadBuilder.domingo(horasTarea); case SUNDAY -> actividadBuilder.domingo(horasTarea);
default -> throw new IllegalArgumentException("Día seleccionado no válido: " + selectedDay);
} }
Actividad nuevaActividadEspecifica = actividadBuilder.build(); Actividad nuevaActividadEspecifica = actividadBuilder.build();
@ -307,7 +332,8 @@ public class HoursWorkedView extends VerticalLayout {
} }
}); });
HorizontalLayout layout= new HorizontalLayout(tareasEspecificasDropdown, tareaEspecificaInput, horasTareaInput, agregarTareaButton); HorizontalLayout layout = new HorizontalLayout(tareasEspecificasDropdown,
tareaEspecificaInput, horasTareaInput, agregarTareaButton);
layout.setSpacing(true); layout.setSpacing(true);
return layout; return layout;
} }
@ -354,7 +380,6 @@ public class HoursWorkedView extends VerticalLayout {
Employee selectedEmployee = employeeComboBox.getValue(); Employee selectedEmployee = employeeComboBox.getValue();
String selectedEquipo = equipoDropdown.getValue(); String selectedEquipo = equipoDropdown.getValue();
if (selectedEmployee == null || selectedEquipo == null) { if (selectedEmployee == null || selectedEquipo == null) {
Notification.show("Por favor selecciona un equipo y un empleado."); Notification.show("Por favor selecciona un equipo y un empleado.");
return; return;
@ -372,6 +397,7 @@ public class HoursWorkedView extends VerticalLayout {
try { try {
hoursWorkedService.saveHoursWorked(hoursWorked); // Usa saveHoursWorked directamente hoursWorkedService.saveHoursWorked(hoursWorked); // Usa saveHoursWorked directamente
Notification.show("Actividades guardadas correctamente."); Notification.show("Actividades guardadas correctamente.");
System.out.println(hoursWorked);
} catch (Exception e) { } catch (Exception e) {
Notification.show("Error al guardar actividades: " + e.getMessage()); Notification.show("Error al guardar actividades: " + e.getMessage());
} }

View File

@ -1,13 +1,17 @@
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.combobox.ComboBox;
import com.vaadin.flow.component.datepicker.DatePicker; 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.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;
@ -15,10 +19,12 @@ 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.vaadin.flow.component.notification.Notification; import com.primefactorsolutions.service.EmployeeService;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.time.DayOfWeek;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.format.TextStyle;
import java.time.temporal.WeekFields; import java.time.temporal.WeekFields;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -31,90 +37,217 @@ 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<String> equipoComboBox = new ComboBox<>("Seleccionar Equipo"); private final ComboBox<Team> equipoComboBox = new ComboBox<>("Seleccionar Equipo");
private final DatePicker semanaPicker = new DatePicker("Seleccionar Semana"); 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 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, final ReportService reportService) { public ReporteView(final HoursWorkedService hoursWorkedService,
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);
equipoComboBox.setItems("Equipo 1", "Equipo 2", "Equipo 3"); // Opciones de equipo List<Team> teams = teamService.findAllTeams();
semanaPicker.setPlaceholder("Seleccione una fecha dentro de la semana deseada"); equipoComboBox.setItems(teams);
equipoComboBox.setItemLabelGenerator(Team::getName);
Button reportButton = new Button("Generar Reporte de Horas Trabajadas", event -> generateHoursWorkedReport()); // Configurar el ComboBox de semanas
HorizontalLayout filtersLayout = new HorizontalLayout(equipoComboBox, semanaPicker, reportButton); 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); add(filtersLayout);
// Añadir `headerLayout` al diseño principal para el encabezado dinámico
add(headerLayout);
updateHeaderLayout(null, null);
grid.addColumn(map -> map.get("ID")).setHeader("ID")
.getElement().getStyle().set("font-weight", "bold");
grid.addColumn(map -> map.get("Employee ID")).setHeader("Employee ID");
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() {
String selectedEquipo = equipoComboBox.getValue(); Team selectedEquipo = equipoComboBox.getValue();
LocalDate selectedDate = semanaPicker.getValue(); String selectedWeek = semanaComboBox.getValue();
if (selectedEquipo == null || selectedDate == null) { if (selectedEquipo == null || selectedWeek == null) {
Notification.show("Por favor, selecciona un equipo y una semana para generar el reporte.", 3000, Notification.Position.MIDDLE); Notification.show("Por favor, selecciona un equipo y una semana para generar el reporte.",
3000,
Notification.Position.MIDDLE);
return; return;
} }
int weekNumber = getWeekOfYear(selectedDate); 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() List<HoursWorked> hoursWorkedList = hoursWorkedService.findAll().stream()
.filter(hw -> hw.getEmployee().getTeam().equals(selectedEquipo) && hw.getWeekNumber() == weekNumber) .filter(hw -> hw.getEmployee().getTeam().getId()
.equals(selectedEquipo.getId()) && hw.getWeekNumber() == weekNumber)
.collect(Collectors.toList()); .collect(Collectors.toList());
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;
} }
try {
List<String> headers = List.of("ID", "Employee ID", "Empleado", "Week Number", "Total Hours");
List<Map<String, Object>> data = hoursWorkedList.stream() List<Map<String, Object>> data = hoursWorkedList.stream()
.map(hoursWorked -> { .map(hoursWorked -> {
Map<String, Object> map = new HashMap<>(); Map<String, Object> map = new HashMap<>();
map.put("ID", hoursWorked.getId().toString()); map.put("ID", hoursWorked.getId().toString());
map.put("Employee ID", hoursWorked.getEmployee().getId().toString()); map.put("Employee ID", hoursWorked.getEmployee().getId().toString());
map.put("Empleado", hoursWorked.getEmployee().getFirstName() + " " + hoursWorked.getEmployee().getLastName()); map.put("Empleado", hoursWorked.getEmployee().getFirstName() + " "
map.put("Week Number", hoursWorked.getWeekNumber()); + hoursWorked.getEmployee().getLastName());
map.put("Total Hours", hoursWorked.getTotalHours()); map.put("Horas Trabajadas", hoursWorked.getTotalHours());
map.put("Horas Pendientes", 40 - hoursWorked.getTotalHours());
map.put("Observaciones", "");
return map; return map;
}) })
.collect(Collectors.toList()); .collect(Collectors.toList());
byte[] excelBytes = reportService.writeAsExcel("hours_worked_report", headers, data); grid.setItems(data);
generateExcelDownloadLink(data, weekNumber);
}
private void updateHeaderLayout(final Team team, final LocalDate dateInWeek) {
headerLayout.removeAll();
if (team != null && dateInWeek != null) {
LocalDate startOfWeek = dateInWeek.with(DayOfWeek.FRIDAY);
LocalDate endOfWeek = dateInWeek.with(DayOfWeek.THURSDAY);
int weekNumber = getWeekOfYear(dateInWeek);
String formattedStartDate = startOfWeek.getDayOfMonth() + " de "
+ startOfWeek.getMonth().getDisplayName(TextStyle.FULL, Locale.getDefault());
String formattedEndDate = endOfWeek.getDayOfMonth() + " de "
+ endOfWeek.getMonth().getDisplayName(TextStyle.FULL, Locale.getDefault());
headerLayout.add(new Span("Informe " + String.format("%03d", weekNumber)
+ "/" + currentYear) {{
getStyle().set("font-size", "24px");
getStyle().set("font-weight", "bold");
}});
String teamLeadName = employeeService.getTeamLeadName(team.getId());
headerLayout.add(
new Span("Asunto: Informe Semanal de Horas Trabajadas") {{
getStyle().set("font-size", "18px");
}},
semanaInfoSpan,
new Span("Horas a cumplir: 40 horas") {{
getStyle().set("font-size", "18px");
}},
new Span("Equipo: " + team.getName()) {{
getStyle().set("font-size", "18px");
}},
new Span("Team Lead: " + teamLeadName) {{
getStyle().set("font-size", "18px");
}}
);
}
}
private void generateExcelDownloadLink(final List<Map<String, Object>> data,
final int weekNumber) {
try {
List<String> headers = List.of("ID", "Employee ID", "Empleado",
"Horas Trabajadas", "Horas Pendientes", "Observaciones");
String selectedTeam = equipoComboBox.getValue().getName();
byte[] excelBytes = reportService.writeAsExcel("hours_worked_report",
headers, data, selectedTeam, weekNumber, currentYear);
StreamResource excelResource = new StreamResource("hours_worked_report.xlsx", StreamResource excelResource = new StreamResource("hours_worked_report.xlsx",
() -> new ByteArrayInputStream(excelBytes)); () -> new ByteArrayInputStream(excelBytes));
excelResource.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); if (downloadLink == null) {
excelResource.setCacheTime(0); downloadLink = new Anchor(excelResource, "Descargar Reporte en Excel");
if (downloadLink != null) {
remove(downloadLink);
}
downloadLink = new Anchor(excelResource, "Descargar Reporte de Horas Trabajadas");
downloadLink.getElement().setAttribute("download", true); downloadLink.getElement().setAttribute("download", true);
add(downloadLink); add(downloadLink);
} else {
Notification.show("Reporte de horas trabajadas generado exitosamente.", downloadLink.setHref(excelResource);
3000, Notification.Position.MIDDLE); }
} catch (Exception e) { } catch (Exception e) {
Notification.show("Error al generar el reporte de horas trabajadas. Inténtalo de nuevo.", Notification.show("Error al generar el reporte de horas trabajadas en Excel.",
3000, Notification.Position.MIDDLE); 3000, Notification.Position.MIDDLE);
e.printStackTrace();
} }
} }
private int getWeekOfYear(final LocalDate date) { private int getWeekOfYear(final LocalDate date) {
return date.get(WeekFields.of(Locale.getDefault()).weekOfWeekBasedYear()); return date.get(WeekFields.of(Locale.getDefault()).weekOfWeekBasedYear());
} }
public int getCurrentYear() {
return currentYear;
}
// Opcional: Si deseas permitir cambiar el año actual manualmente, agrega un setter
public void setCurrentYear(final int currentYear) {
this.currentYear = currentYear;
}
} }