diff --git a/src/main/bundles/prod.bundle b/src/main/bundles/prod.bundle index f83753b..a93fb73 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 10251fc..df7ab94 100644 --- a/src/main/java/com/primefactorsolutions/model/DocumentType.java +++ b/src/main/java/com/primefactorsolutions/model/DocumentType.java @@ -1,7 +1,6 @@ package com.primefactorsolutions.model; public enum DocumentType { - TODOS, CARNET_DE_IDENTIDAD, RECIBOS_DE_PAGO, CONTRATO_DE_TRABAJO, diff --git a/src/main/java/com/primefactorsolutions/model/Week.java b/src/main/java/com/primefactorsolutions/model/Week.java index cb36ca8..1e05323 100644 --- a/src/main/java/com/primefactorsolutions/model/Week.java +++ b/src/main/java/com/primefactorsolutions/model/Week.java @@ -1,13 +1,15 @@ package com.primefactorsolutions.model; +import com.google.common.collect.Lists; + import java.time.LocalDate; import java.time.Year; import java.time.format.DateTimeFormatter; import java.time.temporal.IsoFields; import java.time.temporal.WeekFields; +import java.util.ArrayList; import java.util.List; import java.util.Locale; -import java.util.stream.IntStream; public record Week(LocalDate from, LocalDate to) { @Override @@ -25,11 +27,15 @@ public record Week(LocalDate from, LocalDate to) { public static List getLastWeeks(final int numberOfWeeks) { final int weekNumber = LocalDate.now().get(IsoFields.WEEK_OF_WEEK_BASED_YEAR); + LocalDate from = getFirstDayOfWeek(weekNumber); + final ArrayList result = Lists.newArrayList(); - return IntStream.range(0, numberOfWeeks).mapToObj(idx -> { - LocalDate from = getFirstDayOfWeek(weekNumber - idx); - return new Week(from, from.plusDays(6)); - }).toList(); + for (int i = 0; i < numberOfWeeks; i++) { + result.add(new Week(from, from.plusDays(6))); + from = from.minusDays(7); + } + + return result; } private static LocalDate getFirstDayOfWeek(final int weekNumber) { diff --git a/src/main/java/com/primefactorsolutions/views/BaseEntityForm.java b/src/main/java/com/primefactorsolutions/views/BaseEntityForm.java new file mode 100644 index 0000000..37c42bc --- /dev/null +++ b/src/main/java/com/primefactorsolutions/views/BaseEntityForm.java @@ -0,0 +1,44 @@ +package com.primefactorsolutions.views; + +import com.primefactorsolutions.views.util.AuthUtils; +import com.vaadin.flow.component.ClickEvent; +import com.vaadin.flow.component.ComponentEventListener; +import com.vaadin.flow.component.UI; +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.orderedlayout.HorizontalLayout; +import com.vaadin.flow.spring.security.AuthenticationContext; +import org.vaadin.firitin.form.BeanValidationForm; + +import java.util.Optional; +import java.util.UUID; + +@SuppressWarnings("deprecation") +public abstract class BaseEntityForm extends BeanValidationForm { + private final AuthenticationContext authenticationContext; + + public BaseEntityForm(final AuthenticationContext authenticationContext, final Class entityType) { + super(entityType); + this.authenticationContext = authenticationContext; + } + + @Override + public HorizontalLayout getToolbar() { + return new HorizontalLayout(getCancelButton(), getSaveButton()); + } + + protected Button getCancelButton() { + final Button cancelButton = new Button("Cancel"); + cancelButton.addClickListener((ComponentEventListener>) buttonClickEvent -> + UI.getCurrent().getPage().getHistory().back()); + + return cancelButton; + } + + protected boolean isRoleAdmin() { + return AuthUtils.isAdmin(this.authenticationContext); + } + + protected Optional getEmployeeId() { + return AuthUtils.getEmployeeId(this.authenticationContext); + } +} diff --git a/src/main/java/com/primefactorsolutions/views/DocumentView.java b/src/main/java/com/primefactorsolutions/views/DocumentView.java index 3e738fd..8ffca8a 100644 --- a/src/main/java/com/primefactorsolutions/views/DocumentView.java +++ b/src/main/java/com/primefactorsolutions/views/DocumentView.java @@ -24,7 +24,6 @@ import elemental.json.JsonObject; import jakarta.annotation.security.PermitAll; import org.springframework.context.annotation.Scope; import org.springframework.security.core.userdetails.UserDetails; -import org.vaadin.firitin.form.BeanValidationForm; import com.vaadin.flow.spring.security.AuthenticationContext; import java.io.ByteArrayInputStream; @@ -39,7 +38,7 @@ import java.io.InputStream; @Scope("prototype") @PageTitle("Document") @Route(value = "/documents/:documentId?/:action?", layout = MainLayout.class) -public class DocumentView extends BeanValidationForm implements HasUrlParameter { +public class DocumentView extends BaseEntityForm implements HasUrlParameter { 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"); @@ -50,17 +49,18 @@ public class DocumentView extends BeanValidationForm implements HasUrl private final EmployeeService employeeService; private final AuthenticationContext authContext; private boolean fileUploaded = false; - private Button saveButton; private Button viewDocumentButton; - public DocumentView(final DocumentService documentService, + public DocumentView(final AuthenticationContext authenticationContext, + final DocumentService documentService, final EmployeeService employeeService, final AuthenticationContext authContext) { - super(Document.class); + super(authenticationContext, Document.class); this.documentService = documentService; this.employeeService = employeeService; this.authContext = authContext; initializeView(); + this.setSavedHandler(this::saveDocument); } private void initializeView() { @@ -68,18 +68,6 @@ public class DocumentView extends BeanValidationForm implements HasUrl configureUploadButton(); } - protected Button createSaveButton() { - saveButton = new Button("Guardar"); - saveButton.addClickListener(event -> saveDocument()); - return saveButton; - } - - protected Button createCloseButton() { - Button closeButton = new Button("Salir"); - closeButton.addClickListener(event -> closeForm()); - return closeButton; - } - protected Button createViewDocumentButton() { viewDocumentButton = new Button("Ver documento"); viewDocumentButton.setEnabled(false); @@ -135,13 +123,8 @@ public class DocumentView extends BeanValidationForm implements HasUrl } } - private void navigateToDocumentsListView() { - getUI().ifPresent(ui -> ui.navigate(DocumentsListView.class)); - } - - private void saveDocument() { + private void saveDocument(final Document document) { if (isFormValid()) { - Document document = getEntity(); document.setFileName(fileName.getValue()); document.setDocumentType(documentType.getValue()); document.setEmployee(employeeComboBox.getValue()); @@ -149,17 +132,12 @@ public class DocumentView extends BeanValidationForm implements HasUrl setDocumentCreator(document); documentService.saveDocument(document); - Notification.show("Archivo guardado correctamente."); - clearForm(); + getUI().ifPresent(ui -> ui.navigate(DocumentsListView.class)); } else { Notification.show("Error al guardar: Por favor, complete todos los campos y cargue un archivo."); } } - private void closeForm() { - navigateToDocumentsListView(); - } - private boolean isFormValid() { return !fileName.isEmpty() && documentType.getValue() != null @@ -167,15 +145,6 @@ public class DocumentView extends BeanValidationForm implements HasUrl && fileUploaded; } - private void clearForm() { - fileName.clear(); - documentType.clear(); - employeeComboBox.clear(); - fileUploaded = false; - uploadButton.getElement().setPropertyJson("files", Json.createArray()); - viewDocumentButton.setEnabled(false); - } - private byte[] readFileData() { try { return buffer.getInputStream().readAllBytes(); @@ -197,23 +166,10 @@ public class DocumentView extends BeanValidationForm implements HasUrl fileUploaded = true; } - private void updateSaveButtonState() { - boolean isModified = !fileName.getValue().equals(getEntity().getFileName()) - || documentType.getValue() != getEntity().getDocumentType() - || employeeComboBox.getValue() != getEntity().getEmployee() - || fileUploaded; - saveButton.setEnabled(isModified); - } - private void configureComponents() { setFileNameProperties(); setDocumentTypeProperties(); setEmployeeComboBoxProperties(); - fileName.addValueChangeListener(e -> updateSaveButtonState()); - documentType.addValueChangeListener(e -> updateSaveButtonState()); - employeeComboBox.addValueChangeListener(e -> updateSaveButtonState()); - uploadButton.addSucceededListener(e -> updateSaveButtonState()); - uploadButton.getElement().addEventListener("file-remove", event -> updateSaveButtonState()); } private void configureUploadButton() { @@ -223,29 +179,14 @@ public class DocumentView extends BeanValidationForm implements HasUrl fileUploaded = true; Notification.show("Archivo cargado correctamente."); viewDocumentButton.setEnabled(true); - updateSaveButtonState(); }); uploadButton.getElement().addEventListener("file-remove", event -> { fileUploaded = false; Notification.show("Archivo eliminado."); viewDocumentButton.setEnabled(false); - updateSaveButtonState(); }); } - private void configureViewOrEditAction(final String action, final String documentIdString) { - if ("edit".equals(action) && !documentIdString.isEmpty()) { - setFieldsReadOnly(false); - preLoadFile(getEntity()); - viewDocumentButton.setEnabled(true); - } else if ("view".equals(action) && !documentIdString.isEmpty()) { - setFieldsReadOnly(true); - preLoadFile(getEntity()); - saveButton.setEnabled(false); - viewDocumentButton.setEnabled(true); - } - } - @Override public void setParameter(final BeforeEvent beforeEvent, final String action) { final RouteParameters params = beforeEvent.getRouteParameters(); @@ -257,18 +198,30 @@ public class DocumentView extends BeanValidationForm implements HasUrl assert documentIdString != null; UUID documentId = UUID.fromString(documentIdString); Document document = documentService.getDocument(documentId); - setEntity(document); + employeeComboBox.setValue(document.getEmployee()); preLoadFile(document); - configureViewOrEditAction(action, documentIdString); + + if ("edit".equals(action) && !documentIdString.isEmpty()) { + setEntityWithEnabledSave(document); + setFieldsReadOnly(false); + preLoadFile(document); + viewDocumentButton.setEnabled(true); + } else if ("view".equals(action) && !documentIdString.isEmpty()) { + setEntity(document); + setFieldsReadOnly(true); + preLoadFile(document); + viewDocumentButton.setEnabled(true); + } } } @Override protected List getFormComponents() { - HorizontalLayout buttonLayout = new HorizontalLayout(); + final HorizontalLayout buttonLayout = new HorizontalLayout(); buttonLayout.add(uploadButton, createViewDocumentButton()); buttonLayout.setSpacing(true); - return List.of(fileName, documentType, employeeComboBox, pdfOnlyMessage, buttonLayout, createCloseButton()); + + return List.of(documentType, fileName, employeeComboBox, pdfOnlyMessage, buttonLayout); } } diff --git a/src/main/java/com/primefactorsolutions/views/DocumentsListView.java b/src/main/java/com/primefactorsolutions/views/DocumentsListView.java index f3df80c..ed9d4a5 100644 --- a/src/main/java/com/primefactorsolutions/views/DocumentsListView.java +++ b/src/main/java/com/primefactorsolutions/views/DocumentsListView.java @@ -11,6 +11,7 @@ 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.button.ButtonVariant; import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.contextmenu.MenuItem; import com.vaadin.flow.component.grid.GridSortOrder; @@ -60,7 +61,7 @@ public class DocumentsListView extends BaseView { } private void initializeView() { - getCurrentPageLayout().add(createActionButton("Añadir documento", this::navigateToAddDocumentView)); + getCurrentPageLayout().add(createActionButton("Añadir documento", this::navigateToAddDocumentView, true)); final HorizontalLayout hl = new HorizontalLayout(); hl.add(createDocumentTypeFilter()); @@ -105,16 +106,22 @@ public class DocumentsListView extends BaseView { }); } - private Button createActionButton(final String label, final Runnable onClickAction) { + private Button createActionButton(final String label, final Runnable onClickAction, final boolean isPrimary) { Button actionButton = new Button(label); + + if (isPrimary) { + actionButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY); + } + actionButton.addClickListener(event -> onClickAction.run()); return actionButton; } private ComboBox createDocumentTypeFilter() { documentTypeFilter = new ComboBox<>("Tipo de documento"); + documentTypeFilter.setClearButtonVisible(true); + documentTypeFilter.setPlaceholder("Seleccionar ..."); documentTypeFilter.setItems(DocumentType.values()); - documentTypeFilter.setValue(DocumentType.values()[0]); documentTypeFilter.addValueChangeListener(event -> { updateDocumentGrid(event.getValue(), employeeFilter.getValue()); }); @@ -123,26 +130,19 @@ public class DocumentsListView extends BaseView { private ComboBox createEmployeeFilter() { employeeFilter = new ComboBox<>("Empleado"); + employeeFilter.setPlaceholder("Seleccionar ..."); + employeeFilter.setClearButtonVisible(true); List employees = employeeService.findAllEmployees(); - employees.addFirst(createAllEmployeesOption()); employeeFilter.setItems(employees); employeeFilter.setItemLabelGenerator(this::getEmployeeLabel); - employeeFilter.setValue(employees.getFirst()); employeeFilter.addValueChangeListener(event -> { updateDocumentGrid(documentTypeFilter.getValue(), event.getValue()); }); return employeeFilter; } - private Employee createAllEmployeesOption() { - Employee allEmployeesOption = new Employee(); - allEmployeesOption.setFirstName("TODOS"); - return allEmployeesOption; - } - private String getEmployeeLabel(final Employee employee) { - return employee.getFirstName().equals("TODOS") - ? "TODOS" : employee.getFirstName() + " " + employee.getLastName(); + return employee.getFirstName() + " " + employee.getLastName(); } private void navigateToEditDocumentView(final Document document) { diff --git a/src/main/java/com/primefactorsolutions/views/MainLayout.java b/src/main/java/com/primefactorsolutions/views/MainLayout.java index 37eb480..d082442 100644 --- a/src/main/java/com/primefactorsolutions/views/MainLayout.java +++ b/src/main/java/com/primefactorsolutions/views/MainLayout.java @@ -6,9 +6,8 @@ import com.primefactorsolutions.views.admin.TimeOffListView; import com.primefactorsolutions.views.assessment.AssessmentsListView; import com.primefactorsolutions.views.assessment.CandidatesListView; import com.primefactorsolutions.views.assessment.QuestionsListView; -import com.primefactorsolutions.views.timeoff.TimeOffNewRequestView; -import com.primefactorsolutions.views.timeoff.TimeOffPendingRequestsListView; -import com.primefactorsolutions.views.timeoff.TimeOffRequestListView; +import com.primefactorsolutions.views.timeoff.TimeOffRequestsListView; +import com.primefactorsolutions.views.timeoff.TimeOffSummaryListView; import com.primefactorsolutions.views.timesheet.TimesheetListView; import com.primefactorsolutions.views.timesheet.TimesheetReportView; import com.vaadin.flow.component.Component; @@ -142,10 +141,6 @@ public class MainLayout extends AppLayout { if (isAdmin(authContext)) { SideNavItem admin = new SideNavItem("Admin"); admin.setPrefixComponent(LineAwesomeIcon.BUILDING.create()); - admin.addItem(new SideNavItem("Employees", EmployeesListView.class, - LineAwesomeIcon.USER_EDIT_SOLID.create())); - admin.addItem(new SideNavItem("Documents", DocumentsListView.class, - LineAwesomeIcon.FILE_ALT_SOLID.create())); admin.addItem(new SideNavItem("Calendario", TimeOffListView.class, LineAwesomeIcon.CALENDAR.create())); nav.addItem(admin); @@ -163,15 +158,10 @@ public class MainLayout extends AppLayout { final SideNavItem timeOff = new SideNavItem("Time-off"); timeOff.setPrefixComponent(LineAwesomeIcon.PLANE_DEPARTURE_SOLID.create()); - timeOff.addItem(new SideNavItem("Vacations", TimeOffRequestListView.class, + timeOff.addItem(new SideNavItem("Vacations", TimeOffSummaryListView.class, LineAwesomeIcon.UMBRELLA_BEACH_SOLID.create())); - timeOff.addItem(new SideNavItem("Add TimeOff", TimeOffNewRequestView.class, - LineAwesomeIcon.CALENDAR_PLUS.create())); - - if (isAdmin(authContext)) { - timeOff.addItem(new SideNavItem("Pending Requests", TimeOffPendingRequestsListView.class, - LineAwesomeIcon.LIST_ALT.create())); - } + timeOff.addItem(new SideNavItem("Requests", TimeOffRequestsListView.class, + LineAwesomeIcon.LIST_ALT.create())); final SideNavItem timesheet = new SideNavItem("Timesheet"); timesheet.setPrefixComponent(LineAwesomeIcon.HOURGLASS_START_SOLID.create()); @@ -183,8 +173,12 @@ public class MainLayout extends AppLayout { LineAwesomeIcon.ID_CARD_SOLID.create())); } - final SideNavItem profile = new SideNavItem("Profile", ProfileView.class, - LineAwesomeIcon.USER_EDIT_SOLID.create()); + final SideNavItem profile = new SideNavItem("Employee"); + profile.setPrefixComponent(LineAwesomeIcon.USER_TIE_SOLID.create()); + profile.addItem(new SideNavItem("Profiles", EmployeesListView.class, + LineAwesomeIcon.USER_EDIT_SOLID.create())); + profile.addItem(new SideNavItem("Documents", DocumentsListView.class, + LineAwesomeIcon.FILE_ALT_SOLID.create())); nav.addItem(profile); nav.addItem(timesheet); diff --git a/src/main/java/com/primefactorsolutions/views/admin/EmployeesListView.java b/src/main/java/com/primefactorsolutions/views/admin/EmployeesListView.java index 0b53678..ea18971 100644 --- a/src/main/java/com/primefactorsolutions/views/admin/EmployeesListView.java +++ b/src/main/java/com/primefactorsolutions/views/admin/EmployeesListView.java @@ -7,7 +7,9 @@ import com.primefactorsolutions.views.Constants; import com.primefactorsolutions.views.EmployeeView; import com.primefactorsolutions.views.MainLayout; import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.button.ButtonVariant; import com.vaadin.flow.component.html.Anchor; +import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; import com.vaadin.flow.server.StreamResource; @@ -45,8 +47,8 @@ public class EmployeesListView extends BaseView { private void setupView() { configureTable(); - getCurrentPageLayout().add(createAddEmployeeButton()); - getCurrentPageLayout().add(createExportButton()); + final HorizontalLayout hl = new HorizontalLayout(createAddEmployeeButton(), createExportButton()); + getCurrentPageLayout().add(hl); getCurrentPageLayout().add(table); } @@ -143,18 +145,23 @@ public class EmployeesListView extends BaseView { } private void addEditButtonColumn(final String label, final ButtonClickHandler handler) { - table.addComponentColumn(employee -> createButton(label, () -> handler.handle(employee))); + table.addComponentColumn(employee -> createButton(label, () -> handler.handle(employee), false)); } - private Button createButton(final String label, final Runnable onClickAction) { + private Button createButton(final String label, final Runnable onClickAction, final boolean isPrimary) { Button button = new Button(label); + + if (isPrimary) { + button.addThemeVariants(ButtonVariant.LUMO_PRIMARY); + } + button.addClickListener(event -> onClickAction.run()); return button; } private Button createAddEmployeeButton() { - return createButton("Add Employee", this::navigateToAddEmployeeView); + return createButton("Add Employee", this::navigateToAddEmployeeView, true); } private void navigateToEditView(final Employee employee) { diff --git a/src/main/java/com/primefactorsolutions/views/timeoff/TimeOffNewRequestView.java b/src/main/java/com/primefactorsolutions/views/timeoff/TimeOffNewRequestView.java deleted file mode 100644 index 4f80033..0000000 --- a/src/main/java/com/primefactorsolutions/views/timeoff/TimeOffNewRequestView.java +++ /dev/null @@ -1,560 +0,0 @@ -package com.primefactorsolutions.views.timeoff; - -import com.primefactorsolutions.model.*; -import com.primefactorsolutions.service.EmployeeService; -import com.primefactorsolutions.service.TimeOffRequestService; -import com.primefactorsolutions.service.TimeOffService; -import com.primefactorsolutions.views.MainLayout; -import com.vaadin.flow.component.button.Button; -import com.vaadin.flow.component.combobox.ComboBox; -import com.vaadin.flow.component.datepicker.DatePicker; -import com.vaadin.flow.component.html.H3; -import com.vaadin.flow.component.notification.Notification; -import com.vaadin.flow.component.orderedlayout.HorizontalLayout; -import com.vaadin.flow.component.orderedlayout.VerticalLayout; -import com.vaadin.flow.component.textfield.NumberField; -import com.vaadin.flow.data.binder.Binder; -import com.vaadin.flow.router.*; -import com.vaadin.flow.spring.annotation.SpringComponent; -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; -import java.util.List; -import java.util.UUID; - -@SpringComponent -@PermitAll -@Scope("prototype") -@PageTitle("Request") -@Route(value = "/time-off/requests/new", layout = MainLayout.class) -public class TimeOffNewRequestView extends VerticalLayout { - - private final ComboBox employeeComboBox = new ComboBox<>("Empleado"); - private final ComboBox categoryComboBox = new ComboBox<>("Categoría"); - private final NumberField availableDaysField = new NumberField("Días disponibles"); - private final DatePicker startDatePicker = new DatePicker("Fecha de inicio"); - private final DatePicker endDatePicker = new DatePicker("Fecha final"); - private final NumberField daysToBeTakenField = new NumberField("Días a tomar"); - private final NumberField balanceDaysField = new NumberField("Días de saldo"); - - private final TimeOffRequestService requestService; - private final EmployeeService employeeService; - private final TimeOffService timeOffService; - - private final Binder binder; - private TimeOff timeoff; - private Employee employee; - private LocalDate endDate; - - private Button saveButton; - private Button closeButton; - - public TimeOffNewRequestView(final TimeOffRequestService requestService, - final EmployeeService employeeService, - final TimeOffService timeOffService) { - this.requestService = requestService; - this.employeeService = employeeService; - this.timeOffService = timeOffService; - this.binder = new Binder<>(TimeOffRequest.class); - initializeView(); - } - - private void initializeView() { - requestService.updateRequestStatuses(); - configureFormFields(); - configureButtons(); - configureBinder(); - setupFormLayout(); - configureInitialFieldStates(); - } - - private void configureInitialFieldStates() { - categoryComboBox.setEnabled(false); - startDatePicker.setEnabled(false); - endDatePicker.setEnabled(false); - availableDaysField.setReadOnly(true); - daysToBeTakenField.setReadOnly(true); - balanceDaysField.setReadOnly(true); - } - - private void configureFormFields() { - employeeComboBox.setItems(employeeService.findAllEmployees()); - employeeComboBox.setItemLabelGenerator(emp -> emp.getFirstName() + " " + emp.getLastName()); - employeeComboBox.addValueChangeListener(event -> { - employee = event.getValue(); - handleEmployeeSelection(event.getValue()); - }); - categoryComboBox.addValueChangeListener(event -> { - onCategoryChange(event.getValue()); - handleCategorySelection(event.getValue()); - }); - 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() { - binder.forField(employeeComboBox) - .bind(TimeOffRequest::getEmployee, TimeOffRequest::setEmployee); - binder.forField(categoryComboBox) - .bind(TimeOffRequest::getCategory, TimeOffRequest::setCategory); - binder.forField(availableDaysField) - .bind(TimeOffRequest::getAvailableDays, TimeOffRequest::setAvailableDays); - binder.forField(startDatePicker) - .bind(TimeOffRequest::getStartDate, TimeOffRequest::setStartDate); - binder.forField(endDatePicker) - .bind(TimeOffRequest::getEndDate, TimeOffRequest::setEndDate); - binder.forField(daysToBeTakenField) - .bind(TimeOffRequest::getDaysToBeTake, TimeOffRequest::setDaysToBeTake); - binder.forField(balanceDaysField) - .bind(TimeOffRequest::getDaysBalance, TimeOffRequest::setDaysBalance); - binder.setBean(new TimeOffRequest()); - } - - private void handleEmployeeSelection(final Employee selectedEmployee) { - if (selectedEmployee != null) { - categoryComboBox.clear(); - availableDaysField.clear(); - startDatePicker.clear(); - endDatePicker.clear(); - daysToBeTakenField.clear(); - balanceDaysField.clear(); - categoryComboBox.setEnabled(true); - startDatePicker.setEnabled(false); - endDatePicker.setEnabled(false); - filterCategories(selectedEmployee); - } - } - - private void filterCategories(final Employee employee) { - categoryComboBox.clear(); - List employeeRequests = requestService.findRequestsByEmployeeId(employee.getId()); - List allCategories = Arrays.asList(TimeOffRequestType.values()); - List availableCategories = allCategories.stream() - .filter(category -> isCategoryAvailable(employeeRequests, category)) - .filter(category -> isCategoryAllowedByGender(category, employee.getGender())) - .filter(category -> shouldIncludeVacationGestionActual(employeeRequests, category)) - .filter(category -> shouldIncludeVacationGestionAnterior(employeeRequests, category)) - .toList(); - - categoryComboBox.setItems(availableCategories); - } - - - private boolean shouldIncludeVacationGestionActual(final List employeeRequests, - final TimeOffRequestType category) { - if (category != TimeOffRequestType.VACACION_GESTION_ACTUAL) { - return true; - } - return employeeRequests.stream() - .anyMatch(request -> request.getCategory() == TimeOffRequestType.VACACION_GESTION_ANTERIOR - && request.getDaysBalance() == 0 - && request.getState() == TimeOffRequestStatus.TOMADO); - } - - private boolean shouldIncludeVacationGestionAnterior(final List employeeRequests, - final TimeOffRequestType category) { - if (category != TimeOffRequestType.VACACION_GESTION_ANTERIOR) { - return true; - } - return employeeRequests.stream() - .noneMatch(request -> request.getCategory() == TimeOffRequestType.VACACION_GESTION_ANTERIOR - && request.getDaysBalance() == 0); - } - - private void onCategoryChange(final TimeOffRequestType selectedCategory) { - if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ACTUAL - || selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) { - startDatePicker.setEnabled(true); - endDatePicker.setEnabled(true); - } else { - startDatePicker.setEnabled(true); - endDatePicker.setEnabled(false); - } - } - - 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(); - - if (requestsByCategory.isEmpty()) { - return true; - } - - TimeOffRequest latestRequest = requestsByCategory.stream() - .max(Comparator.comparing(TimeOffRequest::getStartDate)) - .orElse(null); - - boolean isSpecialCategory = category == TimeOffRequestType.PERMISOS_DE_SALUD - || category == TimeOffRequestType.VACACION_GESTION_ACTUAL - || category == TimeOffRequestType.VACACION_GESTION_ANTERIOR; - - if (isSpecialCategory) { - return (latestRequest.getState() == TimeOffRequestStatus.TOMADO - && latestRequest.getDaysBalance() > 0) - || latestRequest.getState() == TimeOffRequestStatus.RECHAZADO - || (latestRequest.getState() == TimeOffRequestStatus.TOMADO - && latestRequest.getDaysBalance() == 0 - && latestRequest.getExpiration().isBefore(LocalDate.now())); - } else { - return (latestRequest.getState() == TimeOffRequestStatus.TOMADO - && latestRequest.getExpiration().isBefore(LocalDate.now())) - || latestRequest.getState() == TimeOffRequestStatus.RECHAZADO; - } - } - - private boolean isCategoryAllowedByGender(final TimeOffRequestType category, final Employee.Gender gender) { - if (gender == Employee.Gender.MALE) { - return category != TimeOffRequestType.MATERNIDAD - && category != TimeOffRequestType.DIA_DE_LA_MADRE - && category != TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL - && category != TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL; - } else { - return category != TimeOffRequestType.DIA_DEL_PADRE - && category != TimeOffRequestType.PATERNIDAD; - } - } - - private void handleCategorySelection(final TimeOffRequestType selectedCategory) { - if (selectedCategory != null) { - updateAvailableDays(selectedCategory); - } - } - - private void updateAvailableDays(final TimeOffRequestType selectedCategory) { - timeoff = timeOffService.findVacationByCategory(selectedCategory); - UUID employeeId = employeeComboBox.getValue().getId(); - List requests = requestService.findByEmployeeAndCategory(employeeId, selectedCategory); - - if (timeoff != null) { - TimeOffRequest requestWithBalance = requests.stream() - .filter(request -> request.getDaysBalance() > 0 - && request.getState() != TimeOffRequestStatus.VENCIDO - && request.getState() != TimeOffRequestStatus.RECHAZADO) - .max(Comparator.comparing(TimeOffRequest::getStartDate)) - .orElse(null); - - if (requestWithBalance != null) { - if (requestWithBalance.getState() == TimeOffRequestStatus.TOMADO - && requestWithBalance.getDaysBalance() > 0) { - availableDaysField.setValue(requestWithBalance.getDaysBalance()); - } else { - availableDaysField.setValue(timeoff.getDuration()); - } - } else if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ACTUAL - || selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) { - LocalDate dateOfEntry = employeeComboBox.getValue().getDateOfEntry(); - LocalDate currentDate = LocalDate.now(); - long yearsOfService = ChronoUnit.YEARS.between(dateOfEntry, currentDate); - - if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) { - yearsOfService -= 1; - } - - if (yearsOfService > 10) { - availableDaysField.setValue(30.0); - } else if (yearsOfService > 5) { - availableDaysField.setValue(20.0); - } else if (yearsOfService > 1) { - availableDaysField.setValue(15.0); - } else { - availableDaysField.setValue(0.0); - } - } else { - availableDaysField.setValue(timeoff.getDuration()); - } - setDatePickerLimits(timeoff); - } - } - - private void setDatePickerLimits(final TimeOff timeoff) { - LocalDate startDate; - endDate = null; - - UUID employeeId = employee.getId(); - List previousRequests - = requestService.findByEmployeeAndCategory(employeeId, timeoff.getCategory()); - - int startYear = calculateStartYear(previousRequests); - - startDate = determineStartDate(timeoff, startYear); - - if (startDate.isBefore(LocalDate.now())) { - startDate = determineStartDate(timeoff, startYear + 1); - } - - if (startDate != null) { - if (timeoff.getExpiration() != null) { - endDate = startDate.plusDays(timeoff.getExpiration().intValue() - 1); - } else { - endDate = LocalDate.of(startDate.getYear(), 12, 31); - } - } else { - startDate = LocalDate.now(); - } - - setPickerValues(timeoff, startDate); - setPickerLimits(startDate, endDate); - } - - private int calculateStartYear(final List previousRequests) { - if (previousRequests.isEmpty()) { - return LocalDate.now().getYear(); - } - - int lastRequestYear = previousRequests.stream() - .max(Comparator.comparing(TimeOffRequest::getStartDate)) - .map(request -> request.getStartDate().getYear()) - .orElse(LocalDate.now().getYear()); - - if (previousRequests.getLast().getState() != TimeOffRequestStatus.RECHAZADO) { - lastRequestYear = lastRequestYear + 1; - } - - int currentYear = LocalDate.now().getYear(); - return Math.max(lastRequestYear, currentYear); - } - - private LocalDate determineStartDate(final TimeOff timeoff, final int startYear) { - if (timeoff.getCategory() == TimeOffRequestType.CUMPLEAÑOS && employee.getBirthday() != null) { - return LocalDate.of(startYear, employee.getBirthday().getMonth(), employee.getBirthday().getDayOfMonth()); - } - - if (timeoff.getDate() != null) { - return LocalDate.of(startYear, timeoff.getDate().getMonthValue(), timeoff.getDate().getDayOfMonth()); - } - - return LocalDate.now(); - } - - private void setPickerValues(final TimeOff timeoff, final LocalDate startDate) { - startDatePicker.setValue(startDate); - - if ((timeoff.getDuration() != null && timeoff.getDuration() == 0.5) - || timeoff.getCategory() == TimeOffRequestType.PERMISOS_DE_SALUD - || timeoff.getCategory() == TimeOffRequestType.CUMPLEAÑOS) { - - endDatePicker.setValue(startDate); - } else { - int durationDays = (timeoff.getDuration() != null ? timeoff.getDuration().intValue() - 1 : 0); - endDatePicker.setValue(startDate.plusDays(durationDays)); - } - } - - private void setPickerLimits(final LocalDate startDate, final LocalDate endDate) { - startDatePicker.setMin(startDate); - startDatePicker.setMax(endDate); - endDatePicker.setMin(startDate); - endDatePicker.setMax(endDate); - } - - private void updateDatePickerMinValues() { - LocalDate startDate = startDatePicker.getValue(); - if (availableDaysField.getValue() != null) { - if (availableDaysField.getValue() == 0.5) { - endDatePicker.setValue(startDate.plusDays(0)); - } else { - endDatePicker.setValue(startDate.plusDays(availableDaysField.getValue().intValue() - 1)); - } - calculateDays(); - } - } - - private void calculateDays() { - LocalDate startDate = startDatePicker.getValue(); - LocalDate endDate = endDatePicker.getValue(); - 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); - - double balanceDays = calculateBalanceDays(availableDays, daysToBeTakenField.getValue()); - balanceDaysField.setValue(balanceDays); - - if (balanceDays < 0.0) { - clearFields(); - } - - if (daysToBeTakenField.getValue() > 10 - && (categoryComboBox.getValue() == TimeOffRequestType.VACACION_GESTION_ANTERIOR - || categoryComboBox.getValue() == TimeOffRequestType.VACACION_GESTION_ACTUAL)) { - clearFields(); - } - } - } - - private boolean areDatesValid(final LocalDate startDate, final LocalDate endDate) { - 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 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) { - if (timeoff.getCategory() == TimeOffRequestType.PERMISOS_DE_SALUD - || timeoff.getCategory() == TimeOffRequestType.CUMPLEAÑOS - || timeoff.getCategory() == TimeOffRequestType.DIA_DEL_PADRE - || timeoff.getCategory() == TimeOffRequestType.DIA_DE_LA_MADRE - || timeoff.getCategory() == TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL - || timeoff.getCategory() == TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL) { - daysToBeTakenField.setValue(0.5); - } else { - daysToBeTakenField.setValue(daysToBeTaken); - } - } - - private double calculateBalanceDays(final double availableDays, final double daysToBeTaken) { - return availableDays - daysToBeTaken; - } - - private void clearFields() { - daysToBeTakenField.clear(); - balanceDaysField.clear(); - endDatePicker.clear(); - } - - private void configureButtons() { - saveButton = new Button("Guardar", event -> saveRequest()); - closeButton = new Button("Salir", event -> closeForm()); - } - - private void setupFormLayout() { - add( - new H3("Añadir solicitud de vacaciones"), - employeeComboBox, - categoryComboBox, - availableDaysField, - startDatePicker, - endDatePicker, - daysToBeTakenField, - balanceDaysField, - new HorizontalLayout(saveButton, closeButton) - ); - } - - private void saveRequest() { - if (!binder.validate().isOk()) { - Notification.show("Rellene correctamente todos los campos obligatorios."); - return; - } - - if (!validateForm()) { - Notification.show("Por favor, complete los campos antes de guardar"); - return; - } - - TimeOffRequest request = prepareRequest(); - - if (request.getCategory() == TimeOffRequestType.VACACION_GESTION_ACTUAL) { - handleVacationRequest(request); - } else { - handleExistingRequests(request); - } - - 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() { - TimeOffRequest request = binder.getBean(); - request.setStartDate(startDatePicker.getValue()); - request.setAvailableDays(availableDaysField.getValue()); - request.setExpiration(endDate != null ? endDate : endDatePicker.getValue()); - request.setState(TimeOffRequestStatus.SOLICITADO); - return request; - } - - private void handleExistingRequests(final TimeOffRequest request) { - List existingRequests = - requestService.findByEmployeeAndCategory(employee.getId(), request.getCategory()); - - int maxRequests = request.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD - && !request.getCategory().name().startsWith("VACACION") - && request.getCategory() != TimeOffRequestType.CUMPLEAÑOS - ? 2 : 1; - - if (existingRequests.size() >= maxRequests) { - existingRequests.stream() - .min(Comparator.comparing(TimeOffRequest::getStartDate)) - .ifPresent(oldestRequest -> requestService.deleteTimeOffRequest(oldestRequest.getId())); - } - } - - private void handleVacationRequest(final TimeOffRequest request) { - List existingRequests = requestService.findByEmployeeAndCategory( - employee.getId(), - TimeOffRequestType.VACACION_GESTION_ACTUAL - ); - if (!existingRequests.isEmpty()) { - TimeOffRequest existingRequest = existingRequests.getFirst(); - existingRequest.setCategory(TimeOffRequestType.VACACION_GESTION_ANTERIOR); - requestService.saveTimeOffRequest(existingRequest); - } - } - - private boolean validateForm() { - return employeeComboBox.getValue() != null - && categoryComboBox.getValue() != null - && startDatePicker.getValue() != null - && endDatePicker.getValue() != null; - } - - private void closeForm() { - getUI().ifPresent(ui -> ui.navigate(TimeOffRequestListView.class)); - } -} diff --git a/src/main/java/com/primefactorsolutions/views/timeoff/TimeOffRequestListView.java b/src/main/java/com/primefactorsolutions/views/timeoff/TimeOffRequestListView.java deleted file mode 100644 index dd0d37f..0000000 --- a/src/main/java/com/primefactorsolutions/views/timeoff/TimeOffRequestListView.java +++ /dev/null @@ -1,508 +0,0 @@ -package com.primefactorsolutions.views.timeoff; - -import com.primefactorsolutions.model.*; -import com.primefactorsolutions.service.EmployeeService; -import com.primefactorsolutions.service.TeamService; -import com.primefactorsolutions.service.TimeOffRequestService; -import com.primefactorsolutions.service.TimeOffService; -import com.primefactorsolutions.views.BaseView; -import com.primefactorsolutions.views.MainLayout; -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; -import com.vaadin.flow.component.menubar.MenuBar; -import com.vaadin.flow.component.menubar.MenuBarVariant; -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 com.vaadin.flow.spring.security.AuthenticationContext; -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.*; -import java.util.stream.Collectors; - -import static com.primefactorsolutions.views.Constants.PAGE_SIZE; -import static com.primefactorsolutions.views.util.MenuBarUtils.createIconItem; - -@SpringComponent -@Scope("prototype") -@PageTitle("Requests") -@Route(value = "/time-off/requests", layout = MainLayout.class) -@PermitAll -public class TimeOffRequestListView extends BaseView { - - private final TimeOffRequestService requestService; - private final EmployeeService employeeService; - private final TeamService teamService; - private final TimeOffService timeOffService; - private final PagingGrid requestGrid = new PagingGrid<>(); - private ComboBox employeeFilter; - private ComboBox teamFilter; - private ComboBox categoryFilter; - private ComboBox stateFilter; - - public TimeOffRequestListView( - final AuthenticationContext authenticationContext, - final TimeOffRequestService requestService, - final EmployeeService employeeService, - final TeamService teamService, - final TimeOffService timeOffService) { - super(authenticationContext); - this.requestService = requestService; - this.employeeService = employeeService; - this.teamService = teamService; - this.timeOffService = timeOffService; - initializeView(); - refreshGeneralRequestGrid(null, null, null, null); - } - - private void initializeView() { - requestService.updateRequestStatuses(); - - if (isRoleAdmin()) { - final Button downloadButton = new Button("Descargar reporte", event -> downloadReport()); - getCurrentPageLayout().add(downloadButton); - } - - setupFilters(); - setupRequestGrid(); - getCurrentPageLayout().add(requestGrid); - } - - private void setupFilters() { - final HorizontalLayout hl = new HorizontalLayout(); - hl.add(createEmployeeFilter()); - hl.add(createTeamFilter()); - hl.add(createCategoryFilter()); - hl.add(createStateFilter()); - getCurrentPageLayout().add(hl); - } - - private void setupRequestGrid() { - 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(); - menuBar.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE); - final MenuItem view = createIconItem(menuBar, VaadinIcon.EYE, "View"); - view.addClickListener((ComponentEventListener>) menuItemClickEvent -> - navigateToTimeOffRequestView(employee.getId())); - - return menuBar; - }); - - requestGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM); - requestGrid.setPageSize(PAGE_SIZE); - } - - 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, category, state); - }); - requestGrid.getDataProvider().refreshAll(); - } - - private List fetchFilteredEmployees(final int start, - final int pageSize, - final Employee employee, - final Team team, - final TimeOffRequestType category, - final Status state) { - List filteredEmployees = employeeService.findAllEmployees(); - - if (employee != null) { - filteredEmployees = filteredEmployees.stream() - .filter(emp -> emp.getId().equals(employee.getId())) - .collect(Collectors.toList()); - } - - if (team != null) { - filteredEmployees = filteredEmployees.stream() - .filter(emp -> emp.getTeam() != null && emp.getTeam().getId().equals(team.getId())) - .collect(Collectors.toList()); - } - - if (category != null) { - 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) { - filteredEmployees = filteredEmployees.stream() - .filter(emp -> { - Optional request = requestService - .findByEmployeeAndState(emp.getId(), TimeOffRequestStatus.EN_USO); - return state == Status.EN_DESCANSO ? request.isPresent() : request.isEmpty(); - }) - .collect(Collectors.toList()); - } - - int end = Math.min(start + pageSize, filteredEmployees.size()); - return filteredEmployees.subList(start, end); - } - - private String getEmployeeFullName(final Employee employee) { - return employee.getFirstName() + " " + employee.getLastName(); - } - - private String getTeamName(final Employee employee) { - Team team = employee.getTeam(); - return team != null ? team.getName() : "Sin asignar"; - } - - private String getTeamLabel(final Team team) { - return team.getName(); - } - - private String getEmployeeStatus(final Employee employee) { - Optional activeRequest = requestService - .findByEmployeeAndState(employee.getId(), TimeOffRequestStatus.EN_USO); - 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 timeOffs = timeOffService.findVacations(); - - List vacationDays = calculateVacationDays(employee); - - double utilizedVacationCurrentDays = vacationDays.get(1); - List vacationCurrentRequests = requestService - .findByEmployeeAndCategory(employee.getId(), 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(employee.getId(), TimeOffRequestType.VACACION_GESTION_ANTERIOR); - if (vacationPreviousRequests != null && !vacationPreviousRequests.isEmpty()) { - utilizedVacationPreviousDays = vacationPreviousRequests.getLast().getDaysBalance(); - } - double totalVacationPreviousDays = vacationDays.getFirst() - - (vacationDays.getFirst() - utilizedVacationPreviousDays); - - - double totalUtilized = calculateTotalUtilized(employeeRequests); - double totalVacations = totalVacationCurrentDays + totalVacationPreviousDays; - double totalAvailable = calculateTotalAvailable(timeOffs, 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); - } - - private Set getExcludedCategories() { - return Set.of( - TimeOffRequestType.MATERNIDAD, - TimeOffRequestType.PATERNIDAD, - TimeOffRequestType.MATRIMONIO, - TimeOffRequestType.DUELO_1ER_GRADO, - TimeOffRequestType.DUELO_2ER_GRADO, - TimeOffRequestType.DIA_DEL_PADRE, - TimeOffRequestType.DIA_DE_LA_MADRE - ); - } - - private Set getGenderSpecificExclusions() { - return Set.of( - TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL, - TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL - ); - } - - private double calculateTotalUtilized(final List employeeRequests) { - 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) - .filter(request -> request.getStartDate() != null && ( - request.getStartDate().getYear() == currentYear - || (request.getCategory().name().startsWith("VACACION") - && request.getStartDate().getYear() == currentYear - 1) - )) - .mapToDouble(request -> request.getDaysToBeTake() != null ? request.getDaysToBeTake() : 0.0) - .sum(); - } - - private List calculateVacationDays(final Employee employee) { - List vacationDays = new ArrayList<>(); - - if (employee.getDateOfEntry() != null) { - LocalDate entryDate = employee.getDateOfEntry(); - LocalDate today = LocalDate.now(); - - boolean hasAnniversaryPassed = entryDate.getMonthValue() < today.getMonthValue() - || (entryDate.getMonthValue() == today.getMonthValue() && entryDate.getDayOfMonth() - <= today.getDayOfMonth()); - - LocalDate previousVacationYearDate; - LocalDate currentVacationYearDate; - - if (hasAnniversaryPassed) { - previousVacationYearDate = LocalDate.of( - today.getYear() - 1, - entryDate.getMonth(), - entryDate.getDayOfMonth() - ); - currentVacationYearDate = LocalDate.of( - today.getYear(), - entryDate.getMonth(), - entryDate.getDayOfMonth() - ); - } else { - previousVacationYearDate = LocalDate.of( - today.getYear() - 2, - entryDate.getMonth(), - entryDate.getDayOfMonth() - ); - currentVacationYearDate = LocalDate.of( - today.getYear() - 1, - entryDate.getMonth(), - entryDate.getDayOfMonth() - ); - } - - vacationDays.add(calculateVacationDaysSinceEntry(entryDate, previousVacationYearDate)); - vacationDays.add(calculateVacationDaysSinceEntry(entryDate, currentVacationYearDate)); - } else { - vacationDays.add(0.0); - vacationDays.add(0.0); - } - return vacationDays; - } - - private double calculateTotalAvailable(final List timeOffs, final List employeeRequests, - final Employee employee) { - Set excludedCategories = getExcludedCategories(); - Set genderSpecificExclusions = getGenderSpecificExclusions(); - Set employeeRequestCategories = employeeRequests.stream() - .map(TimeOffRequest::getCategory) - .collect(Collectors.toSet()); - - double healthLicence = 2; - List healthRequests = requestService - .findByEmployeeAndCategory(employee.getId(), TimeOffRequestType.PERMISOS_DE_SALUD); - if (healthRequests != null && !healthRequests.isEmpty()) { - healthLicence = healthRequests.getLast().getDaysBalance(); - } - - double totalAvailable = timeOffs.stream() - .filter(Objects::nonNull) - .filter(vacation -> vacation.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD) - .filter(vacation -> shouldIncludeVacation( - vacation, - excludedCategories, - genderSpecificExclusions, - employee, employeeRequestCategories - )) - .mapToDouble(vacation -> vacation.getDuration() != null ? vacation.getDuration() : 0.0) - .sum(); - - return totalAvailable + healthLicence; - } - - private double calculateVacationDaysSinceEntry(final LocalDate dateOfEntry, final LocalDate date) { - int yearsOfService = dateOfEntry != null ? Period.between(dateOfEntry, date).getYears() : 0; - if (yearsOfService > 10) { - return 30; - } - if (yearsOfService > 5) { - return 20; - } - if (yearsOfService > 1) { - return 15; - } - return 0; - } - - private boolean shouldIncludeVacation(final TimeOff timeoff, - final Set excludedCategories, - final Set genderSpecificExclusions, - final Employee employee, - final Set employeeRequestCategories) { - if (excludedCategories.contains(timeoff.getCategory()) - && !employeeRequestCategories.contains(timeoff.getCategory())) { - return false; - } - - return isFemale(employee) || !genderSpecificExclusions.contains(timeoff.getCategory()); - } - - private boolean isFemale(final Employee employee) { - return employee.getGender() == Employee.Gender.FEMALE; - } - - private ComboBox createEmployeeFilter() { - employeeFilter = new ComboBox<>("Empleado"); - - final List employees = new ArrayList<>(employeeService.findAllEmployees()); - employeeFilter.setPlaceholder("TODOS"); - employeeFilter.setClearButtonVisible(true); - employeeFilter.setItems(employees); - employeeFilter.setItemLabelGenerator(this::getEmployeeFullName); - employeeFilter.addValueChangeListener(event -> - refreshGeneralRequestGrid( - event.getValue(), - teamFilter.getValue(), - categoryFilter.getValue(), - stateFilter.getValue() - ) - ); - - return employeeFilter; - } - - private ComboBox createTeamFilter() { - teamFilter = new ComboBox<>("Equipo"); - teamFilter.setPlaceholder("TODOS"); - teamFilter.setClearButtonVisible(true); - final List teams = new ArrayList<>(teamService.findAllTeams()); - teamFilter.setItems(teams); - teamFilter.setItemLabelGenerator(this::getTeamLabel); - teamFilter.addValueChangeListener(event -> - refreshGeneralRequestGrid( - employeeFilter.getValue(), - event.getValue(), - categoryFilter.getValue(), - stateFilter.getValue() - ) - ); - return teamFilter; - } - - private ComboBox createCategoryFilter() { - categoryFilter = new ComboBox<>("Category"); - categoryFilter.setClearButtonVisible(true); - categoryFilter.setPlaceholder("TODOS"); - categoryFilter.setItems(TimeOffRequestType.values()); - categoryFilter.addValueChangeListener(event -> - refreshGeneralRequestGrid( - employeeFilter.getValue(), - teamFilter.getValue(), - event.getValue(), - stateFilter.getValue() - ) - ); - return categoryFilter; - } - - private ComboBox createStateFilter() { - stateFilter = new ComboBox<>("Estado del empleado"); - stateFilter.setPlaceholder("TODOS"); - stateFilter.setClearButtonVisible(true); - stateFilter.setItems(Status.values()); - stateFilter.addValueChangeListener(event -> - refreshGeneralRequestGrid( - employeeFilter.getValue(), - teamFilter.getValue(), - categoryFilter.getValue(), - event.getValue() - ) - ); - return stateFilter; - } - - private enum Status { - EN_DESCANSO, - EN_FUNCIONES - } - - private void navigateToTimeOffRequestView(final UUID idEmployee) { - getUI().ifPresent(ui -> ui.navigate(TimeOffRequestsByEmployeeView.class, 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 diff --git a/src/main/java/com/primefactorsolutions/views/timeoff/TimeOffRequestView.java b/src/main/java/com/primefactorsolutions/views/timeoff/TimeOffRequestView.java index 283517b..daa97f1 100644 --- a/src/main/java/com/primefactorsolutions/views/timeoff/TimeOffRequestView.java +++ b/src/main/java/com/primefactorsolutions/views/timeoff/TimeOffRequestView.java @@ -3,112 +3,515 @@ package com.primefactorsolutions.views.timeoff; import com.primefactorsolutions.model.*; import com.primefactorsolutions.service.EmployeeService; import com.primefactorsolutions.service.TimeOffRequestService; +import com.primefactorsolutions.service.TimeOffService; +import com.primefactorsolutions.views.BaseEntityForm; import com.primefactorsolutions.views.MainLayout; import com.vaadin.flow.component.Component; import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.datepicker.DatePicker; -import com.vaadin.flow.component.html.H3; +import com.vaadin.flow.component.notification.Notification; import com.vaadin.flow.component.textfield.NumberField; import com.vaadin.flow.router.*; import com.vaadin.flow.spring.annotation.SpringComponent; +import com.vaadin.flow.spring.security.AuthenticationContext; import jakarta.annotation.security.PermitAll; import org.springframework.context.annotation.Scope; -import org.vaadin.firitin.form.BeanValidationForm; +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.Comparator; import java.util.List; import java.util.UUID; -@SuppressWarnings("deprecation") +import static com.primefactorsolutions.views.util.ComponentUtils.withFullWidth; + @SpringComponent @PermitAll @Scope("prototype") @PageTitle("Request") @Route(value = "/requests/:requestId?/:action?", layout = MainLayout.class) -public class TimeOffRequestView extends BeanValidationForm implements HasUrlParameter { +public class TimeOffRequestView extends BaseEntityForm implements HasUrlParameter { - private final ComboBox state = new ComboBox<>("Estado de la solicitud"); - private final DatePicker expiration = new DatePicker("Vencimiento"); - private final DatePicker startDate = new DatePicker("Fecha de inicio"); - private final DatePicker endDate = new DatePicker("Fecha de fin"); - private final NumberField availableDays = new NumberField("Días disponibles"); - private final NumberField daysToBeTake = new NumberField("Días a tomar"); + private final ComboBox category = withFullWidth(new ComboBox<>("Categoría")); + private final ComboBox state = withFullWidth(new ComboBox<>("Estado de la solicitud")); + private final DatePicker expiration = withFullWidth(new DatePicker("Vencimiento")); + private final DatePicker startDate = withFullWidth(new DatePicker("Fecha de inicio")); + private final DatePicker endDate = withFullWidth(new DatePicker("Fecha de fin")); + private final NumberField daysToBeTake = withFullWidth(new NumberField("Días a tomar")); + private final NumberField daysBalance = withFullWidth(new NumberField("Días disponibles")); - private final TimeOffRequestService requestService; + private final TimeOffService timeOffService; + private final TimeOffRequestService timeOffRequestService; private final EmployeeService employeeService; - private TimeOffRequest request; + private Employee employee; - public TimeOffRequestView(final TimeOffRequestService requestService, + public TimeOffRequestView(final AuthenticationContext authenticationContext, + final TimeOffRequestService timeOffRequestService, + final TimeOffService timeOffService, final EmployeeService employeeService) { - super(TimeOffRequest.class); - this.requestService = requestService; + super(authenticationContext, TimeOffRequest.class); + this.timeOffService = timeOffService; + this.timeOffRequestService = timeOffRequestService; this.employeeService = employeeService; state.setItems(List.of(TimeOffRequestStatus.values())); this.setSavedHandler(this::saveRequest); + + initView(); + } + + private void initView() { + category.addValueChangeListener(event -> { + onCategoryChange(event.getValue()); + handleCategorySelection(event.getValue()); + }); + startDate.addValueChangeListener(event -> { + LocalDate selectedDate = event.getValue(); + if (selectedDate != null && (selectedDate.getDayOfWeek().getValue() == 6 + || selectedDate.getDayOfWeek().getValue() == 7)) { + startDate.setValue(selectedDate.minusDays(1)); + } + updateDatePickerMinValues(); + }); + endDate.addValueChangeListener(event -> { + if (endDate.getValue() != null) { + endDate.setMin(endDate.getValue()); + } + final LocalDate selectedDate = event.getValue(); + + if (selectedDate != null && (selectedDate.getDayOfWeek().getValue() == 6 + || selectedDate.getDayOfWeek().getValue() == 7)) { + endDate.setValue(selectedDate.minusDays(1)); + } + calculateDays(); + }); } @Override public void setParameter(final BeforeEvent beforeEvent, final String action) { final RouteParameters params = beforeEvent.getRouteParameters(); final String requestIdString = params.get("requestId").orElse(null); + final UUID employeeId = getEmployeeId().get(); + employee = employeeService.getEmployee(employeeId); + filterCategories(employee); if ("new".equals(action)) { setEntityWithEnabledSave(new TimeOffRequest()); } else { assert requestIdString != null; UUID requestId = UUID.fromString(requestIdString); - request = requestService.findTimeOffRequest(requestId); - UUID employeeId = request.getEmployee().getId(); - employee = employeeService.getEmployee(employeeId); - setEntity(request); - configureViewOrEditAction(action); + TimeOffRequest timeOffRequest = timeOffRequestService.findTimeOffRequest(requestId); + + if ("edit".equals(action)) { + setEntityWithEnabledSave(timeOffRequest); + setFieldsReadOnly(false); + } else if ("view".equals(action)) { + setEntity(timeOffRequest); + setFieldsReadOnly(true); + } } } @Override protected List getFormComponents() { return List.of( - createEmployeeHeader(), - createTeamHeader(), - createCategoryHeader(), + category, state, expiration, startDate, endDate, - availableDays, + daysBalance, daysToBeTake ); } + private void filterCategories(final Employee employee) { + category.clear(); + List employeeRequests = timeOffRequestService.findRequestsByEmployeeId(employee.getId()); + List allCategories = Arrays.asList(TimeOffRequestType.values()); + List availableCategories = allCategories.stream() + .filter(category -> isCategoryAvailable(employeeRequests, category)) + .filter(category -> isCategoryAllowedByGender(category, employee.getGender())) + .filter(category -> shouldIncludeVacationGestionActual(employeeRequests, category)) + .filter(category -> shouldIncludeVacationGestionAnterior(employeeRequests, category)) + .toList(); + + category.setItems(availableCategories); + } + + private boolean shouldIncludeVacationGestionActual(final List employeeRequests, + final TimeOffRequestType category) { + if (category != TimeOffRequestType.VACACION_GESTION_ACTUAL) { + return true; + } + return employeeRequests.stream() + .anyMatch(request -> request.getCategory() == TimeOffRequestType.VACACION_GESTION_ANTERIOR + && request.getDaysBalance() == 0 + && request.getState() == TimeOffRequestStatus.TOMADO); + } + + private boolean shouldIncludeVacationGestionAnterior(final List employeeRequests, + final TimeOffRequestType category) { + if (category != TimeOffRequestType.VACACION_GESTION_ANTERIOR) { + return true; + } + return employeeRequests.stream() + .noneMatch(request -> request.getCategory() == TimeOffRequestType.VACACION_GESTION_ANTERIOR + && request.getDaysBalance() == 0); + } + + private void onCategoryChange(final TimeOffRequestType selectedCategory) { + if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ACTUAL + || selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) { + startDate.setEnabled(true); + endDate.setEnabled(true); + } else { + startDate.setEnabled(true); + endDate.setEnabled(false); + } + } + + 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(); + + if (requestsByCategory.isEmpty()) { + return true; + } + + TimeOffRequest latestRequest = requestsByCategory.stream() + .max(Comparator.comparing(TimeOffRequest::getStartDate)) + .orElse(null); + + boolean isSpecialCategory = category == TimeOffRequestType.PERMISOS_DE_SALUD + || category == TimeOffRequestType.VACACION_GESTION_ACTUAL + || category == TimeOffRequestType.VACACION_GESTION_ANTERIOR; + + if (isSpecialCategory) { + return (latestRequest.getState() == TimeOffRequestStatus.TOMADO + && latestRequest.getDaysBalance() > 0) + || latestRequest.getState() == TimeOffRequestStatus.RECHAZADO + || (latestRequest.getState() == TimeOffRequestStatus.TOMADO + && latestRequest.getDaysBalance() == 0 + && latestRequest.getExpiration().isBefore(LocalDate.now())); + } else { + return (latestRequest.getState() == TimeOffRequestStatus.TOMADO + && latestRequest.getExpiration().isBefore(LocalDate.now())) + || latestRequest.getState() == TimeOffRequestStatus.RECHAZADO; + } + } + + private boolean isCategoryAllowedByGender(final TimeOffRequestType category, final Employee.Gender gender) { + if (gender == Employee.Gender.MALE) { + return category != TimeOffRequestType.MATERNIDAD + && category != TimeOffRequestType.DIA_DE_LA_MADRE + && category != TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL + && category != TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL; + } else { + return category != TimeOffRequestType.DIA_DEL_PADRE + && category != TimeOffRequestType.PATERNIDAD; + } + } + + private void handleCategorySelection(final TimeOffRequestType selectedCategory) { + if (selectedCategory != null) { + updateAvailableDays(selectedCategory); + } + } + + private void updateAvailableDays(final TimeOffRequestType selectedCategory) { + final TimeOff timeoff = timeOffService.findVacationByCategory(selectedCategory); + final List requests = + timeOffRequestService.findByEmployeeAndCategory(employee.getId(), selectedCategory); + + final TimeOffRequest requestWithBalance = requests.stream() + .filter(request -> request.getDaysBalance() > 0 + && request.getState() != TimeOffRequestStatus.VENCIDO + && request.getState() != TimeOffRequestStatus.RECHAZADO) + .max(Comparator.comparing(TimeOffRequest::getStartDate)) + .orElse(null); + + if (requestWithBalance != null) { + if (requestWithBalance.getState() == TimeOffRequestStatus.TOMADO + && requestWithBalance.getDaysBalance() > 0) { + daysBalance.setValue(requestWithBalance.getDaysBalance()); + } else { + daysBalance.setValue(timeoff.getDuration()); + } + } else if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ACTUAL + || selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) { + final LocalDate dateOfEntry = employee.getDateOfEntry(); + final LocalDate currentDate = LocalDate.now(); + long yearsOfService = ChronoUnit.YEARS.between(dateOfEntry, currentDate); + + if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) { + yearsOfService -= 1; + } + + if (yearsOfService > 10) { + daysBalance.setValue(30.0); + } else if (yearsOfService > 5) { + daysBalance.setValue(20.0); + } else if (yearsOfService > 1) { + daysBalance.setValue(15.0); + } else { + daysBalance.setValue(0.0); + } + } else { + daysBalance.setValue(timeoff.getDuration()); + } + + setDatePickerLimits(timeoff); + } + + private void setDatePickerLimits(final TimeOff timeoff) { + LocalDate startDateValue; + LocalDate endDateValue = null; + + final UUID employeeId = employee.getId(); + final List previousRequests + = timeOffRequestService.findByEmployeeAndCategory(employeeId, timeoff.getCategory()); + + final int startYear = calculateStartYear(previousRequests); + + startDateValue = determineStartDate(timeoff, startYear); + + if (startDateValue.isBefore(LocalDate.now())) { + startDateValue = determineStartDate(timeoff, startYear + 1); + } + + if (startDateValue != null) { + if (timeoff.getExpiration() != null) { + endDateValue = startDateValue.plusDays(timeoff.getExpiration().intValue() - 1); + } else { + endDateValue = LocalDate.of(startDateValue.getYear(), 12, 31); + } + } else { + startDateValue = LocalDate.now(); + } + + setPickerValues(timeoff, startDateValue); + setPickerLimits(startDateValue, endDateValue); + } + + private int calculateStartYear(final List previousRequests) { + if (previousRequests.isEmpty()) { + return LocalDate.now().getYear(); + } + + int lastRequestYear = previousRequests.stream() + .max(Comparator.comparing(TimeOffRequest::getStartDate)) + .map(request -> request.getStartDate().getYear()) + .orElse(LocalDate.now().getYear()); + + if (previousRequests.getLast().getState() != TimeOffRequestStatus.RECHAZADO) { + lastRequestYear = lastRequestYear + 1; + } + + int currentYear = LocalDate.now().getYear(); + return Math.max(lastRequestYear, currentYear); + } + + private LocalDate determineStartDate(final TimeOff timeoff, final int startYear) { + if (timeoff.getCategory() == TimeOffRequestType.CUMPLEAÑOS && employee.getBirthday() != null) { + return LocalDate.of(startYear, employee.getBirthday().getMonth(), employee.getBirthday().getDayOfMonth()); + } + + if (timeoff.getDate() != null) { + return LocalDate.of(startYear, timeoff.getDate().getMonthValue(), timeoff.getDate().getDayOfMonth()); + } + + return LocalDate.now(); + } + + private void setPickerValues(final TimeOff timeoff, final LocalDate startDateValue) { + startDate.setValue(startDateValue); + + if ((timeoff.getDuration() != null && timeoff.getDuration() == 0.5) + || timeoff.getCategory() == TimeOffRequestType.PERMISOS_DE_SALUD + || timeoff.getCategory() == TimeOffRequestType.CUMPLEAÑOS) { + + endDate.setValue(startDateValue); + } else { + int durationDays = (timeoff.getDuration() != null ? timeoff.getDuration().intValue() - 1 : 0); + endDate.setValue(startDateValue.plusDays(durationDays)); + } + } + + private void setPickerLimits(final LocalDate startDateValue, final LocalDate endDateValue) { + startDate.setMin(startDateValue); + startDate.setMax(endDateValue); + endDate.setMin(startDateValue); + endDate.setMax(endDateValue); + } + + private void updateDatePickerMinValues() { + LocalDate startDateValue = startDate.getValue(); + if (daysBalance.getValue() != null) { + if (daysBalance.getValue() == 0.5) { + endDate.setValue(startDateValue.plusDays(0)); + } else { + endDate.setValue(startDateValue.plusDays(daysBalance.getValue().intValue() - 1)); + } + + calculateDays(); + } + } + + private void calculateDays() { + LocalDate startDateValue = startDate.getValue(); + LocalDate endDateValue = endDate.getValue(); + Double availableDaysValue = daysBalance.getValue(); + + if (areDatesValid(startDateValue, endDateValue)) { + long workDays = countWorkDaysBetween(startDateValue, endDateValue); + + daysToBeTake.setValue((double) workDays); + daysBalance.setValue(daysBalance.getValue() - workDays); + + double daysToBeTaken = calculateDaysBetween(startDateValue, endDateValue); + setDaysToBeTakenField(daysToBeTaken); + + double balanceDays = calculateBalanceDays(availableDaysValue, daysToBeTake.getValue()); + daysBalance.setValue(balanceDays); + + if (balanceDays < 0.0) { + clearFields(); + } + + if (daysToBeTake.getValue() > 10 + && (category.getValue() == TimeOffRequestType.VACACION_GESTION_ANTERIOR + || category.getValue() == TimeOffRequestType.VACACION_GESTION_ACTUAL)) { + clearFields(); + } + } + } + + private void clearFields() { + daysToBeTake.clear(); + daysBalance.clear(); + endDate.clear(); + } + + private boolean areDatesValid(final LocalDate startDate, final LocalDate endDate) { + 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 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) { + final TimeOffRequest timeOffRequest = getEntity(); + + if (timeOffRequest.getCategory() == TimeOffRequestType.PERMISOS_DE_SALUD + || timeOffRequest.getCategory() == TimeOffRequestType.CUMPLEAÑOS + || timeOffRequest.getCategory() == TimeOffRequestType.DIA_DEL_PADRE + || timeOffRequest.getCategory() == TimeOffRequestType.DIA_DE_LA_MADRE + || timeOffRequest.getCategory() == TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL + || timeOffRequest.getCategory() == TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL) { + daysToBeTake.setValue(0.5); + } else { + daysToBeTake.setValue(daysToBeTaken); + } + } + + private double calculateBalanceDays(final double availableDays, final double daysToBeTaken) { + return availableDays - daysToBeTaken; + } + private void setFieldsReadOnly(final boolean option) { state.setReadOnly(option); expiration.setReadOnly(option); startDate.setReadOnly(option); endDate.setReadOnly(option); - availableDays.setReadOnly(option); + daysBalance.setReadOnly(option); daysToBeTake.setReadOnly(option); } private void saveRequest(final TimeOffRequest request) { - if (isFormValid()) { - setRequestFieldValues(request); - requestService.saveTimeOffRequest(request); + if (!isFormValid()) { + Notification.show("Por favor, complete los campos antes de guardar"); + return; + } + + prepareRequest(request); + + if (request.getCategory() == TimeOffRequestType.VACACION_GESTION_ACTUAL) { + handleVacationRequest(request); + } else { + handleExistingRequests(request); + } + + long differentDays = ChronoUnit.DAYS.between(LocalDate.now(), request.getStartDate()); + if (differentDays >= -15 && differentDays <= 90) { + timeOffRequestService.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 void setRequestFieldValues(final TimeOffRequest request) { - request.setState(state.getValue()); - request.setExpiration(expiration.getValue()); + private void prepareRequest(final TimeOffRequest request) { request.setStartDate(startDate.getValue()); - request.setEndDate(endDate.getValue()); - request.setAvailableDays(availableDays.getValue()); - request.setDaysToBeTake(daysToBeTake.getValue()); + request.setAvailableDays(daysBalance.getValue()); + request.setExpiration(endDate.getValue()); + request.setState(TimeOffRequestStatus.SOLICITADO); + } + + private void handleExistingRequests(final TimeOffRequest request) { + final List existingRequests = + timeOffRequestService.findByEmployeeAndCategory(employee.getId(), request.getCategory()); + + int maxRequests = request.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD + && !request.getCategory().name().startsWith("VACACION") + && request.getCategory() != TimeOffRequestType.CUMPLEAÑOS + ? 2 : 1; + + if (existingRequests.size() >= maxRequests) { + existingRequests.stream() + .min(Comparator.comparing(TimeOffRequest::getStartDate)) + .ifPresent(oldestRequest -> timeOffRequestService.deleteTimeOffRequest(oldestRequest.getId())); + } + } + + private void handleVacationRequest(final TimeOffRequest request) { + final List existingRequests = timeOffRequestService.findByEmployeeAndCategory( + employee.getId(), + TimeOffRequestType.VACACION_GESTION_ACTUAL + ); + if (!existingRequests.isEmpty()) { + TimeOffRequest existingRequest = existingRequests.getFirst(); + existingRequest.setCategory(TimeOffRequestType.VACACION_GESTION_ANTERIOR); + timeOffRequestService.saveTimeOffRequest(existingRequest); + } } private void closeForm() { - getUI().ifPresent(ui -> ui.navigate(TimeOffRequestsByEmployeeView.class, employee.getId().toString())); + getUI().ifPresent(ui -> ui.navigate(TimeOffRequestsListView.class)); } private boolean isFormValid() { @@ -116,27 +519,7 @@ public class TimeOffRequestView extends BeanValidationForm imple && expiration.getValue() != null && startDate.getValue() != null && endDate.getValue() != null - && availableDays.getValue() != null + && daysBalance.getValue() != null && daysToBeTake.getValue() != null; } - - private void configureViewOrEditAction(final String action) { - if ("edit".equals(action) && !request.getId().toString().isEmpty()) { - setFieldsReadOnly(false); - } else if ("view".equals(action) && !request.getId().toString().isEmpty()) { - setFieldsReadOnly(true); - } - } - - private H3 createEmployeeHeader() { - return new H3("Empleado: " + employee.getFirstName() + " " + employee.getLastName()); - } - - private H3 createTeamHeader() { - return new H3("Equipo: " + employee.getTeam().getName()); - } - - private H3 createCategoryHeader() { - return new H3("Categoría: " + request.getCategory()); - } } diff --git a/src/main/java/com/primefactorsolutions/views/timeoff/TimeOffPendingRequestsListView.java b/src/main/java/com/primefactorsolutions/views/timeoff/TimeOffRequestsListView.java similarity index 70% rename from src/main/java/com/primefactorsolutions/views/timeoff/TimeOffPendingRequestsListView.java rename to src/main/java/com/primefactorsolutions/views/timeoff/TimeOffRequestsListView.java index ca0ab40..527efe4 100644 --- a/src/main/java/com/primefactorsolutions/views/timeoff/TimeOffPendingRequestsListView.java +++ b/src/main/java/com/primefactorsolutions/views/timeoff/TimeOffRequestsListView.java @@ -12,6 +12,7 @@ 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.button.ButtonVariant; import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.contextmenu.MenuItem; import com.vaadin.flow.component.icon.VaadinIcon; @@ -46,36 +47,43 @@ import static com.primefactorsolutions.views.Constants.PAGE_SIZE; @SpringComponent @Scope("prototype") -@PageTitle("Pending Requests") -@Route(value = "/time-off/requests/pending", layout = MainLayout.class) +@PageTitle("Time-Off Requests") +@Route(value = "/time-off/requests", layout = MainLayout.class) @PermitAll -public class TimeOffPendingRequestsListView extends BaseView { +public class TimeOffRequestsListView extends BaseView { private final TimeOffRequestService requestService; private final EmployeeService employeeService; private final TeamService teamService; - private final PagingGrid pendingRequestsGrid = new PagingGrid<>(); + private final PagingGrid requestsGrid = new PagingGrid<>(); private ComboBox employeeFilter; private ComboBox teamFilter; private ComboBox categoryFilter; - public TimeOffPendingRequestsListView(final AuthenticationContext authenticationContext, - final TimeOffRequestService requestService, - final EmployeeService employeeService, - final TeamService teamService) { + public TimeOffRequestsListView(final AuthenticationContext authenticationContext, + final TimeOffRequestService requestService, + final EmployeeService employeeService, + final TeamService teamService) { super(authenticationContext); this.requestService = requestService; this.employeeService = employeeService; this.teamService = teamService; initializeView(); - refreshGeneralPendingRequestsGrid(null, null, null); + refreshGeneralRequestsGrid(null, null, null); } private void initializeView() { - Button downloadButton = new Button("Descargar reporte de rechazos", event -> downloadReport()); - getCurrentPageLayout().add(downloadButton); + final Button newRequestButton = new Button("Crear nueva peticion", event -> navigateToAddNew()); + newRequestButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY); + final Button downloadReportButton = new Button("Descargar reporte de rechazos", event -> downloadReport()); + final HorizontalLayout hl = new HorizontalLayout(newRequestButton, downloadReportButton); + getCurrentPageLayout().add(hl); setupFilters(); - setupPendingRequestsGrid(); + setupRequestsGrid(); + } + + private void navigateToAddNew() { + getUI().ifPresent(ui -> ui.navigate(TimeOffRequestView.class, "new")); } private void setupFilters() { @@ -87,11 +95,11 @@ public class TimeOffPendingRequestsListView extends BaseView { getCurrentPageLayout().add(hl); } - private void setupPendingRequestsGrid() { - pendingRequestsGrid.addColumn(this::getEmployeeFullName).setHeader("Empleado"); - pendingRequestsGrid.addColumn(this::getTeamName).setHeader("Equipo"); - pendingRequestsGrid.addColumn(this::getCategory).setHeader("Categoría"); - pendingRequestsGrid.addComponentColumn((ValueProvider) timeOffRequest -> { + private void setupRequestsGrid() { + requestsGrid.addColumn(this::getEmployeeFullName).setHeader("Empleado"); + requestsGrid.addColumn(this::getTeamName).setHeader("Equipo"); + requestsGrid.addColumn(this::getCategory).setHeader("Categoría"); + requestsGrid.addComponentColumn((ValueProvider) timeOffRequest -> { final MenuBar menuBar = new MenuBar(); menuBar.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE); final MenuItem approveItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.CHECK, "Aprobar"); @@ -103,58 +111,58 @@ public class TimeOffPendingRequestsListView extends BaseView { return menuBar; }); - pendingRequestsGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM); - pendingRequestsGrid.setPageSize(PAGE_SIZE); + requestsGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM); + requestsGrid.setPageSize(PAGE_SIZE); - getCurrentPageLayout().add(pendingRequestsGrid); + getCurrentPageLayout().add(requestsGrid); } private void actionForRequest(final UUID selectedRequestId, final TimeOffRequestStatus status) { TimeOffRequest request = requestService.findTimeOffRequest(selectedRequestId); request.setState(status); requestService.saveTimeOffRequest(request); - refreshGeneralPendingRequestsGrid(null, null, null); + refreshGeneralRequestsGrid(null, null, null); } - private void refreshGeneralPendingRequestsGrid(final Employee employee, - final Team team, - final TimeOffRequestType category) { - pendingRequestsGrid.setPagingDataProvider((page, pageSize) -> { - int start = (int) (page * pendingRequestsGrid.getPageSize()); - return fetchFilteredPendingRequests(start, pageSize, employee, team, category); + private void refreshGeneralRequestsGrid(final Employee employee, + final Team team, + final TimeOffRequestType category) { + requestsGrid.setPagingDataProvider((page, pageSize) -> { + int start = (int) (page * requestsGrid.getPageSize()); + return fetchFilteredRequests(start, pageSize, employee, team, category); }); - pendingRequestsGrid.getDataProvider().refreshAll(); + requestsGrid.getDataProvider().refreshAll(); } - private List fetchFilteredPendingRequests(final int start, - final int pageSize, - final Employee employee, - final Team team, - final TimeOffRequestType category) { - List filteredPendingRequests - = requestService.findRequestsByState(TimeOffRequestStatus.SOLICITADO); + private List fetchFilteredRequests(final int start, + final int pageSize, + final Employee employee, + final Team team, + final TimeOffRequestType category) { + List filteredRequests + = requestService.findAllTimeOffRequests(); if (employee != null) { - filteredPendingRequests = filteredPendingRequests.stream() + filteredRequests = filteredRequests.stream() .filter(emp -> emp.getEmployee().getId().equals(employee.getId())) .collect(Collectors.toList()); } if (team != null) { - filteredPendingRequests = filteredPendingRequests.stream() + filteredRequests = filteredRequests.stream() .filter(emp -> emp.getEmployee().getTeam() != null && emp.getEmployee().getTeam().getId().equals(team.getId())) .collect(Collectors.toList()); } if (category != null) { - filteredPendingRequests = filteredPendingRequests.stream() + filteredRequests = filteredRequests.stream() .filter(emp -> emp.getCategory().equals(category)) .collect(Collectors.toList()); } - int end = Math.min(start + pageSize, filteredPendingRequests.size()); - return filteredPendingRequests.subList(start, end); + int end = Math.min(start + pageSize, filteredRequests.size()); + return filteredRequests.subList(start, end); } private String getEmployeeFullName(final TimeOffRequest request) { @@ -163,8 +171,7 @@ public class TimeOffPendingRequestsListView extends BaseView { } private String getEmployeeFullNameLabel(final Employee employee) { - return "TODOS".equals(employee.getFirstName()) - ? "TODOS" : employee.getFirstName() + " " + employee.getLastName(); + return employee.getFirstName() + " " + employee.getLastName(); } private String getTeamName(final TimeOffRequest request) { @@ -173,7 +180,7 @@ public class TimeOffPendingRequestsListView extends BaseView { } private String getTeamLabel(final Team team) { - return "TODOS".equals(team.getName()) ? "TODOS" : team.getName(); + return team.getName(); } private String getCategory(final TimeOffRequest request) { @@ -183,13 +190,13 @@ public class TimeOffPendingRequestsListView extends BaseView { private ComboBox createEmployeeFilter() { employeeFilter = new ComboBox<>("Empleado"); employeeFilter.setClearButtonVisible(true); - employeeFilter.setPlaceholder("TODOS"); + employeeFilter.setPlaceholder("Seleccionar ..."); final List employees = new ArrayList<>(employeeService.findAllEmployees()); employeeFilter.setItems(employees); employeeFilter.setItemLabelGenerator(this::getEmployeeFullNameLabel); employeeFilter.addValueChangeListener(event -> - refreshGeneralPendingRequestsGrid( + refreshGeneralRequestsGrid( event.getValue(), teamFilter.getValue(), categoryFilter.getValue() @@ -201,12 +208,12 @@ public class TimeOffPendingRequestsListView extends BaseView { private ComboBox createTeamFilter() { teamFilter = new ComboBox<>("Equipo"); teamFilter.setClearButtonVisible(true); - teamFilter.setPlaceholder("TODOS"); + teamFilter.setPlaceholder("Seleccionar ..."); final List teams = new ArrayList<>(teamService.findAllTeams()); teamFilter.setItems(teams); teamFilter.setItemLabelGenerator(this::getTeamLabel); teamFilter.addValueChangeListener(event -> - refreshGeneralPendingRequestsGrid( + refreshGeneralRequestsGrid( employeeFilter.getValue(), event.getValue(), categoryFilter.getValue() @@ -217,11 +224,11 @@ public class TimeOffPendingRequestsListView extends BaseView { private ComboBox createCategoryFilter() { categoryFilter = new ComboBox<>("Categoría"); - categoryFilter.setPlaceholder("TODOS"); + categoryFilter.setPlaceholder("Seleccionar ..."); categoryFilter.setClearButtonVisible(true); categoryFilter.setItems(TimeOffRequestType.values()); categoryFilter.addValueChangeListener(event -> - refreshGeneralPendingRequestsGrid( + refreshGeneralRequestsGrid( employeeFilter.getValue(), teamFilter.getValue(), event.getValue() diff --git a/src/main/java/com/primefactorsolutions/views/timeoff/TimeOffRequestsByEmployeeView.java b/src/main/java/com/primefactorsolutions/views/timeoff/TimeOffSummaryListView.java similarity index 55% rename from src/main/java/com/primefactorsolutions/views/timeoff/TimeOffRequestsByEmployeeView.java rename to src/main/java/com/primefactorsolutions/views/timeoff/TimeOffSummaryListView.java index 2f1991e..38acd93 100644 --- a/src/main/java/com/primefactorsolutions/views/timeoff/TimeOffRequestsByEmployeeView.java +++ b/src/main/java/com/primefactorsolutions/views/timeoff/TimeOffSummaryListView.java @@ -1,666 +1,774 @@ -package com.primefactorsolutions.views.timeoff; - -import com.primefactorsolutions.model.*; -import com.primefactorsolutions.service.EmployeeService; -import com.primefactorsolutions.service.TimeOffRequestService; -import com.primefactorsolutions.service.TimeOffService; -import com.primefactorsolutions.views.BaseView; -import com.primefactorsolutions.views.MainLayout; -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; -import com.vaadin.flow.component.html.Span; -import com.vaadin.flow.component.icon.VaadinIcon; -import com.vaadin.flow.component.menubar.MenuBar; -import com.vaadin.flow.component.menubar.MenuBarVariant; -import com.vaadin.flow.component.orderedlayout.HorizontalLayout; -import com.vaadin.flow.component.orderedlayout.VerticalLayout; -import com.vaadin.flow.function.ValueProvider; -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 com.vaadin.flow.spring.security.AuthenticationContext; -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.time.Year; -import java.util.*; -import java.util.stream.Collectors; - -import static com.primefactorsolutions.views.Constants.PAGE_SIZE; -import static com.primefactorsolutions.views.util.MenuBarUtils.createIconItem; - -@SpringComponent -@PermitAll -@Scope("prototype") -@PageTitle("Employee Request") -@Route(value = "/time-off/requests", layout = MainLayout.class) -public class TimeOffRequestsByEmployeeView extends BaseView implements HasUrlParameter { - - private final TimeOffRequestService requestService; - private final EmployeeService employeeService; - private final TimeOffService timeOffService; - private final PagingGrid requestGrid = new PagingGrid<>(TimeOffRequest.class); - private List requests = Collections.emptyList(); - private ComboBox categoryFilter; - private ComboBox stateFilter; - private UUID employeeId; - private double remainingHolidayDays; - private double remainingPersonalDays; - private double remainingVacationDays; - - public TimeOffRequestsByEmployeeView(final AuthenticationContext authenticationContext, - final TimeOffRequestService requestService, - final EmployeeService employeeService, - final TimeOffService timeOffService) { - super(authenticationContext); - this.requestService = requestService; - this.employeeService = employeeService; - this.timeOffService = timeOffService; - } - - private void initializeView() { - requestService.updateRequestStatuses(); - Button downloadButton = new Button("Descargar reporte", event -> downloadReport()); - getCurrentPageLayout().add(downloadButton); - setupFilters(); - setupGrid(); - getCurrentPageLayout().add(requestGrid, new H3("Balance"), createSummaryLayout()); - refreshRequestGrid(null, null); - } - - private void setupFilters() { - categoryFilter = createCategoryFilter(); - stateFilter = createStateFilter(); - HorizontalLayout hl = new HorizontalLayout(categoryFilter, stateFilter); - getCurrentPageLayout().add(hl); - } - - private ComboBox createCategoryFilter() { - categoryFilter = new ComboBox<>("Categoría"); - categoryFilter.setItems(TimeOffRequestType.values()); - categoryFilter.setValue(TimeOffRequestType.values()[0]); - categoryFilter.addValueChangeListener(event -> refreshRequestGrid(event.getValue(), stateFilter.getValue())); - return categoryFilter; - } - - private ComboBox createStateFilter() { - stateFilter = new ComboBox<>("Estado de la solicitud"); - stateFilter.setItems(TimeOffRequestStatus.values()); - stateFilter.setValue(TimeOffRequestStatus.values()[0]); - stateFilter.addValueChangeListener(event -> refreshRequestGrid(categoryFilter.getValue(), event.getValue())); - return stateFilter; - } - - private void setupGrid() { - requestGrid.setColumns( - "category", - "state", - "startDate", - "endDate", - "daysToBeTake"); - - requestGrid.getColumnByKey("category").setHeader("Categoría"); - requestGrid.getColumnByKey("state").setHeader("Estado"); - requestGrid.getColumnByKey("startDate").setHeader("Fecha de Inicio"); - requestGrid.getColumnByKey("endDate").setHeader("Fecha de Fin"); - requestGrid.getColumnByKey("daysToBeTake").setHeader("Días a Tomar"); - requestGrid.addComponentColumn((ValueProvider) timeOffRequest -> { - final MenuBar menuBar = new MenuBar(); - menuBar.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE); - final MenuItem view = createIconItem(menuBar, VaadinIcon.EYE, "View"); - view.addClickListener((ComponentEventListener>) menuItemClickEvent -> - navigateToViewRequest(timeOffRequest)); - final MenuItem edit = createIconItem(menuBar, VaadinIcon.PENCIL, "Edit"); - edit.addClickListener((ComponentEventListener>) menuItemClickEvent -> - navigateToEditRequest(timeOffRequest)); - return menuBar; - }); - - requestGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM); - requestGrid.setPageSize(PAGE_SIZE); - } - - private Set getStandardExclusions() { - return Set.of( - TimeOffRequestType.MATERNIDAD, - TimeOffRequestType.PATERNIDAD, - TimeOffRequestType.MATRIMONIO, - TimeOffRequestType.DUELO_1ER_GRADO, - TimeOffRequestType.DUELO_2ER_GRADO, - TimeOffRequestType.DIA_DEL_PADRE, - TimeOffRequestType.DIA_DE_LA_MADRE - ); - } - - private Set getMaleSpecificExclusions() { - return Set.of( - TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL, - TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL - ); - } - - private VerticalLayout createSummaryLayout() { - Employee employee = employeeService.getEmployee(employeeId); - boolean isMale = employee.getGender() == Employee.Gender.MALE; - int currentYear = LocalDate.now().getYear(); - LocalDate currentDate = LocalDate.now(); - - List timeOffs = timeOffService.findVacations(); - - double healthLicence = getHealthLicence(employeeId); - - double totalFixedAndMovableHolidays = calculateHolidayDays(timeOffs); - double totalPersonalDays = calculatePersonalDays(timeOffs, isMale); - List vacationDays = calculateVacationDays(employee); - - 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); - - remainingHolidayDays = calculateRemainingHolidayDays( - totalFixedAndMovableHolidays, - utilizedFixedAndMovableHolidays, - employee.getDateOfExit(), - currentDate - ); - remainingPersonalDays = calculateRemainingPersonalDays( - totalPersonalDays, - utilizedPersonalDays, - healthLicence, - employee.getDateOfExit(), - currentDate - ); - remainingVacationDays = calculateRemainingVacationDays( - totalVacationCurrentDays, - totalVacationPreviousDays, - employee.getDateOfExit(), - currentDate - ); - - double totalAvailableDays = remainingHolidayDays + remainingPersonalDays + remainingVacationDays; - - return new VerticalLayout( - new Span("Total feriados fijos y movibles: " + remainingHolidayDays), - new Span("Total días libres personales: " + remainingPersonalDays), - new Span("Total vacaciones pendientes de uso: " + remainingVacationDays), - new Span("TOTAL GENERAL DE DÍAS DISPONIBLES: " + totalAvailableDays) - ); - } - - private double getHealthLicence(final UUID employeeId) { - List 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 timeOffs) { - return timeOffs.stream() - .filter(req -> req.getType() != TimeOff.Type.OTHER) - .mapToDouble(TimeOff::getDuration) - .sum(); - } - - private double calculatePersonalDays(final List timeOffs, final boolean isMale) { - return timeOffs.stream() - .filter(req -> req.getType() == TimeOff.Type.OTHER) - .filter(req -> !getStandardExclusions().contains(req.getCategory())) - .filter(req -> !(isMale && getMaleSpecificExclusions().contains(req.getCategory()))) - .filter(req -> !req.getCategory().name().startsWith("VACACION")) - .filter(req -> req.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD) - .mapToDouble(TimeOff::getDuration) - .sum(); - } - - private List calculateVacationDays(final Employee employee) { - List vacationDays = new ArrayList<>(); - - if (employee.getDateOfEntry() != null) { - LocalDate entryDate = employee.getDateOfEntry(); - LocalDate today = LocalDate.now(); - - boolean hasAnniversaryPassed = entryDate.getMonthValue() < today.getMonthValue() - || (entryDate.getMonthValue() == today.getMonthValue() && entryDate.getDayOfMonth() - <= today.getDayOfMonth()); - - LocalDate previousVacationYearDate; - LocalDate currentVacationYearDate; - - if (hasAnniversaryPassed) { - previousVacationYearDate = LocalDate.of( - today.getYear() - 1, - entryDate.getMonth(), - entryDate.getDayOfMonth() - ); - currentVacationYearDate = LocalDate.of( - today.getYear(), - entryDate.getMonth(), - entryDate.getDayOfMonth() - ); - } else { - previousVacationYearDate = LocalDate.of( - today.getYear() - 2, - entryDate.getMonth(), - entryDate.getDayOfMonth() - ); - currentVacationYearDate = LocalDate.of( - today.getYear() - 1, - entryDate.getMonth(), - entryDate.getDayOfMonth() - ); - } - - vacationDays.add(calculateVacationDaysSinceEntry(entryDate, previousVacationYearDate)); - vacationDays.add(calculateVacationDaysSinceEntry(entryDate, currentVacationYearDate)); - } else { - vacationDays.add(0.0); - vacationDays.add(0.0); - } - return vacationDays; - } - - private double calculateHolidayUtilizedDays(final int year) { - return requests.stream() - .filter(this::verificationIsHoliday) - .filter(req -> req.getState() == TimeOffRequestStatus.TOMADO - || req.getState() == TimeOffRequestStatus.APROBADO) - .filter(req -> getStartDateYear(req) == year) - .mapToDouble(TimeOffRequest::getDaysToBeTake) - .sum(); - } - - private double calculatePersonalDaysUtilized(final boolean isMale, final int year) { - return requests.stream() - .filter(req -> !verificationIsHoliday(req)) - .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")) - .filter(req -> req.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD) - .filter(req -> getStartDateYear(req) == year) - .mapToDouble(TimeOffRequest::getDaysToBeTake) - .sum(); - } - - private int getStartDateYear(final TimeOffRequest request) { - if (request.getStartDate() != null) { - return request.getStartDate().getYear(); - } - return 0; - } - - private double calculateVacationDaysSinceEntry(final LocalDate dateOfEntry, final LocalDate date) { - int yearsOfService = dateOfEntry != null ? Period.between(dateOfEntry, date).getYears() : 0; - if (yearsOfService > 10) { - return 30; - } - if (yearsOfService > 5) { - return 20; - } - if (yearsOfService > 1) { - return 15; - } - return 0; - } - - private Boolean verificationIsHoliday(final TimeOffRequest request) { - TimeOff timeoff = timeOffService.findVacationByCategory(request.getCategory()); - return timeoff.getType() != TimeOff.Type.OTHER; - } - - private void navigateToEditRequest(final TimeOffRequest request) { - navigateToRequestView(request, "edit"); - } - - private void navigateToViewRequest(final TimeOffRequest request) { - navigateToRequestView(request, "view"); - } - - private void navigateToRequestView(final TimeOffRequest request, final String action) { - getUI().ifPresent(ui -> ui.navigate(TimeOffRequestView.class, request.getId().toString() + "/" + action)); - } - - private void refreshRequestGrid(final TimeOffRequestType category, final TimeOffRequestStatus state) { - requestGrid.setPagingDataProvider((page, pageSize) -> { - int start = (int) (page * requestGrid.getPageSize()); - return fetchFilteredTimeOffRequests(start, pageSize, category, state); - }); - requestGrid.getDataProvider().refreshAll(); - } - - private List fetchFilteredTimeOffRequests(final int start, - final int pageSize, - final TimeOffRequestType category, - final TimeOffRequestStatus state) { - - requests = requestService.findRequestsByEmployeeId(employeeId); - generateRequests(); - if (category != null && !"TODOS".equals(category.name())) { - requests = requests.stream() - .filter(req -> req.getCategory().equals(category)) - .collect(Collectors.toList()); - } - if (state != null && !"TODOS".equals(state.name())) { - requests = requests.stream() - .filter(req -> req.getState().equals(state)) - .collect(Collectors.toList()); - } - int end = Math.min(start + pageSize, requests.size()); - return requests.subList(start, end); - } - - public void generateRequests() { - boolean isMale = isEmployeeMale(); - - for (TimeOffRequestType type : TimeOffRequestType.values()) { - if (shouldIncludeRequest(type) && isValidRequestType(type, isMale)) { - TimeOffRequest request = createRequest(type); - if (isVacationExpired(request)) { - request.setState(TimeOffRequestStatus.VENCIDO); - } else { - request.setState(TimeOffRequestStatus.PENDIENTE); - } - requests.add(request); - } - } - } - - private boolean isEmployeeMale() { - return employeeService.getEmployee(employeeId).getGender() == Employee.Gender.MALE; - } - - private boolean isValidRequestType(final TimeOffRequestType type, final boolean isMale) { - return !getStandardExclusions().contains(type) - && !(isMale && getMaleSpecificExclusions().contains(type)); - } - - private TimeOffRequest createRequest(final TimeOffRequestType type) { - TimeOffRequest request = new TimeOffRequest(); - request.setCategory(type); - return request; - } - - private boolean isVacationExpired(final TimeOffRequest request) { - TimeOff timeoff = timeOffService.findVacationByCategory(request.getCategory()); - - if (timeoff != null && timeoff.getDate() != null) { - int vacationMonth = timeoff.getDate().getMonthValue(); - int vacationDay = timeoff.getDate().getDayOfMonth(); - int currentMonth = LocalDate.now().getMonthValue(); - int currentDay = LocalDate.now().getDayOfMonth(); - - return vacationMonth < currentMonth || (vacationMonth == currentMonth && vacationDay < currentDay); - } - - return false; - } - - private boolean shouldIncludeRequest(final TimeOffRequestType type) { - List existingRequest = requestService.findByEmployeeAndCategory(employeeId, type); - return existingRequest.isEmpty(); - } - - private String getDateString(final LocalDate date) { - return (date != null) ? date.toString() : ""; - } - - private ByteArrayInputStream generatePdfReport() { - try (PDDocument document = new PDDocument(); ByteArrayOutputStream out = new ByteArrayOutputStream()) { - PDPage page = new PDPage(); - document.addPage(page); - - Employee employee = employeeService.getEmployee(employeeId); - List 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 { - contentStream = new PDPageContentStream(document, page); - - contentStream.setFont(PDType1Font.TIMES_BOLD, 18); - contentStream.beginText(); - contentStream.newLineAtOffset(200, 750); - contentStream.showText("Reporte de Vacaciones"); - contentStream.endText(); - - contentStream.setFont(PDType1Font.TIMES_ROMAN, 14); - contentStream.beginText(); - contentStream.newLineAtOffset(50, 700); - contentStream.showText("Empleado: " + employee.getFirstName() + " " + employee.getLastName()); - contentStream.newLineAtOffset(0, -15); - contentStream.showText("Equipo: " + employee.getTeam().getName()); - contentStream.endText(); - - float tableTopY = 650; - float margin = 50; - float cellHeight = 20; - String[] headers = {"Categoría", "Estado", "Fecha de Inicio", "Fecha de Fin", "Días a Tomar"}; - int columns = headers.length; - - float[] columnWidths = new float[columns]; - for (int i = 0; i < columns; i++) { - columnWidths[i] = getMaxColumnWidth(headers[i], requests, i); - } - - contentStream.setFont(PDType1Font.TIMES_BOLD, 10); - float currentX = margin; - for (int i = 0; i < columns; i++) { - contentStream.addRect(currentX, tableTopY, columnWidths[i], -cellHeight); - contentStream.beginText(); - contentStream.newLineAtOffset(currentX + 5, tableTopY - 15); - contentStream.showText(headers[i]); - contentStream.endText(); - currentX += columnWidths[i]; - } - contentStream.stroke(); - - contentStream.setFont(PDType1Font.TIMES_ROMAN, 10); - float currentY = tableTopY - cellHeight; - for (TimeOffRequest request : filteredRequests) { - String startDate = getDateString(request.getStartDate()); - String endDate = getDateString(request.getEndDate()); - - String[] rowData = { - request.getCategory().name(), - request.getState().name(), - startDate, - endDate, - String.valueOf(request.getDaysToBeTake() != null ? request.getDaysToBeTake() : 0) - }; - - currentX = margin; - for (int i = 0; i < columns; i++) { - contentStream.addRect(currentX, currentY, columnWidths[i], -cellHeight); - contentStream.beginText(); - contentStream.newLineAtOffset(currentX + 5, currentY - 15); - contentStream.showText(rowData[i]); - contentStream.endText(); - currentX += columnWidths[i]; - } - contentStream.stroke(); - currentY -= cellHeight; - - if (currentY < 50) { - contentStream.close(); - page = new PDPage(); - document.addPage(page); - contentStream = new PDPageContentStream(document, page); - currentY = 750; - } - } - - currentY -= 30; - - if (currentY < 80) { - contentStream.close(); - page = new PDPage(); - document.addPage(page); - contentStream = new PDPageContentStream(document, page); - currentY = 750; - } - - contentStream.setFont(PDType1Font.TIMES_ROMAN, 14); - contentStream.beginText(); - contentStream.newLineAtOffset(50, currentY - 20); - contentStream.showText("Total feriados fijos y movibles: " + remainingHolidayDays); - contentStream.newLineAtOffset(0, -20); - contentStream.showText("Total días libres personales: " + remainingPersonalDays); - contentStream.newLineAtOffset(0, -20); - contentStream.showText("Total vacaciones pendientes de uso: " + remainingVacationDays); - contentStream.newLineAtOffset(0, -20); - contentStream.showText("TOTAL GENERAL DE DÍAS DISPONIBLES: " - + (remainingHolidayDays + remainingPersonalDays + remainingVacationDays)); - contentStream.endText(); - - } finally { - if (contentStream != null) { - contentStream.close(); - } - } - document.save(out); - return new ByteArrayInputStream(out.toByteArray()); - } catch (IOException e) { - throw new UncheckedIOException("Error al generar el reporte", e); - } - } - - private float getMaxColumnWidth(final String header, final List 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(), - employee.getDateOfExit() - ); - requestGrid.setItems(requests); - initializeView(); - } - - private void setViewTitle(final String employeeName, final String employeeTeam, final LocalDate dateOfExit) { - getCurrentPageLayout().addComponentAsFirst(new H3("Nombre del empleado: " + employeeName)); - getCurrentPageLayout().addComponentAtIndex(1, new H3("Equipo: " + employeeTeam)); - - if (dateOfExit != null) { - getCurrentPageLayout().addComponentAtIndex( - 2, new H3("Descontado a cero en fecha " - + dateOfExit - + " por pago de finiquito incluidas duodecima.")); - } - } -} +package com.primefactorsolutions.views.timeoff; + +import com.primefactorsolutions.model.*; +import com.primefactorsolutions.service.EmployeeService; +import com.primefactorsolutions.service.TeamService; +import com.primefactorsolutions.service.TimeOffRequestService; +import com.primefactorsolutions.service.TimeOffService; +import com.primefactorsolutions.views.BaseView; +import com.primefactorsolutions.views.MainLayout; +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.html.Span; +import com.vaadin.flow.component.orderedlayout.HorizontalLayout; +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 com.vaadin.flow.spring.security.AuthenticationContext; +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.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.time.Year; +import java.util.*; +import java.util.stream.Collectors; + +import static com.primefactorsolutions.views.Constants.PAGE_SIZE; + +@SpringComponent +@Scope("prototype") +@PageTitle("Requests") +@Route(value = "/time-off/summary", layout = MainLayout.class) +@PermitAll +public class TimeOffSummaryListView extends BaseView { + + private final TimeOffRequestService requestService; + private final EmployeeService employeeService; + private final TeamService teamService; + private final TimeOffService timeOffService; + private final PagingGrid requestGrid = new PagingGrid<>(); + private ComboBox employeeFilter; + private ComboBox teamFilter; + + public TimeOffSummaryListView( + final AuthenticationContext authenticationContext, + final TimeOffRequestService requestService, + final EmployeeService employeeService, + final TeamService teamService, + final TimeOffService timeOffService) { + super(authenticationContext); + this.requestService = requestService; + this.employeeService = employeeService; + this.teamService = teamService; + this.timeOffService = timeOffService; + initializeView(); + refreshGeneralRequestGrid(null, null); + } + + private void initializeView() { + requestService.updateRequestStatuses(); + + if (isRoleAdmin()) { + final Button downloadButton = new Button("Descargar reporte", event -> downloadReport()); + getCurrentPageLayout().add(downloadButton); + } + + setupFilters(); + setupRequestGrid(); + getCurrentPageLayout().add(requestGrid); + } + + private void setupFilters() { + final HorizontalLayout hl = new HorizontalLayout(); + hl.add(createEmployeeFilter()); + hl.add(createTeamFilter()); + getCurrentPageLayout().add(hl); + } + + private void setupRequestGrid() { + requestGrid.addColumn(this::getEmployeeFullName).setHeader("Empleado"); + requestGrid.addColumn(this::getTeamName).setHeader("Equipo"); + requestGrid.addColumn(this::getGeneralTotal).setHeader("Total general"); + + requestGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM); + requestGrid.setPageSize(PAGE_SIZE); + } + + private void refreshGeneralRequestGrid(final Employee employee, + final Team team) { + requestGrid.setPagingDataProvider((page, pageSize) -> { + int start = (int) (page * requestGrid.getPageSize()); + return fetchFilteredEmployees(start, pageSize, employee, team); + }); + requestGrid.getDataProvider().refreshAll(); + } + + private List fetchFilteredEmployees(final int start, + final int pageSize, + final Employee employee, + final Team team) { + List filteredEmployees = employeeService.findAllEmployees(); + + if (employee != null) { + filteredEmployees = filteredEmployees.stream() + .filter(emp -> emp.getId().equals(employee.getId())) + .collect(Collectors.toList()); + } + + if (team != null) { + 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 String getEmployeeFullName(final Employee employee) { + return employee.getFirstName() + " " + employee.getLastName(); + } + + private String getTeamName(final Employee employee) { + Team team = employee.getTeam(); + return team != null ? team.getName() : "Sin asignar"; + } + + private String getTeamLabel(final Team team) { + return team.getName(); + } + + private String getEmployeeStatus(final Employee employee) { + Optional activeRequest = requestService + .findByEmployeeAndState(employee.getId(), TimeOffRequestStatus.EN_USO); + return activeRequest.isPresent() ? "EN_DESCANSO" : "EN_FUNCIONES"; + } + + private String getGeneralTotal(final Employee employee) { + List employeeRequests = requestService.findRequestsByEmployeeId(employee.getId()); + List timeOffs = timeOffService.findVacations(); + + List vacationDays = calculateVacationDays(employee); + + double utilizedVacationCurrentDays = vacationDays.get(1); + List vacationCurrentRequests = requestService + .findByEmployeeAndCategory(employee.getId(), 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(employee.getId(), TimeOffRequestType.VACACION_GESTION_ANTERIOR); + if (vacationPreviousRequests != null && !vacationPreviousRequests.isEmpty()) { + utilizedVacationPreviousDays = vacationPreviousRequests.getLast().getDaysBalance(); + } + double totalVacationPreviousDays = vacationDays.getFirst() + - (vacationDays.getFirst() - utilizedVacationPreviousDays); + + + double totalUtilized = calculateTotalUtilized(employeeRequests); + double totalVacations = totalVacationCurrentDays + totalVacationPreviousDays; + double totalAvailable = calculateTotalAvailable(timeOffs, 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); + } + + private Set getExcludedCategories() { + return Set.of( + TimeOffRequestType.MATERNIDAD, + TimeOffRequestType.PATERNIDAD, + TimeOffRequestType.MATRIMONIO, + TimeOffRequestType.DUELO_1ER_GRADO, + TimeOffRequestType.DUELO_2ER_GRADO, + TimeOffRequestType.DIA_DEL_PADRE, + TimeOffRequestType.DIA_DE_LA_MADRE + ); + } + + private Set getGenderSpecificExclusions() { + return Set.of( + TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL, + TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL + ); + } + + private double calculateTotalUtilized(final List employeeRequests) { + 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) + .filter(request -> request.getStartDate() != null && ( + request.getStartDate().getYear() == currentYear + || (request.getCategory().name().startsWith("VACACION") + && request.getStartDate().getYear() == currentYear - 1) + )) + .mapToDouble(request -> request.getDaysToBeTake() != null ? request.getDaysToBeTake() : 0.0) + .sum(); + } + + private List calculateVacationDays(final Employee employee) { + List vacationDays = new ArrayList<>(); + + if (employee.getDateOfEntry() != null) { + LocalDate entryDate = employee.getDateOfEntry(); + LocalDate today = LocalDate.now(); + + boolean hasAnniversaryPassed = entryDate.getMonthValue() < today.getMonthValue() + || (entryDate.getMonthValue() == today.getMonthValue() && entryDate.getDayOfMonth() + <= today.getDayOfMonth()); + + LocalDate previousVacationYearDate; + LocalDate currentVacationYearDate; + + if (hasAnniversaryPassed) { + previousVacationYearDate = LocalDate.of( + today.getYear() - 1, + entryDate.getMonth(), + entryDate.getDayOfMonth() + ); + currentVacationYearDate = LocalDate.of( + today.getYear(), + entryDate.getMonth(), + entryDate.getDayOfMonth() + ); + } else { + previousVacationYearDate = LocalDate.of( + today.getYear() - 2, + entryDate.getMonth(), + entryDate.getDayOfMonth() + ); + currentVacationYearDate = LocalDate.of( + today.getYear() - 1, + entryDate.getMonth(), + entryDate.getDayOfMonth() + ); + } + + vacationDays.add(calculateVacationDaysSinceEntry(entryDate, previousVacationYearDate)); + vacationDays.add(calculateVacationDaysSinceEntry(entryDate, currentVacationYearDate)); + } else { + vacationDays.add(0.0); + vacationDays.add(0.0); + } + return vacationDays; + } + + private double calculateTotalAvailable(final List timeOffs, final List employeeRequests, + final Employee employee) { + Set excludedCategories = getExcludedCategories(); + Set genderSpecificExclusions = getGenderSpecificExclusions(); + Set employeeRequestCategories = employeeRequests.stream() + .map(TimeOffRequest::getCategory) + .collect(Collectors.toSet()); + + double healthLicence = 2; + List healthRequests = requestService + .findByEmployeeAndCategory(employee.getId(), TimeOffRequestType.PERMISOS_DE_SALUD); + if (healthRequests != null && !healthRequests.isEmpty()) { + healthLicence = healthRequests.getLast().getDaysBalance(); + } + + double totalAvailable = timeOffs.stream() + .filter(Objects::nonNull) + .filter(vacation -> vacation.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD) + .filter(vacation -> shouldIncludeVacation( + vacation, + excludedCategories, + genderSpecificExclusions, + employee, employeeRequestCategories + )) + .mapToDouble(vacation -> vacation.getDuration() != null ? vacation.getDuration() : 0.0) + .sum(); + + return totalAvailable + healthLicence; + } + + private double calculateVacationDaysSinceEntry(final LocalDate dateOfEntry, final LocalDate date) { + int yearsOfService = dateOfEntry != null ? Period.between(dateOfEntry, date).getYears() : 0; + if (yearsOfService > 10) { + return 30; + } + if (yearsOfService > 5) { + return 20; + } + if (yearsOfService > 1) { + return 15; + } + return 0; + } + + private boolean shouldIncludeVacation(final TimeOff timeoff, + final Set excludedCategories, + final Set genderSpecificExclusions, + final Employee employee, + final Set employeeRequestCategories) { + if (excludedCategories.contains(timeoff.getCategory()) + && !employeeRequestCategories.contains(timeoff.getCategory())) { + return false; + } + + return isFemale(employee) || !genderSpecificExclusions.contains(timeoff.getCategory()); + } + + private boolean isFemale(final Employee employee) { + return employee.getGender() == Employee.Gender.FEMALE; + } + + private ComboBox createEmployeeFilter() { + employeeFilter = new ComboBox<>("Empleado"); + + final List employees = new ArrayList<>(employeeService.findAllEmployees()); + employeeFilter.setPlaceholder("Seleccionar ..."); + employeeFilter.setClearButtonVisible(true); + employeeFilter.setItems(employees); + employeeFilter.setItemLabelGenerator(this::getEmployeeFullName); + employeeFilter.addValueChangeListener(event -> + refreshGeneralRequestGrid( + event.getValue(), + teamFilter.getValue() + ) + ); + + return employeeFilter; + } + + private ComboBox createTeamFilter() { + teamFilter = new ComboBox<>("Equipo"); + teamFilter.setPlaceholder("Seleccionar ..."); + teamFilter.setClearButtonVisible(true); + final List teams = new ArrayList<>(teamService.findAllTeams()); + teamFilter.setItems(teams); + teamFilter.setItemLabelGenerator(this::getTeamLabel); + teamFilter.addValueChangeListener(event -> + refreshGeneralRequestGrid( + employeeFilter.getValue(), + event.getValue() + ) + ); + return teamFilter; + } + + private ByteArrayInputStream generateExcelReport(final List employees) { + try (Workbook workbook = new XSSFWorkbook()) { + final Sheet sheet = workbook.createSheet("REPORTE_GENERAL_DE_VACACIONES"); + final 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 (final 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() { + final List employees = employeeService.findAllEmployees(); + final ByteArrayInputStream excelStream = generateExcelReport(employees); + return new StreamResource("reporte_general_de_vacaciones_" + LocalDate.now() + ".xlsx", + () -> excelStream); + } + + private void downloadReport() { + final StreamResource resource = generateGeneralVacationReport(); + getUI().ifPresent(ui -> openDocumentStream(resource, ui)); + } + + private void openDocumentStream(final StreamResource resource, final UI ui) { + final StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource); + ui.getPage().open(registration.getResourceUri().toString()); + } + + private HorizontalLayout createSummaryLayout(final Employee employee) { + ResultEmp result = extracted(employee); + double totalAvailableDays = result.remainingHolidayDays() + result.remainingPersonalDays() + + result.remainingVacationDays(); + + return new HorizontalLayout( + new Span("Total feriados fijos y movibles: " + result.remainingHolidayDays()), + new Span("Total días libres personales: " + result.remainingPersonalDays()), + new Span("Total vacaciones pendientes de uso: " + result.remainingVacationDays()), + new Span("TOTAL GENERAL DE DÍAS DISPONIBLES: " + totalAvailableDays) + ); + } + + private ResultEmp extracted(final Employee employee) { + final boolean isMale = employee.getGender() == Employee.Gender.MALE; + final int currentYear = LocalDate.now().getYear(); + final LocalDate currentDate = LocalDate.now(); + final List timeOffs = timeOffService.findVacations(); + double healthLicence = getHealthLicence(employee.getId()); + + double totalFixedAndMovableHolidays = calculateHolidayDays(timeOffs); + double totalPersonalDays = calculatePersonalDays(timeOffs, isMale); + final List vacationDays = calculateVacationDays(employee); + + double totalVacationCurrentDays = calculateUtilizedVacationDays( + employee.getId(), + vacationDays.get(1), + TimeOffRequestType.VACACION_GESTION_ACTUAL + ); + double totalVacationPreviousDays = calculateUtilizedVacationDays( + employee.getId(), + vacationDays.get(0), + TimeOffRequestType.VACACION_GESTION_ANTERIOR + ); + + final List requests = requestService.findRequestsByEmployeeId(employee.getId()); + double utilizedFixedAndMovableHolidays = calculateHolidayUtilizedDays(requests, currentYear); + double utilizedPersonalDays = calculatePersonalDaysUtilized(requests, isMale, currentYear); + + 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 + ); + + return new ResultEmp(remainingHolidayDays, remainingPersonalDays, remainingVacationDays); + } + + private record ResultEmp(double remainingHolidayDays, double remainingPersonalDays, double remainingVacationDays) { + } + + private double getHealthLicence(final UUID employeeId) { + List healthRequests = requestService + .findByEmployeeAndCategory(employeeId, TimeOffRequestType.PERMISOS_DE_SALUD); + return healthRequests != null && !healthRequests.isEmpty() ? healthRequests.getLast().getDaysBalance() : 2; + } + + private double calculateUtilizedVacationDays(final UUID employeeId, 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 timeOffs) { + return timeOffs.stream() + .filter(req -> req.getType() != TimeOff.Type.OTHER) + .mapToDouble(TimeOff::getDuration) + .sum(); + } + + private double calculatePersonalDays(final List timeOffs, final boolean isMale) { + return timeOffs.stream() + .filter(req -> req.getType() == TimeOff.Type.OTHER) + .filter(req -> !getStandardExclusions().contains(req.getCategory())) + .filter(req -> !(isMale && getMaleSpecificExclusions().contains(req.getCategory()))) + .filter(req -> !req.getCategory().name().startsWith("VACACION")) + .filter(req -> req.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD) + .mapToDouble(TimeOff::getDuration) + .sum(); + } + + + private Set getStandardExclusions() { + return Set.of( + TimeOffRequestType.MATERNIDAD, + TimeOffRequestType.PATERNIDAD, + TimeOffRequestType.MATRIMONIO, + TimeOffRequestType.DUELO_1ER_GRADO, + TimeOffRequestType.DUELO_2ER_GRADO, + TimeOffRequestType.DIA_DEL_PADRE, + TimeOffRequestType.DIA_DE_LA_MADRE + ); + } + + private Set getMaleSpecificExclusions() { + return Set.of( + TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL, + TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL + ); + } + + private double calculateHolidayUtilizedDays(final List requests, final int year) { + return requests.stream() + .filter(this::verificationIsHoliday) + .filter(req -> req.getState() == TimeOffRequestStatus.TOMADO + || req.getState() == TimeOffRequestStatus.APROBADO) + .filter(req -> getStartDateYear(req) == year) + .mapToDouble(TimeOffRequest::getDaysToBeTake) + .sum(); + } + + private double calculatePersonalDaysUtilized(final List requests, final boolean isMale, + final int year) { + return requests.stream() + .filter(req -> !verificationIsHoliday(req)) + .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")) + .filter(req -> req.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD) + .filter(req -> getStartDateYear(req) == year) + .mapToDouble(TimeOffRequest::getDaysToBeTake) + .sum(); + } + + private int getStartDateYear(final TimeOffRequest request) { + if (request.getStartDate() != null) { + return request.getStartDate().getYear(); + } + return 0; + } + + private Boolean verificationIsHoliday(final TimeOffRequest request) { + TimeOff timeoff = timeOffService.findVacationByCategory(request.getCategory()); + return timeoff.getType() != TimeOff.Type.OTHER; + } + + private String getDateString(final LocalDate date) { + return (date != null) ? date.toString() : ""; + } + + private ByteArrayInputStream generatePdfReport(final UUID employeeId) { + final List requests = requestService.findRequestsByEmployeeId(employeeId); + final Employee employee = employeeService.getEmployee(employeeId); + final ResultEmp result = extracted(employee); + + try (PDDocument document = new PDDocument(); ByteArrayOutputStream out = new ByteArrayOutputStream()) { + PDPage page = new PDPage(); + document.addPage(page); + + List 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 { + contentStream = new PDPageContentStream(document, page); + + contentStream.setFont(PDType1Font.TIMES_BOLD, 18); + contentStream.beginText(); + contentStream.newLineAtOffset(200, 750); + contentStream.showText("Reporte de Vacaciones"); + contentStream.endText(); + + contentStream.setFont(PDType1Font.TIMES_ROMAN, 14); + contentStream.beginText(); + contentStream.newLineAtOffset(50, 700); + contentStream.showText("Empleado: " + employee.getFirstName() + " " + employee.getLastName()); + contentStream.newLineAtOffset(0, -15); + contentStream.showText("Equipo: " + employee.getTeam().getName()); + contentStream.endText(); + + float tableTopY = 650; + float margin = 50; + float cellHeight = 20; + String[] headers = {"Categoría", "Estado", "Fecha de Inicio", "Fecha de Fin", "Días a Tomar"}; + int columns = headers.length; + + float[] columnWidths = new float[columns]; + for (int i = 0; i < columns; i++) { + columnWidths[i] = getMaxColumnWidth(headers[i], requests, i); + } + + contentStream.setFont(PDType1Font.TIMES_BOLD, 10); + float currentX = margin; + for (int i = 0; i < columns; i++) { + contentStream.addRect(currentX, tableTopY, columnWidths[i], -cellHeight); + contentStream.beginText(); + contentStream.newLineAtOffset(currentX + 5, tableTopY - 15); + contentStream.showText(headers[i]); + contentStream.endText(); + currentX += columnWidths[i]; + } + contentStream.stroke(); + + contentStream.setFont(PDType1Font.TIMES_ROMAN, 10); + float currentY = tableTopY - cellHeight; + for (TimeOffRequest request : filteredRequests) { + String startDate = getDateString(request.getStartDate()); + String endDate = getDateString(request.getEndDate()); + + String[] rowData = { + request.getCategory().name(), + request.getState().name(), + startDate, + endDate, + String.valueOf(request.getDaysToBeTake() != null ? request.getDaysToBeTake() : 0) + }; + + currentX = margin; + for (int i = 0; i < columns; i++) { + contentStream.addRect(currentX, currentY, columnWidths[i], -cellHeight); + contentStream.beginText(); + contentStream.newLineAtOffset(currentX + 5, currentY - 15); + contentStream.showText(rowData[i]); + contentStream.endText(); + currentX += columnWidths[i]; + } + contentStream.stroke(); + currentY -= cellHeight; + + if (currentY < 50) { + contentStream.close(); + page = new PDPage(); + document.addPage(page); + contentStream = new PDPageContentStream(document, page); + currentY = 750; + } + } + + currentY -= 30; + + if (currentY < 80) { + contentStream.close(); + page = new PDPage(); + document.addPage(page); + contentStream = new PDPageContentStream(document, page); + currentY = 750; + } + + contentStream.setFont(PDType1Font.TIMES_ROMAN, 14); + contentStream.beginText(); + contentStream.newLineAtOffset(50, currentY - 20); + contentStream.showText("Total feriados fijos y movibles: " + result.remainingHolidayDays); + contentStream.newLineAtOffset(0, -20); + contentStream.showText("Total días libres personales: " + result.remainingPersonalDays); + contentStream.newLineAtOffset(0, -20); + contentStream.showText("Total vacaciones pendientes de uso: " + result.remainingVacationDays); + contentStream.newLineAtOffset(0, -20); + contentStream.showText("TOTAL GENERAL DE DÍAS DISPONIBLES: " + + (result.remainingHolidayDays + result.remainingPersonalDays + result.remainingVacationDays)); + contentStream.endText(); + + } finally { + if (contentStream != null) { + contentStream.close(); + } + } + document.save(out); + return new ByteArrayInputStream(out.toByteArray()); + } catch (IOException e) { + throw new UncheckedIOException("Error al generar el reporte", e); + } + } + + private float getMaxColumnWidth(final String header, final List 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(final UUID employeeId) { + 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(employeeId); + + return new StreamResource(fileName, () -> pdfStream) + .setContentType("application/pdf") + .setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); + } + + private void downloadEmployeeReport(final UUID employeeId) { + StreamResource resource = generateVacationReport(employeeId); + getUI().ifPresent(ui -> openDocumentEmployeeStream(resource, ui)); + } + + private void openDocumentEmployeeStream(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 diff --git a/src/main/java/com/primefactorsolutions/views/timesheet/TimesheetEntryView.java b/src/main/java/com/primefactorsolutions/views/timesheet/TimesheetEntryView.java index 8d9b413..84bb945 100644 --- a/src/main/java/com/primefactorsolutions/views/timesheet/TimesheetEntryView.java +++ b/src/main/java/com/primefactorsolutions/views/timesheet/TimesheetEntryView.java @@ -6,6 +6,7 @@ import com.primefactorsolutions.model.Team; import com.primefactorsolutions.service.EmployeeService; import com.primefactorsolutions.service.TimesheetService; import com.primefactorsolutions.service.TeamService; +import com.primefactorsolutions.views.BaseEntityForm; import com.primefactorsolutions.views.MainLayout; import com.vaadin.flow.component.Component; import com.vaadin.flow.component.textfield.NumberField; @@ -13,10 +14,10 @@ import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.router.*; import com.vaadin.flow.spring.annotation.SpringComponent; +import com.vaadin.flow.spring.security.AuthenticationContext; 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.time.LocalDate; import java.time.YearMonth; @@ -25,13 +26,12 @@ import java.util.List; import java.util.UUID; import java.util.stream.Collectors; -@SuppressWarnings("deprecation") @SpringComponent @PermitAll @Scope("prototype") @PageTitle("Horas Trabajadas") @Route(value = "/timesheet/:hours-workedId?/:action?", layout = MainLayout.class) -public class TimesheetEntryView extends BeanValidationForm implements HasUrlParameter { +public class TimesheetEntryView extends BaseEntityForm implements HasUrlParameter { private final ComboBox team = new ComboBox<>("Equipo"); private final ComboBox employee = new ComboBox<>("Empleado"); private final ComboBox task = new ComboBox<>("Tarea"); @@ -45,10 +45,11 @@ public class TimesheetEntryView extends BeanValidationForm imple private TimesheetEntry timesheetEntry; - public TimesheetEntryView(final TimesheetService timesheetService, + public TimesheetEntryView(final AuthenticationContext authenticationContext, + final TimesheetService timesheetService, final EmployeeService employeeService, final TeamService teamService) { - super(TimesheetEntry.class); + super(authenticationContext, TimesheetEntry.class); this.timesheetService = timesheetService; this.employeeService = employeeService; this.teamService = teamService; diff --git a/src/main/java/com/primefactorsolutions/views/timesheet/TimesheetListView.java b/src/main/java/com/primefactorsolutions/views/timesheet/TimesheetListView.java index 2900293..8442108 100644 --- a/src/main/java/com/primefactorsolutions/views/timesheet/TimesheetListView.java +++ b/src/main/java/com/primefactorsolutions/views/timesheet/TimesheetListView.java @@ -176,7 +176,7 @@ public class TimesheetListView extends BaseView { private ComboBox createEmployeeFilter() { employeeFilter = new ComboBox<>("Empleado"); - employeeFilter.setPlaceholder("Seleccionar empleado ..."); + employeeFilter.setPlaceholder("Seleccionar ..."); employeeFilter.setClearButtonVisible(true); List employees = new ArrayList<>(employeeService.findAllEmployees()); @@ -214,7 +214,7 @@ public class TimesheetListView extends BaseView { private ComboBox createTeamFilter() { teamFilter = new ComboBox<>("Equipo"); final List teams = new ArrayList<>(teamService.findAllTeams()); - teamFilter.setPlaceholder("Seleccionar equipo ..."); + teamFilter.setPlaceholder("Seleccionar ..."); teamFilter.setClearButtonVisible(true); teamFilter.setItems(teams); teamFilter.setItemLabelGenerator(this::getTeamLabel); diff --git a/src/main/java/com/primefactorsolutions/views/util/ComponentUtils.java b/src/main/java/com/primefactorsolutions/views/util/ComponentUtils.java new file mode 100644 index 0000000..4c65890 --- /dev/null +++ b/src/main/java/com/primefactorsolutions/views/util/ComponentUtils.java @@ -0,0 +1,13 @@ +package com.primefactorsolutions.views.util; + +import com.vaadin.flow.component.HasSize; +import lombok.experimental.UtilityClass; + +@UtilityClass +public class ComponentUtils { + + public static T withFullWidth(final T component) { + component.setWidthFull(); + return component; + } +}