diff --git a/Reporte_Vacaciones.xlsx b/Reporte_Vacaciones.xlsx new file mode 100644 index 0000000..8ce8134 Binary files /dev/null and b/Reporte_Vacaciones.xlsx differ diff --git a/src/main/bundles/prod.bundle b/src/main/bundles/prod.bundle index 2d36e74..b8a4d20 100644 Binary files a/src/main/bundles/prod.bundle and b/src/main/bundles/prod.bundle differ diff --git a/src/main/java/com/primefactorsolutions/model/DocumentType.java b/src/main/java/com/primefactorsolutions/model/DocumentType.java index 96c8ce3..10251fc 100644 --- a/src/main/java/com/primefactorsolutions/model/DocumentType.java +++ b/src/main/java/com/primefactorsolutions/model/DocumentType.java @@ -1,36 +1,35 @@ package com.primefactorsolutions.model; public enum DocumentType { - All, - ID_CARD, - PAY_STUB, - PAY_SLIPS, - EMPLOYMENT_CONTRACT, - WORK_CERTIFICATES, + TODOS, + CARNET_DE_IDENTIDAD, + RECIBOS_DE_PAGO, + CONTRATO_DE_TRABAJO, + CERTIFICADO_DE_TRABAJO, NDA, - MEMORANDUMS, - CONTRACT_APPROVAL_MTEPS, - BACKGROUND_CHECK_CERTIFICATE, - PRE_EMPLOYMENT_EVALUATION, - INSURANCE_REGISTRATION_FORM, - INSURANCE_CANCELLATION_FORM, - PROFESSIONAL_DEGREE_1, - PROFESSIONAL_CERTIFICATE_1, - PROFESSIONAL_DEGREE_2, - PROFESSIONAL_CERTIFICATE_2, - PROFESSIONAL_DEGREE_3, - PROFESSIONAL_CERTIFICATE_3, - GENERAL_LABOR_REGULATIONS, - REMOTE_WORK_GUIDELINES, - SAFETY_REGULATIONS, - HUMAN_RESOURCES_GUIDELINES, - ADMINISTRATION_FUNCTIONS_MANUAL, - ENGINEERING_FUNCTIONS_MANUAL, - GENERAL_LABOR_LAW, - SUPREME_DECREE, - REGULATORY_RESOLUTION, - COMPLEMENTARY_REGULATION, - HEALTH_SAFETY_LAW, - INTERNSHIP_RULES, - OTHER + MEMORÁNDUMS, + APROBACIÓN_DE_CONTRATO_MTEPS, + CERTIFICADO_DE_ANTECEDENTES, + EVALUACIÓN_PRE_EMPLEO, + FORMULARIO_DE_INSCRIPCIÓN_AL_SEGURO, + FORMULARIO_DE_CANCELACIÓN_DE_SEGURO, + TÍTULO_PROFESIONAL_1, + CERTIFICACIÓN_PROFESIONAL_1, + TÍTULO_PROFESIONAL_2, + CERTIFICACIÓN_PROFECIONAL_2, + TÍTULO_PROFESIONAL_3, + CERTIFICACIÓN_PROFECIONAL_3, + NORMATIVA_LABORAL_GENERAL, + NORMAS_DE_TRABAJO_REMOTO, + NORMAS_DE_SEGURIDAD, + INSTRUCTIVOS_DE_RECURSOS_HUMANOS, + MANUAL_DE_FUNCIONES_DE_ADMINISTRACIÓN, + MANUAL_DE_FUNCIONES_DE_INGENIERÍA, + LEY_GENERAL_DEL_TRABAJO, + DECRETOS_SUPREMOS, + RESOLUCIONES_O_DISPOSICIONES_REGLAMENTARIAS, + NORMATIVA_COMPLEMENTARIA, + LEY_GRAL_DE_HIGIENE_SALUD_SEGURIDAD_OCUPACIONAL_Y_BIENESTAR, + NORMATIVA_REGLAMENTARIA_PARA_DESARROLLO_DE_PASANTÍAS, + OTROS } diff --git a/src/main/java/com/primefactorsolutions/model/TimeOffRequestStatus.java b/src/main/java/com/primefactorsolutions/model/TimeOffRequestStatus.java index 6f0a2d0..3c8d1e2 100644 --- a/src/main/java/com/primefactorsolutions/model/TimeOffRequestStatus.java +++ b/src/main/java/com/primefactorsolutions/model/TimeOffRequestStatus.java @@ -10,7 +10,4 @@ public enum TimeOffRequestStatus { VENCIDO, SOLICITADO, - EN_REVISION, - COMPLETADO, - CANCELADO, } diff --git a/src/main/java/com/primefactorsolutions/views/DocumentView.java b/src/main/java/com/primefactorsolutions/views/DocumentView.java index 49c44a3..b1bbad9 100644 --- a/src/main/java/com/primefactorsolutions/views/DocumentView.java +++ b/src/main/java/com/primefactorsolutions/views/DocumentView.java @@ -39,9 +39,9 @@ import java.io.InputStream; @PageTitle("Document") @Route(value = "/documents/:documentId?/:action?", layout = MainLayout.class) public class DocumentView extends BeanValidationForm implements HasUrlParameter { - private final TextField fileName = new TextField("Document Name"); - private final ComboBox documentType = new ComboBox<>("Document Type"); - private final ComboBox employeeComboBox = new ComboBox<>("Employee"); + 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 MemoryBuffer buffer = new MemoryBuffer(); private final Upload uploadButton = new Upload(buffer); private final DocumentService documentService; @@ -68,19 +68,19 @@ public class DocumentView extends BeanValidationForm implements HasUrl } protected Button createSaveButton() { - saveButton = new Button("Save"); + saveButton = new Button("Guardar"); saveButton.addClickListener(event -> saveDocument()); return saveButton; } protected Button createCloseButton() { - Button closeButton = new Button("Close"); + Button closeButton = new Button("Salir"); closeButton.addClickListener(event -> closeForm()); return closeButton; } protected Button createViewDocumentButton() { - viewDocumentButton = new Button("View Document"); + viewDocumentButton = new Button("Ver documento"); viewDocumentButton.setEnabled(false); viewDocumentButton.addClickListener(event -> viewDocument()); return viewDocumentButton; @@ -130,7 +130,7 @@ public class DocumentView extends BeanValidationForm implements HasUrl ui.getPage().open(registration.getResourceUri().toString()); }); } catch (IOException e) { - Notification.show("Error reading file."); + Notification.show("Error al leer el archivo."); } } @@ -148,10 +148,10 @@ public class DocumentView extends BeanValidationForm implements HasUrl setDocumentCreator(document); documentService.saveDocument(document); - Notification.show("File saved successfully."); + Notification.show("Archivo guardado correctamente."); clearForm(); } else { - Notification.show("Save failed: Please complete all fields and upload a file."); + Notification.show("Error al guardar: Por favor, complete todos los campos y cargue un archivo."); } } @@ -179,7 +179,7 @@ public class DocumentView extends BeanValidationForm implements HasUrl try { return buffer.getInputStream().readAllBytes(); } catch (IOException e) { - Notification.show("Error reading file data."); + Notification.show("Error al leer los datos del archivo."); return new byte[0]; } } @@ -220,13 +220,13 @@ public class DocumentView extends BeanValidationForm implements HasUrl uploadButton.setAcceptedFileTypes(".pdf"); uploadButton.addSucceededListener(event -> { fileUploaded = true; - Notification.show("File uploaded successfully."); + Notification.show("Archivo cargado correctamente."); viewDocumentButton.setEnabled(true); updateSaveButtonState(); }); uploadButton.getElement().addEventListener("file-remove", event -> { fileUploaded = false; - Notification.show("File removed."); + Notification.show("Archivo eliminado."); viewDocumentButton.setEnabled(false); updateSaveButtonState(); }); diff --git a/src/main/java/com/primefactorsolutions/views/DocumentsListView.java b/src/main/java/com/primefactorsolutions/views/DocumentsListView.java index c034a6b..312a3f3 100644 --- a/src/main/java/com/primefactorsolutions/views/DocumentsListView.java +++ b/src/main/java/com/primefactorsolutions/views/DocumentsListView.java @@ -56,7 +56,7 @@ public class DocumentsListView extends BaseView { } private void initializeView() { - getCurrentPageLayout().add(createActionButton("Add Document", this::navigateToAddDocumentView)); + getCurrentPageLayout().add(createActionButton("Añadir documento", this::navigateToAddDocumentView)); final HorizontalLayout hl = new HorizontalLayout(); hl.add(createDocumentTypeFilter()); @@ -70,7 +70,10 @@ public class DocumentsListView extends BaseView { private void configureDocumentGrid() { documentGrid.setColumns("fileName", "documentType", "creator"); - documentGrid.addComponentColumn(this::createEmployeeSpan).setHeader("Employee"); + documentGrid.getColumnByKey("fileName").setHeader("Nombre archivo"); + documentGrid.getColumnByKey("documentType").setHeader("Tipo"); + documentGrid.getColumnByKey("creator").setHeader("Creador"); + documentGrid.addComponentColumn(this::createEmployeeSpan).setHeader("Empleado"); addActionColumns(); configurePagination(); } @@ -85,13 +88,13 @@ public class DocumentsListView extends BaseView { documentGrid.addComponentColumn((ValueProvider) document -> { final MenuBar menuBar = new MenuBar(); menuBar.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE); - final MenuItem viewItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.EYE, "View"); + final MenuItem viewItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.EYE, "Ver"); viewItem.addClickListener((ComponentEventListener>) menuItemClickEvent -> navigateToDocumentView(document)); - final MenuItem editItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.PENCIL, "Edit"); + final MenuItem editItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.PENCIL, "Editar"); editItem.addClickListener((ComponentEventListener>) menuItemClickEvent -> navigateToEditDocumentView(document)); - final MenuItem downloadItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.DOWNLOAD, "Download"); + final MenuItem downloadItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.DOWNLOAD, "Descargar"); downloadItem.addClickListener((ComponentEventListener>) menuItemClickEvent -> downloadDocument(document)); return menuBar; @@ -105,7 +108,7 @@ public class DocumentsListView extends BaseView { } private ComboBox createDocumentTypeFilter() { - documentTypeFilter = new ComboBox<>("Document Type"); + documentTypeFilter = new ComboBox<>("Tipo de documento"); documentTypeFilter.setItems(DocumentType.values()); documentTypeFilter.setValue(DocumentType.values()[0]); documentTypeFilter.addValueChangeListener(event -> { @@ -115,7 +118,7 @@ public class DocumentsListView extends BaseView { } private ComboBox createEmployeeFilter() { - employeeFilter = new ComboBox<>("Employee"); + employeeFilter = new ComboBox<>("Empleado"); List employees = employeeService.findAllEmployees(); employees.addFirst(createAllEmployeesOption()); employeeFilter.setItems(employees); @@ -129,12 +132,13 @@ public class DocumentsListView extends BaseView { private Employee createAllEmployeesOption() { Employee allEmployeesOption = new Employee(); - allEmployeesOption.setFirstName("All"); + allEmployeesOption.setFirstName("TODOS"); return allEmployeesOption; } private String getEmployeeLabel(final Employee employee) { - return employee.getFirstName().equals("All") ? "All" : employee.getFirstName() + " " + employee.getLastName(); + return employee.getFirstName().equals("TODOS") + ? "TODOS" : employee.getFirstName() + " " + employee.getLastName(); } private void navigateToEditDocumentView(final Document document) { diff --git a/src/main/java/com/primefactorsolutions/views/RequestEmployeeView.java b/src/main/java/com/primefactorsolutions/views/RequestEmployeeView.java index d97697c..15c207d 100644 --- a/src/main/java/com/primefactorsolutions/views/RequestEmployeeView.java +++ b/src/main/java/com/primefactorsolutions/views/RequestEmployeeView.java @@ -7,6 +7,8 @@ import com.primefactorsolutions.service.VacationService; 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.html.H3; @@ -21,11 +23,21 @@ import com.vaadin.flow.router.BeforeEvent; import com.vaadin.flow.router.HasUrlParameter; 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.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.apache.pdfbox.pdmodel.font.PDType1Font; import org.springframework.context.annotation.Scope; import org.vaadin.firitin.components.grid.PagingGrid; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UncheckedIOException; import java.time.LocalDate; import java.time.Period; import java.util.*; @@ -60,6 +72,8 @@ public class RequestEmployeeView extends BaseView implements HasUrlParameter downloadReport()); + getCurrentPageLayout().add(downloadButton); setupFilters(); setupGrid(); getCurrentPageLayout().add(requestGrid, new H3("Balance"), createSummaryLayout()); @@ -141,43 +155,47 @@ public class RequestEmployeeView extends BaseView implements HasUrlParameter vacations = vacationService.findVacations(); - double healthLicence = 2; - List healthRequests = requestService - .findByEmployeeAndCategory(employeeId, TimeOffRequestType.PERMISOS_DE_SALUD); - if (healthRequests != null && !healthRequests.isEmpty()) { - healthLicence = healthRequests.getLast().getDaysBalance(); - } + double healthLicence = getHealthLicence(employeeId); double totalFixedAndMovableHolidays = calculateHolidayDays(vacations); double totalPersonalDays = calculatePersonalDays(vacations, isMale); List vacationDays = calculateVacationDays(employee); - double utilizedVacationCurrentDays = vacationDays.get(1); - List vacationCurrentRequests = requestService - .findByEmployeeAndCategory(employeeId, TimeOffRequestType.VACACION_GESTION_ACTUAL); - if (vacationCurrentRequests != null && !vacationCurrentRequests.isEmpty()) { - utilizedVacationCurrentDays = vacationCurrentRequests.getLast().getDaysBalance(); - } - double totalVacationCurrentDays = vacationDays.get(1) - (vacationDays.get(1) - utilizedVacationCurrentDays); - - double utilizedVacationPreviousDays = vacationDays.get(0); - List vacationPreviousRequests = requestService - .findByEmployeeAndCategory(employeeId, TimeOffRequestType.VACACION_GESTION_ANTERIOR); - if (vacationPreviousRequests != null && !vacationPreviousRequests.isEmpty()) { - utilizedVacationPreviousDays = vacationPreviousRequests.getLast().getDaysBalance(); - } - double totalVacationPreviousDays = vacationDays.getFirst() - - (vacationDays.getFirst() - utilizedVacationPreviousDays); + double totalVacationCurrentDays = calculateUtilizedVacationDays( + vacationDays.get(1), + TimeOffRequestType.VACACION_GESTION_ACTUAL + ); + double totalVacationPreviousDays = calculateUtilizedVacationDays( + vacationDays.get(0), + TimeOffRequestType.VACACION_GESTION_ANTERIOR + ); double utilizedFixedAndMovableHolidays = calculateHolidayUtilizedDays(currentYear); double utilizedPersonalDays = calculatePersonalDaysUtilized(isMale, currentYear); - double remainingHolidayDays = totalFixedAndMovableHolidays - utilizedFixedAndMovableHolidays; - double remainingPersonalDays = (totalPersonalDays - utilizedPersonalDays) + healthLicence; - double remainingVacationDays = totalVacationCurrentDays + totalVacationPreviousDays; + double remainingHolidayDays = calculateRemainingHolidayDays( + totalFixedAndMovableHolidays, + utilizedFixedAndMovableHolidays, + employee.getDateOfExit(), + currentDate + ); + double remainingPersonalDays = calculateRemainingPersonalDays( + totalPersonalDays, + utilizedPersonalDays, + healthLicence, + employee.getDateOfExit(), + currentDate + ); + double remainingVacationDays = calculateRemainingVacationDays( + totalVacationCurrentDays, + totalVacationPreviousDays, + employee.getDateOfExit(), + currentDate + ); double totalAvailableDays = remainingHolidayDays + remainingPersonalDays + remainingVacationDays; @@ -189,6 +207,51 @@ public class RequestEmployeeView extends BaseView implements HasUrlParameter healthRequests = requestService + .findByEmployeeAndCategory(employeeId, TimeOffRequestType.PERMISOS_DE_SALUD); + return healthRequests != null && !healthRequests.isEmpty() ? healthRequests.getLast().getDaysBalance() : 2; + } + + private double calculateUtilizedVacationDays(final double vacationDays, final TimeOffRequestType requestType) { + List vacationRequests = requestService.findByEmployeeAndCategory(employeeId, requestType); + if (vacationRequests != null && !vacationRequests.isEmpty()) { + return vacationRequests.getLast().getDaysBalance(); + } + return vacationDays; + } + + private double calculateRemainingVacationDays(final double totalVacationCurrentDays, + final double totalVacationPreviousDays, + final LocalDate exitDate, + final LocalDate currentDate) { + if (exitDate == null || exitDate.isAfter(currentDate)) { + return totalVacationCurrentDays + totalVacationPreviousDays; + } + return 0; + } + + private double calculateRemainingHolidayDays(final double totalFixedAndMovableHolidays, + final double utilizedFixedAndMovableHolidays, + final LocalDate exitDate, + final LocalDate currentDate) { + if (exitDate == null || exitDate.isAfter(currentDate)) { + return totalFixedAndMovableHolidays - utilizedFixedAndMovableHolidays; + } + return 0; + } + + private double calculateRemainingPersonalDays(final double totalPersonalDays, + final double utilizedPersonalDays, + final double healthLicence, + final LocalDate exitDate, + final LocalDate currentDate) { + if (exitDate == null || exitDate.isAfter(currentDate)) { + return (totalPersonalDays - utilizedPersonalDays) + healthLicence; + } + return 0; + } + private double calculateHolidayDays(final List vacations) { return vacations.stream() .filter(req -> req.getType() != Vacation.Type.OTHER) @@ -257,7 +320,8 @@ public class RequestEmployeeView extends BaseView implements HasUrlParameter req.getState() == TimeOffRequestStatus.TOMADO) + .filter(req -> req.getState() == TimeOffRequestStatus.TOMADO + || req.getState() == TimeOffRequestStatus.APROBADO) .filter(req -> getStartDateYear(req) == year) .mapToDouble(TimeOffRequest::getDaysToBeTake) .sum(); @@ -266,7 +330,8 @@ public class RequestEmployeeView extends BaseView implements HasUrlParameter !verificationIsHoliday(req)) - .filter(req -> req.getState() == TimeOffRequestStatus.TOMADO) + .filter(req -> req.getState() == TimeOffRequestStatus.TOMADO + || req.getState() == TimeOffRequestStatus.APROBADO) .filter(req -> !getStandardExclusions().contains(req.getCategory())) .filter(req -> !(isMale && getMaleSpecificExclusions().contains(req.getCategory()))) .filter(req -> !req.getCategory().name().startsWith("VACACION")) @@ -302,10 +367,6 @@ public class RequestEmployeeView extends BaseView implements HasUrlParameter ui.navigate(RequestsListView.class)); - } - private void navigateToEditRequest(final TimeOffRequest request) { navigateToRequestView(request, "edit"); } @@ -399,17 +460,164 @@ public class RequestEmployeeView extends BaseView implements HasUrlParameter requests, final int columnIndex) { + float maxWidth = header.length(); + for (TimeOffRequest request : requests) { + String value = switch (columnIndex) { + case 0 -> request.getCategory().name(); + case 1 -> request.getState().name(); + case 2 -> getDateString(request.getStartDate()); + case 3 -> getDateString(request.getEndDate()); + case 4 -> String.valueOf(request.getDaysToBeTake()); + default -> ""; + }; + if (value != null) { + maxWidth = Math.max(maxWidth, value.length()); + } + } + return maxWidth * 7; + } + + private StreamResource generateVacationReport() { + Employee employee = employeeService.getEmployee(employeeId); + String fileName = String.format("%s_%s-reporte_de_vacaciones_%s.pdf", + employee.getFirstName(), + employee.getLastName(), + LocalDate.now()); + + ByteArrayInputStream pdfStream = generatePdfReport(); + + return new StreamResource(fileName, () -> pdfStream) + .setContentType("application/pdf") + .setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); + } + + private void downloadReport() { + StreamResource resource = generateVacationReport(); + getUI().ifPresent(ui -> openDocumentStream(resource, ui)); + } + + private void openDocumentStream(final StreamResource resource, final UI ui) { + StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource); + ui.getPage().open(registration.getResourceUri().toString()); + } + @Override public void setParameter(final BeforeEvent event, final String parameter) { employeeId = UUID.fromString(parameter); Employee employee = employeeService.getEmployee(employeeId); requests = requestService.findRequestsByEmployeeId(employeeId); - setViewTitle(employee.getFirstName() + " " + employee.getLastName(), employee.getTeam().getName()); + setViewTitle( + employee.getFirstName() + " " + employee.getLastName(), + employee.getTeam().getName(), + employee.getDateOfExit() + ); requestGrid.setItems(requests); initializeView(); } - private void setViewTitle(final String employeeName, final String employeeTeam) { - getCurrentPageLayout().addComponentAsFirst(new H3(String.format("%s (%s)", employeeName, employeeTeam))); + private void setViewTitle(final String employeeName, final String employeeTeam, final LocalDate dateOfExit) { + addComponentAsFirst(new H3("Nombre del empleado: " + employeeName)); + addComponentAtIndex(1, new H3("Equipo: " + employeeTeam)); + if (dateOfExit != null) { + addComponentAtIndex(2, new H3("Descontado a cero en fecha " + dateOfExit + " por pago de finiquito.")); + } } } diff --git a/src/main/java/com/primefactorsolutions/views/RequestRegisterView.java b/src/main/java/com/primefactorsolutions/views/RequestRegisterView.java index 4d56f10..4f5ae22 100644 --- a/src/main/java/com/primefactorsolutions/views/RequestRegisterView.java +++ b/src/main/java/com/primefactorsolutions/views/RequestRegisterView.java @@ -19,6 +19,7 @@ import jakarta.annotation.security.PermitAll; import org.springframework.context.annotation.Scope; import java.time.LocalDate; +import java.time.DayOfWeek; import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Comparator; @@ -91,8 +92,25 @@ public class RequestRegisterView extends VerticalLayout { onCategoryChange(event.getValue()); handleCategorySelection(event.getValue()); }); - startDatePicker.addValueChangeListener(event -> updateDatePickerMinValues()); - endDatePicker.addValueChangeListener(event -> calculateDays()); + startDatePicker.addValueChangeListener(event -> { + LocalDate selectedDate = event.getValue(); + if (selectedDate != null && (selectedDate.getDayOfWeek().getValue() == 6 + || selectedDate.getDayOfWeek().getValue() == 7)) { + startDatePicker.setValue(selectedDate.minusDays(1)); + } + updateDatePickerMinValues(); + }); + endDatePicker.addValueChangeListener(event -> { + if (startDatePicker.getValue() != null) { + endDatePicker.setMin(startDatePicker.getValue()); + } + LocalDate selectedDate = event.getValue(); + if (selectedDate != null && (selectedDate.getDayOfWeek().getValue() == 6 + || selectedDate.getDayOfWeek().getValue() == 7)) { + endDatePicker.setValue(selectedDate.minusDays(1)); + } + calculateDays(); + }); } private void configureBinder() { @@ -167,7 +185,7 @@ public class RequestRegisterView extends VerticalLayout { private void onCategoryChange(final TimeOffRequestType selectedCategory) { if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ACTUAL - || selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) { + || selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) { startDatePicker.setEnabled(true); endDatePicker.setEnabled(true); } else { @@ -178,6 +196,10 @@ public class RequestRegisterView extends VerticalLayout { private boolean isCategoryAvailable(final List employeeRequests, final TimeOffRequestType category) { + if (category == TimeOffRequestType.CUMPLEAÑOS && employee.getBirthday() == null) { + return false; + } + List requestsByCategory = employeeRequests.stream() .filter(request -> request.getCategory() == category) .toList(); @@ -199,8 +221,8 @@ public class RequestRegisterView extends VerticalLayout { && latestRequest.getDaysBalance() > 0) || latestRequest.getState() == TimeOffRequestStatus.RECHAZADO || (latestRequest.getState() == TimeOffRequestStatus.TOMADO - && latestRequest.getDaysBalance() == 0 - && latestRequest.getExpiration().isBefore(LocalDate.now())); + && latestRequest.getDaysBalance() == 0 + && latestRequest.getExpiration().isBefore(LocalDate.now())); } else { return (latestRequest.getState() == TimeOffRequestStatus.TOMADO && latestRequest.getExpiration().isBefore(LocalDate.now())) @@ -375,6 +397,13 @@ public class RequestRegisterView extends VerticalLayout { Double availableDays = availableDaysField.getValue(); if (areDatesValid(startDate, endDate)) { + long workDays = countWorkDaysBetween(startDate, endDate); + + daysToBeTakenField.setValue((double) workDays); + + balanceDaysField.setValue(availableDaysField.getValue() - workDays); + + double daysToBeTaken = calculateDaysBetween(startDate, endDate); setDaysToBeTakenField(daysToBeTaken); @@ -384,6 +413,12 @@ public class RequestRegisterView extends VerticalLayout { if (balanceDays < 0.0) { clearFields(); } + + if (daysToBeTakenField.getValue() > 10 + && (categoryComboBox.getValue() == TimeOffRequestType.VACACION_GESTION_ANTERIOR + || categoryComboBox.getValue() == TimeOffRequestType.VACACION_GESTION_ACTUAL)) { + clearFields(); + } } } @@ -391,8 +426,19 @@ public class RequestRegisterView extends VerticalLayout { return startDate != null && endDate != null; } + private long countWorkDaysBetween(final LocalDate startDate, final LocalDate endDate) { + return startDate.datesUntil(endDate.plusDays(1)) + .filter(date -> date.getDayOfWeek() != DayOfWeek.SATURDAY && date.getDayOfWeek() != DayOfWeek.SUNDAY) + .count(); + } + private double calculateDaysBetween(final LocalDate startDate, final LocalDate endDate) { - return java.time.temporal.ChronoUnit.DAYS.between(startDate, endDate) + 1; + return startDate.datesUntil(endDate.plusDays(1)) + .filter(date -> { + DayOfWeek day = date.getDayOfWeek(); + return day != DayOfWeek.SATURDAY && day != DayOfWeek.SUNDAY; + }) + .count(); } private void setDaysToBeTakenField(final double daysToBeTaken) { @@ -456,9 +502,16 @@ public class RequestRegisterView extends VerticalLayout { handleExistingRequests(request); } - requestService.saveTimeOffRequest(request); - Notification.show("Solicitud guardada correctamente."); - closeForm(); + long differentDays = ChronoUnit.DAYS.between(LocalDate.now(), request.getStartDate()); + if (differentDays >= -15 && differentDays <= 90) { + requestService.saveTimeOffRequest(request); + Notification.show("Solicitud guardada correctamente."); + closeForm(); + } else { + Notification.show( + "La fecha de inicio debe encontrarse dentro del rango de 15 días a 3 meses de anticipación." + ); + } } private TimeOffRequest prepareRequest() { diff --git a/src/main/java/com/primefactorsolutions/views/RequestsListView.java b/src/main/java/com/primefactorsolutions/views/RequestsListView.java index a2daf3d..1c9c1d3 100644 --- a/src/main/java/com/primefactorsolutions/views/RequestsListView.java +++ b/src/main/java/com/primefactorsolutions/views/RequestsListView.java @@ -8,6 +8,8 @@ import com.primefactorsolutions.service.VacationService; 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,19 @@ 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.*; +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.time.Period; import java.util.*; @@ -61,6 +71,8 @@ public class RequestsListView extends BaseView { private void initializeView() { requestService.updateRequestStatuses(); + Button downloadButton = new Button("Descargar reporte", event -> downloadReport()); + getCurrentPageLayout().add(downloadButton); setupFilters(); setupRequestGrid(); getCurrentPageLayout().add(requestGrid); @@ -71,7 +83,6 @@ public class RequestsListView extends BaseView { hl.add(createEmployeeFilter()); hl.add(createTeamFilter()); hl.add(createStateFilter()); - getCurrentPageLayout().add(hl); } @@ -154,7 +165,7 @@ public class RequestsListView extends BaseView { private String getEmployeeStatus(final Employee employee) { Optional activeRequest = requestService .findByEmployeeAndState(employee.getId(), TimeOffRequestStatus.EN_USO); - return activeRequest.isPresent() ? "EN_DESCANSO" : "ACTIVO"; + return activeRequest.isPresent() ? "EN_DESCANSO" : "EN_FUNCIONES"; } private String getGeneralTotal(final Employee employee) { @@ -186,6 +197,13 @@ public class RequestsListView extends BaseView { double totalAvailable = calculateTotalAvailable(vacations, employeeRequests, employee); double generalTotal = totalAvailable + totalVacations - totalUtilized; + + if (employee.getDateOfExit() != null + && (employee.getDateOfExit().isBefore(LocalDate.now()) + || employee.getDateOfExit().isEqual(LocalDate.now()))) { + generalTotal = 0; + } + return String.valueOf(generalTotal); } @@ -212,6 +230,8 @@ public class RequestsListView extends BaseView { int currentYear = LocalDate.now().getYear(); return employeeRequests.stream() .filter(Objects::nonNull) + .filter(request -> request.getState() == TimeOffRequestStatus.APROBADO + || request.getState() == TimeOffRequestStatus.TOMADO) .filter(request -> request.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD) .filter(request -> request.getCategory() != TimeOffRequestType.VACACION_GESTION_ACTUAL) .filter(request -> request.getCategory() != TimeOffRequestType.VACACION_GESTION_ANTERIOR) @@ -383,7 +403,7 @@ public class RequestsListView extends BaseView { private enum Status { TODOS, EN_DESCANSO, - ACTIVO + EN_FUNCIONES } private Employee createAllEmployeesOption() { @@ -405,4 +425,50 @@ public class RequestsListView extends BaseView { private void navigateToTimeOffRequestView(final UUID idEmployee) { getUI().ifPresent(ui -> ui.navigate("requests/" + idEmployee.toString())); } + + private ByteArrayInputStream generateExcelReport(final List employees) { + try (Workbook workbook = new XSSFWorkbook()) { + Sheet sheet = workbook.createSheet("REPORTE_GENERAL_DE_VACACIONES"); + Row headerRow = sheet.createRow(0); + + String[] headers = {"Empleado", "Equipo", "Estado", "Total Horas"}; + for (int i = 0; i < headers.length; i++) { + Cell cell = headerRow.createCell(i); + cell.setCellValue(headers[i]); + } + + int rowIndex = 1; + for (Employee employee : employees) { + Row row = sheet.createRow(rowIndex++); + row.createCell(0).setCellValue(getEmployeeFullName(employee)); + row.createCell(1).setCellValue(getTeamName(employee)); + row.createCell(2).setCellValue(getEmployeeStatus(employee)); + row.createCell(3).setCellValue(getGeneralTotal(employee)); + } + + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + workbook.write(out); + return new ByteArrayInputStream(out.toByteArray()); + } + } catch (IOException e) { + throw new UncheckedIOException("Error al generar el archivo Excel", e); + } + } + + private StreamResource generateGeneralVacationReport() { + List employees = employeeService.findAllEmployees(); + ByteArrayInputStream excelStream = generateExcelReport(employees); + return new StreamResource("reporte_general_de_vacaciones_" + LocalDate.now() + ".xlsx", + () -> excelStream); + } + + private void downloadReport() { + StreamResource resource = generateGeneralVacationReport(); + getUI().ifPresent(ui -> openDocumentStream(resource, ui)); + } + + private void openDocumentStream(final StreamResource resource, final UI ui) { + StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource); + ui.getPage().open(registration.getResourceUri().toString()); + } } \ No newline at end of file