diff --git a/src/main/java/com/primefactorsolutions/service/EmployeeService.java b/src/main/java/com/primefactorsolutions/service/EmployeeService.java index f426d01..7a4f4de 100644 --- a/src/main/java/com/primefactorsolutions/service/EmployeeService.java +++ b/src/main/java/com/primefactorsolutions/service/EmployeeService.java @@ -62,6 +62,7 @@ public class EmployeeService { public List findEmployees( final int start, final int pageSize, final String sortProperty, final boolean asc) { List employees = employeeRepository.findAll(); + Collections.reverse(employees); int end = Math.min(start + pageSize, employees.size()); employees.sort(new BeanComparator<>(sortProperty)); @@ -75,6 +76,7 @@ public class EmployeeService { public List findEmployees(final int start, final int pageSize) { List employees = employeeRepository.findAll(); + Collections.reverse(employees); int end = Math.min(start + pageSize, employees.size()); return employees.subList(start, end); @@ -124,7 +126,9 @@ public class EmployeeService { } public List findAllEmployees() { - return employeeRepository.findAll(); + List employees = employeeRepository.findAll(); + Collections.reverse(employees); + return employees; } public List findEmployeesByTeam(final String teamName) { diff --git a/src/main/java/com/primefactorsolutions/views/DocumentView.java b/src/main/java/com/primefactorsolutions/views/DocumentView.java index b1bbad9..3e738fd 100644 --- a/src/main/java/com/primefactorsolutions/views/DocumentView.java +++ b/src/main/java/com/primefactorsolutions/views/DocumentView.java @@ -8,6 +8,7 @@ import com.primefactorsolutions.service.EmployeeService; import com.vaadin.flow.component.Component; import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.combobox.ComboBox; +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.textfield.TextField; @@ -42,6 +43,7 @@ public class DocumentView extends BeanValidationForm implements HasUrl private final TextField fileName = new TextField("Nombre del documento"); private final ComboBox documentType = new ComboBox<>("Tipo de documento"); private final ComboBox employeeComboBox = new ComboBox<>("Empleado"); + private final Span pdfOnlyMessage = new Span("Únicamente se permite la carga de archivos en formato PDF."); private final MemoryBuffer buffer = new MemoryBuffer(); private final Upload uploadButton = new Upload(buffer); private final DocumentService documentService; @@ -51,7 +53,6 @@ public class DocumentView extends BeanValidationForm implements HasUrl private Button saveButton; private Button viewDocumentButton; - public DocumentView(final DocumentService documentService, final EmployeeService employeeService, final AuthenticationContext authContext) { @@ -268,6 +269,6 @@ public class DocumentView extends BeanValidationForm implements HasUrl HorizontalLayout buttonLayout = new HorizontalLayout(); buttonLayout.add(uploadButton, createViewDocumentButton()); buttonLayout.setSpacing(true); - return List.of(fileName, documentType, employeeComboBox, buttonLayout, createCloseButton()); + return List.of(fileName, documentType, employeeComboBox, pdfOnlyMessage, buttonLayout, createCloseButton()); } } diff --git a/src/main/java/com/primefactorsolutions/views/PendingRequestsListView.java b/src/main/java/com/primefactorsolutions/views/PendingRequestsListView.java index cc74f94..abb096a 100644 --- a/src/main/java/com/primefactorsolutions/views/PendingRequestsListView.java +++ b/src/main/java/com/primefactorsolutions/views/PendingRequestsListView.java @@ -8,6 +8,8 @@ import com.primefactorsolutions.views.util.MenuBarUtils; import com.vaadin.flow.component.ClickEvent; import com.vaadin.flow.component.Component; import com.vaadin.flow.component.ComponentEventListener; +import com.vaadin.flow.component.UI; +import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.contextmenu.MenuItem; import com.vaadin.flow.component.icon.VaadinIcon; @@ -17,11 +19,23 @@ import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.function.ValueProvider; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; +import com.vaadin.flow.server.StreamRegistration; +import com.vaadin.flow.server.StreamResource; import com.vaadin.flow.spring.annotation.SpringComponent; import jakarta.annotation.security.PermitAll; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.context.annotation.Scope; import org.vaadin.firitin.components.grid.PagingGrid; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.time.LocalDate; import java.util.*; import java.util.stream.Collectors; @@ -54,6 +68,8 @@ public class PendingRequestsListView extends BaseView { } private void initializeView() { + Button downloadButton = new Button("Descargar reporte de rechazos", event -> downloadReport()); + getCurrentPageLayout().add(downloadButton); setupFilters(); setupPendingRequestsGrid(); } @@ -221,4 +237,50 @@ public class PendingRequestsListView extends BaseView { allTeamsOption.setName("TODOS"); return allTeamsOption; } + + private void downloadReport() { + StreamResource resource = generateGeneralVacationReport(); + getUI().ifPresent(ui -> openDocumentStream(resource, ui)); + } + + private StreamResource generateGeneralVacationReport() { + List requests = requestService.findAllTimeOffRequests().stream() + .filter(request -> request.getState() == TimeOffRequestStatus.RECHAZADO) + .collect(Collectors.toList()); + ByteArrayInputStream excelStream = generateExcelReport(requests); + return new StreamResource("reporte_de_solicitudes_rechazadas_" + LocalDate.now() + ".xlsx", + () -> excelStream); + } + + private void openDocumentStream(final StreamResource resource, final UI ui) { + StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource); + ui.getPage().open(registration.getResourceUri().toString()); + } + + private ByteArrayInputStream generateExcelReport(final List requests) { + try (Workbook workbook = new XSSFWorkbook()) { + Sheet sheet = workbook.createSheet("REPORTE_DE_SOLICITUDES_DE_VACACIONES_RECHAZADAS"); + Row headerRow = sheet.createRow(0); + + String[] headers = {"Empleado", "Categoria", "Observaciones"}; + for (int i = 0; i < headers.length; i++) { + Cell cell = headerRow.createCell(i); + cell.setCellValue(headers[i]); + } + + int rowIndex = 1; + for (TimeOffRequest request : requests) { + Row row = sheet.createRow(rowIndex++); + row.createCell(0).setCellValue(getEmployeeFullName(request)); + row.createCell(1).setCellValue(getCategory(request)); + } + + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + workbook.write(out); + return new ByteArrayInputStream(out.toByteArray()); + } + } catch (IOException e) { + throw new UncheckedIOException("Error al generar el archivo Excel", e); + } + } } \ No newline at end of file diff --git a/src/main/java/com/primefactorsolutions/views/RequestEmployeeView.java b/src/main/java/com/primefactorsolutions/views/RequestEmployeeView.java index 15c207d..b0a5d07 100644 --- a/src/main/java/com/primefactorsolutions/views/RequestEmployeeView.java +++ b/src/main/java/com/primefactorsolutions/views/RequestEmployeeView.java @@ -40,6 +40,7 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.time.LocalDate; import java.time.Period; +import java.time.Year; import java.util.*; import java.util.stream.Collectors; @@ -61,6 +62,10 @@ public class RequestEmployeeView extends BaseView implements HasUrlParameter categoryFilter; private ComboBox stateFilter; private UUID employeeId; + private double remainingHolidayDays; + private double remainingPersonalDays; + private double remainingVacationDays; + public RequestEmployeeView(final TimeOffRequestService requestService, final EmployeeService employeeService, @@ -177,20 +182,20 @@ public class RequestEmployeeView extends BaseView implements HasUrlParameter filteredRequests = requests.stream() + .filter(request -> + (request.getStartDate() == null || Year.from(request.getStartDate()).equals(Year.now())) + || request.getCategory() == TimeOffRequestType.VACACION_GESTION_ACTUAL + || request.getCategory() == TimeOffRequestType.VACACION_GESTION_ANTERIOR) + .toList(); PDPageContentStream contentStream = null; try { @@ -513,7 +525,7 @@ public class RequestEmployeeView extends BaseView implements HasUrlParameter employeeFilter; private ComboBox teamFilter; + private ComboBox categoryFilter; private ComboBox stateFilter; public RequestsListView(final TimeOffRequestService requestService, @@ -66,7 +67,7 @@ public class RequestsListView extends BaseView { this.teamService = teamService; this.vacationService = vacationService; initializeView(); - refreshGeneralRequestGrid(null, null, null); + refreshGeneralRequestGrid(null, null, null, null); } private void initializeView() { @@ -82,6 +83,7 @@ public class RequestsListView extends BaseView { final HorizontalLayout hl = new HorizontalLayout(); hl.add(createEmployeeFilter()); hl.add(createTeamFilter()); + hl.add(createCategoryFilter()); hl.add(createStateFilter()); getCurrentPageLayout().add(hl); } @@ -90,6 +92,7 @@ public class RequestsListView extends BaseView { requestGrid.addColumn(this::getEmployeeFullName).setHeader("Empleado"); requestGrid.addColumn(this::getTeamName).setHeader("Equipo"); requestGrid.addColumn(this::getEmployeeStatus).setHeader("Estado del empleado"); + requestGrid.addColumn(this::getCategory).setHeader("Categoria"); requestGrid.addColumn(this::getGeneralTotal).setHeader("Total general"); requestGrid.addComponentColumn((ValueProvider) employee -> { final MenuBar menuBar = new MenuBar(); @@ -107,10 +110,11 @@ public class RequestsListView extends BaseView { private void refreshGeneralRequestGrid(final Employee employee, final Team team, + final TimeOffRequestType category, final Status state) { requestGrid.setPagingDataProvider((page, pageSize) -> { int start = (int) (page * requestGrid.getPageSize()); - return fetchFilteredEmployees(start, pageSize, employee, team, state); + return fetchFilteredEmployees(start, pageSize, employee, team, category, state); }); requestGrid.getDataProvider().refreshAll(); } @@ -119,6 +123,7 @@ public class RequestsListView extends BaseView { final int pageSize, final Employee employee, final Team team, + final TimeOffRequestType category, final Status state) { List filteredEmployees = employeeService.findAllEmployees(); @@ -134,6 +139,16 @@ public class RequestsListView extends BaseView { .collect(Collectors.toList()); } + if (category != null && category != TimeOffRequestType.values()[0]) { + filteredEmployees = filteredEmployees.stream() + .filter(emp -> { + Optional request = requestService + .findByEmployeeAndState(emp.getId(), TimeOffRequestStatus.EN_USO); + return request.isPresent() && request.get().getCategory() == category; + }) + .collect(Collectors.toList()); + } + if (state != null && state != Status.TODOS) { filteredEmployees = filteredEmployees.stream() .filter(emp -> { @@ -168,6 +183,12 @@ public class RequestsListView extends BaseView { return activeRequest.isPresent() ? "EN_DESCANSO" : "EN_FUNCIONES"; } + private String getCategory(final Employee employee) { + Optional activeRequest = requestService + .findByEmployeeAndState(employee.getId(), TimeOffRequestStatus.EN_USO); + return activeRequest.map(request -> request.getCategory().toString()).orElse(""); + } + private String getGeneralTotal(final Employee employee) { List employeeRequests = requestService.findRequestsByEmployeeId(employee.getId()); List vacations = vacationService.findVacations(); @@ -363,6 +384,7 @@ public class RequestsListView extends BaseView { refreshGeneralRequestGrid( event.getValue(), teamFilter.getValue(), + categoryFilter.getValue(), stateFilter.getValue() ) ); @@ -380,12 +402,28 @@ public class RequestsListView extends BaseView { refreshGeneralRequestGrid( employeeFilter.getValue(), event.getValue(), + categoryFilter.getValue(), stateFilter.getValue() ) ); return teamFilter; } + private ComboBox createCategoryFilter() { + categoryFilter = new ComboBox<>("Category"); + categoryFilter.setItems(TimeOffRequestType.values()); + categoryFilter.setValue(TimeOffRequestType.values()[0]); + categoryFilter.addValueChangeListener(event -> + refreshGeneralRequestGrid( + employeeFilter.getValue(), + teamFilter.getValue(), + event.getValue(), + stateFilter.getValue() + ) + ); + return categoryFilter; + } + private ComboBox createStateFilter() { stateFilter = new ComboBox<>("Estado del empleado"); stateFilter.setItems(Status.values()); @@ -394,6 +432,7 @@ public class RequestsListView extends BaseView { refreshGeneralRequestGrid( employeeFilter.getValue(), teamFilter.getValue(), + categoryFilter.getValue(), event.getValue() ) ); diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 5cb7a98..a7e185b 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -78,7 +78,7 @@ values ('9c6f12ba-e333-4e7a-b8a6-caa0982bd8c3', 1, '5c6f11fe-c341-4be7-a9a6-bba0 insert into time_off_request (id, version, employee_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance) values ('9d6f12ba-e444-4e7a-b8a6-caa0982bd8d4', 1, '5c6f11fe-c341-4be7-a9a6-bba0081ad7c6', 'VIERNES_SANTO', 'APROBADO', 1, '2024-03-29', '2024-03-29', '2024-03-29', 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 ('9e6f12ba-e555-4e7a-b8a6-caa0982bd8e5', 1, '5c6f11fe-c341-4be7-a9a6-bba0081ad7c6', 'VACACION_GESTION_ANTERIOR', 'APROBADO', 20, '2024-11-01', '2022-11-01', '2022-11-30', 20, 0); +values ('9e6f12ba-e555-4e7a-b8a6-caa0982bd8e5', 1, '5c6f11fe-c341-4be7-a9a6-bba0081ad7c6', 'VACACION_GESTION_ANTERIOR', 'APROBADO', 20, '2024-11-01', '2023-11-01', '2023-11-30', 20, 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 ('8c653f2a-f9a3-4d67-b3b6-12ad98fe0983', 1, 'f6ab3c6d-7078-45f6-9b22-4e37637bfec6', 'DIA_DEL_TRABAJADOR', 'APROBADO', 1, '2024-05-01', '2024-05-01', '2024-05-01', 1, 0);