This commit is contained in:
parent
9266a5f634
commit
87418e03e5
Binary file not shown.
@ -1,7 +1,6 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public enum DocumentType {
|
||||
TODOS,
|
||||
CARNET_DE_IDENTIDAD,
|
||||
RECIBOS_DE_PAGO,
|
||||
CONTRATO_DE_TRABAJO,
|
||||
|
@ -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<Week> getLastWeeks(final int numberOfWeeks) {
|
||||
final int weekNumber = LocalDate.now().get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
|
||||
LocalDate from = getFirstDayOfWeek(weekNumber);
|
||||
final ArrayList<Week> 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) {
|
||||
|
@ -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<T> extends BeanValidationForm<T> {
|
||||
private final AuthenticationContext authenticationContext;
|
||||
|
||||
public BaseEntityForm(final AuthenticationContext authenticationContext, final Class<T> 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<ClickEvent<Button>>) buttonClickEvent ->
|
||||
UI.getCurrent().getPage().getHistory().back());
|
||||
|
||||
return cancelButton;
|
||||
}
|
||||
|
||||
protected boolean isRoleAdmin() {
|
||||
return AuthUtils.isAdmin(this.authenticationContext);
|
||||
}
|
||||
|
||||
protected Optional<UUID> getEmployeeId() {
|
||||
return AuthUtils.getEmployeeId(this.authenticationContext);
|
||||
}
|
||||
}
|
@ -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<Document> implements HasUrlParameter<String> {
|
||||
public class DocumentView extends BaseEntityForm<Document> implements HasUrlParameter<String> {
|
||||
private final TextField fileName = new TextField("Nombre del documento");
|
||||
private final ComboBox<DocumentType> documentType = new ComboBox<>("Tipo de documento");
|
||||
private final ComboBox<Employee> employeeComboBox = new ComboBox<>("Empleado");
|
||||
@ -50,17 +49,18 @@ public class DocumentView extends BeanValidationForm<Document> 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<Document> 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<Document> 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<Document> 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<Document> 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<Document> 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<Document> 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<Document> 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<Component> 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);
|
||||
}
|
||||
}
|
||||
|
@ -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<DocumentType> 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<Employee> createEmployeeFilter() {
|
||||
employeeFilter = new ComboBox<>("Empleado");
|
||||
employeeFilter.setPlaceholder("Seleccionar ...");
|
||||
employeeFilter.setClearButtonVisible(true);
|
||||
List<Employee> 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) {
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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<Employee> employeeComboBox = new ComboBox<>("Empleado");
|
||||
private final ComboBox<TimeOffRequestType> 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<TimeOffRequest> 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<TimeOffRequest> employeeRequests = requestService.findRequestsByEmployeeId(employee.getId());
|
||||
List<TimeOffRequestType> allCategories = Arrays.asList(TimeOffRequestType.values());
|
||||
List<TimeOffRequestType> 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<TimeOffRequest> 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<TimeOffRequest> 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<TimeOffRequest> employeeRequests,
|
||||
final TimeOffRequestType category) {
|
||||
if (category == TimeOffRequestType.CUMPLEAÑOS && employee.getBirthday() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<TimeOffRequest> 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<TimeOffRequest> 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<TimeOffRequest> 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<TimeOffRequest> 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<TimeOffRequest> 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<TimeOffRequest> 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));
|
||||
}
|
||||
}
|
@ -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<Employee> requestGrid = new PagingGrid<>();
|
||||
private ComboBox<Employee> employeeFilter;
|
||||
private ComboBox<Team> teamFilter;
|
||||
private ComboBox<TimeOffRequestType> categoryFilter;
|
||||
private ComboBox<Status> 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, Component>) employee -> {
|
||||
final MenuBar menuBar = new MenuBar();
|
||||
menuBar.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE);
|
||||
final MenuItem view = createIconItem(menuBar, VaadinIcon.EYE, "View");
|
||||
view.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) 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<Employee> fetchFilteredEmployees(final int start,
|
||||
final int pageSize,
|
||||
final Employee employee,
|
||||
final Team team,
|
||||
final TimeOffRequestType category,
|
||||
final Status state) {
|
||||
List<Employee> 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<TimeOffRequest> 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<TimeOffRequest> 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<TimeOffRequest> activeRequest = requestService
|
||||
.findByEmployeeAndState(employee.getId(), TimeOffRequestStatus.EN_USO);
|
||||
return activeRequest.isPresent() ? "EN_DESCANSO" : "EN_FUNCIONES";
|
||||
}
|
||||
|
||||
private String getCategory(final Employee employee) {
|
||||
Optional<TimeOffRequest> activeRequest = requestService
|
||||
.findByEmployeeAndState(employee.getId(), TimeOffRequestStatus.EN_USO);
|
||||
return activeRequest.map(request -> request.getCategory().toString()).orElse("");
|
||||
}
|
||||
|
||||
private String getGeneralTotal(final Employee employee) {
|
||||
List<TimeOffRequest> employeeRequests = requestService.findRequestsByEmployeeId(employee.getId());
|
||||
List<TimeOff> timeOffs = timeOffService.findVacations();
|
||||
|
||||
List<Double> vacationDays = calculateVacationDays(employee);
|
||||
|
||||
double utilizedVacationCurrentDays = vacationDays.get(1);
|
||||
List<TimeOffRequest> 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<TimeOffRequest> 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<TimeOffRequestType> 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<TimeOffRequestType> getGenderSpecificExclusions() {
|
||||
return Set.of(
|
||||
TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL,
|
||||
TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL
|
||||
);
|
||||
}
|
||||
|
||||
private double calculateTotalUtilized(final List<TimeOffRequest> 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<Double> calculateVacationDays(final Employee employee) {
|
||||
List<Double> 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<TimeOff> timeOffs, final List<TimeOffRequest> employeeRequests,
|
||||
final Employee employee) {
|
||||
Set<TimeOffRequestType> excludedCategories = getExcludedCategories();
|
||||
Set<TimeOffRequestType> genderSpecificExclusions = getGenderSpecificExclusions();
|
||||
Set<TimeOffRequestType> employeeRequestCategories = employeeRequests.stream()
|
||||
.map(TimeOffRequest::getCategory)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
double healthLicence = 2;
|
||||
List<TimeOffRequest> 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<TimeOffRequestType> excludedCategories,
|
||||
final Set<TimeOffRequestType> genderSpecificExclusions,
|
||||
final Employee employee,
|
||||
final Set<TimeOffRequestType> 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<Employee> createEmployeeFilter() {
|
||||
employeeFilter = new ComboBox<>("Empleado");
|
||||
|
||||
final List<Employee> 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<Team> createTeamFilter() {
|
||||
teamFilter = new ComboBox<>("Equipo");
|
||||
teamFilter.setPlaceholder("TODOS");
|
||||
teamFilter.setClearButtonVisible(true);
|
||||
final List<Team> 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<TimeOffRequestType> 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<Status> 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<Employee> 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<Employee> 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());
|
||||
}
|
||||
}
|
@ -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<TimeOffRequest> implements HasUrlParameter<String> {
|
||||
public class TimeOffRequestView extends BaseEntityForm<TimeOffRequest> implements HasUrlParameter<String> {
|
||||
|
||||
private final ComboBox<TimeOffRequestStatus> 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<TimeOffRequestType> category = withFullWidth(new ComboBox<>("Categoría"));
|
||||
private final ComboBox<TimeOffRequestStatus> 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<Component> getFormComponents() {
|
||||
return List.of(
|
||||
createEmployeeHeader(),
|
||||
createTeamHeader(),
|
||||
createCategoryHeader(),
|
||||
category,
|
||||
state,
|
||||
expiration,
|
||||
startDate,
|
||||
endDate,
|
||||
availableDays,
|
||||
daysBalance,
|
||||
daysToBeTake
|
||||
);
|
||||
}
|
||||
|
||||
private void filterCategories(final Employee employee) {
|
||||
category.clear();
|
||||
List<TimeOffRequest> employeeRequests = timeOffRequestService.findRequestsByEmployeeId(employee.getId());
|
||||
List<TimeOffRequestType> allCategories = Arrays.asList(TimeOffRequestType.values());
|
||||
List<TimeOffRequestType> 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<TimeOffRequest> 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<TimeOffRequest> 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<TimeOffRequest> employeeRequests,
|
||||
final TimeOffRequestType category) {
|
||||
if (category == TimeOffRequestType.CUMPLEAÑOS && employee.getBirthday() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<TimeOffRequest> 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<TimeOffRequest> 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<TimeOffRequest> 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<TimeOffRequest> 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<TimeOffRequest> 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<TimeOffRequest> 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<TimeOffRequest> 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());
|
||||
}
|
||||
}
|
||||
|
@ -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<TimeOffRequest> pendingRequestsGrid = new PagingGrid<>();
|
||||
private final PagingGrid<TimeOffRequest> requestsGrid = new PagingGrid<>();
|
||||
private ComboBox<Employee> employeeFilter;
|
||||
private ComboBox<Team> teamFilter;
|
||||
private ComboBox<TimeOffRequestType> 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, Component>) 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, Component>) 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<TimeOffRequest> fetchFilteredPendingRequests(final int start,
|
||||
final int pageSize,
|
||||
final Employee employee,
|
||||
final Team team,
|
||||
final TimeOffRequestType category) {
|
||||
List<TimeOffRequest> filteredPendingRequests
|
||||
= requestService.findRequestsByState(TimeOffRequestStatus.SOLICITADO);
|
||||
private List<TimeOffRequest> fetchFilteredRequests(final int start,
|
||||
final int pageSize,
|
||||
final Employee employee,
|
||||
final Team team,
|
||||
final TimeOffRequestType category) {
|
||||
List<TimeOffRequest> 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<Employee> createEmployeeFilter() {
|
||||
employeeFilter = new ComboBox<>("Empleado");
|
||||
employeeFilter.setClearButtonVisible(true);
|
||||
employeeFilter.setPlaceholder("TODOS");
|
||||
employeeFilter.setPlaceholder("Seleccionar ...");
|
||||
final List<Employee> 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<Team> createTeamFilter() {
|
||||
teamFilter = new ComboBox<>("Equipo");
|
||||
teamFilter.setClearButtonVisible(true);
|
||||
teamFilter.setPlaceholder("TODOS");
|
||||
teamFilter.setPlaceholder("Seleccionar ...");
|
||||
final List<Team> 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<TimeOffRequestType> 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()
|
@ -2,27 +2,16 @@ 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.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;
|
||||
@ -34,6 +23,8 @@ 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;
|
||||
|
||||
@ -48,100 +39,156 @@ 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<String> {
|
||||
@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<TimeOffRequest> requestGrid = new PagingGrid<>(TimeOffRequest.class);
|
||||
private List<TimeOffRequest> requests = Collections.emptyList();
|
||||
private ComboBox<TimeOffRequestType> categoryFilter;
|
||||
private ComboBox<TimeOffRequestStatus> stateFilter;
|
||||
private UUID employeeId;
|
||||
private double remainingHolidayDays;
|
||||
private double remainingPersonalDays;
|
||||
private double remainingVacationDays;
|
||||
private final PagingGrid<Employee> requestGrid = new PagingGrid<>();
|
||||
private ComboBox<Employee> employeeFilter;
|
||||
private ComboBox<Team> teamFilter;
|
||||
|
||||
public TimeOffRequestsByEmployeeView(final AuthenticationContext authenticationContext,
|
||||
final TimeOffRequestService requestService,
|
||||
final EmployeeService employeeService,
|
||||
final TimeOffService timeOffService) {
|
||||
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();
|
||||
Button downloadButton = new Button("Descargar reporte", event -> downloadReport());
|
||||
getCurrentPageLayout().add(downloadButton);
|
||||
|
||||
if (isRoleAdmin()) {
|
||||
final Button downloadButton = new Button("Descargar reporte", event -> downloadReport());
|
||||
getCurrentPageLayout().add(downloadButton);
|
||||
}
|
||||
|
||||
setupFilters();
|
||||
setupGrid();
|
||||
getCurrentPageLayout().add(requestGrid, new H3("Balance"), createSummaryLayout());
|
||||
refreshRequestGrid(null, null);
|
||||
setupRequestGrid();
|
||||
getCurrentPageLayout().add(requestGrid);
|
||||
}
|
||||
|
||||
private void setupFilters() {
|
||||
categoryFilter = createCategoryFilter();
|
||||
stateFilter = createStateFilter();
|
||||
HorizontalLayout hl = new HorizontalLayout(categoryFilter, stateFilter);
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
hl.add(createEmployeeFilter());
|
||||
hl.add(createTeamFilter());
|
||||
getCurrentPageLayout().add(hl);
|
||||
}
|
||||
|
||||
private ComboBox<TimeOffRequestType> 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<TimeOffRequestStatus> 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, Component>) timeOffRequest -> {
|
||||
final MenuBar menuBar = new MenuBar();
|
||||
menuBar.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE);
|
||||
final MenuItem view = createIconItem(menuBar, VaadinIcon.EYE, "View");
|
||||
view.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent ->
|
||||
navigateToViewRequest(timeOffRequest));
|
||||
final MenuItem edit = createIconItem(menuBar, VaadinIcon.PENCIL, "Edit");
|
||||
edit.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent ->
|
||||
navigateToEditRequest(timeOffRequest));
|
||||
return menuBar;
|
||||
});
|
||||
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 Set<TimeOffRequestType> getStandardExclusions() {
|
||||
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<Employee> fetchFilteredEmployees(final int start,
|
||||
final int pageSize,
|
||||
final Employee employee,
|
||||
final Team team) {
|
||||
List<Employee> 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<TimeOffRequest> activeRequest = requestService
|
||||
.findByEmployeeAndState(employee.getId(), TimeOffRequestStatus.EN_USO);
|
||||
return activeRequest.isPresent() ? "EN_DESCANSO" : "EN_FUNCIONES";
|
||||
}
|
||||
|
||||
private String getGeneralTotal(final Employee employee) {
|
||||
List<TimeOffRequest> employeeRequests = requestService.findRequestsByEmployeeId(employee.getId());
|
||||
List<TimeOff> timeOffs = timeOffService.findVacations();
|
||||
|
||||
List<Double> vacationDays = calculateVacationDays(employee);
|
||||
|
||||
double utilizedVacationCurrentDays = vacationDays.get(1);
|
||||
List<TimeOffRequest> 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<TimeOffRequest> 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<TimeOffRequestType> getExcludedCategories() {
|
||||
return Set.of(
|
||||
TimeOffRequestType.MATERNIDAD,
|
||||
TimeOffRequestType.PATERNIDAD,
|
||||
@ -153,67 +200,282 @@ public class TimeOffRequestsByEmployeeView extends BaseView implements HasUrlPar
|
||||
);
|
||||
}
|
||||
|
||||
private Set<TimeOffRequestType> getMaleSpecificExclusions() {
|
||||
private Set<TimeOffRequestType> getGenderSpecificExclusions() {
|
||||
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;
|
||||
private double calculateTotalUtilized(final List<TimeOffRequest> employeeRequests) {
|
||||
int currentYear = LocalDate.now().getYear();
|
||||
LocalDate currentDate = LocalDate.now();
|
||||
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();
|
||||
}
|
||||
|
||||
List<TimeOff> timeOffs = timeOffService.findVacations();
|
||||
private List<Double> calculateVacationDays(final Employee employee) {
|
||||
List<Double> vacationDays = new ArrayList<>();
|
||||
|
||||
double healthLicence = getHealthLicence(employeeId);
|
||||
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<TimeOff> timeOffs, final List<TimeOffRequest> employeeRequests,
|
||||
final Employee employee) {
|
||||
Set<TimeOffRequestType> excludedCategories = getExcludedCategories();
|
||||
Set<TimeOffRequestType> genderSpecificExclusions = getGenderSpecificExclusions();
|
||||
Set<TimeOffRequestType> employeeRequestCategories = employeeRequests.stream()
|
||||
.map(TimeOffRequest::getCategory)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
double healthLicence = 2;
|
||||
List<TimeOffRequest> 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<TimeOffRequestType> excludedCategories,
|
||||
final Set<TimeOffRequestType> genderSpecificExclusions,
|
||||
final Employee employee,
|
||||
final Set<TimeOffRequestType> 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<Employee> createEmployeeFilter() {
|
||||
employeeFilter = new ComboBox<>("Empleado");
|
||||
|
||||
final List<Employee> 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<Team> createTeamFilter() {
|
||||
teamFilter = new ComboBox<>("Equipo");
|
||||
teamFilter.setPlaceholder("Seleccionar ...");
|
||||
teamFilter.setClearButtonVisible(true);
|
||||
final List<Team> 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<Employee> 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<Employee> 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<TimeOff> timeOffs = timeOffService.findVacations();
|
||||
double healthLicence = getHealthLicence(employee.getId());
|
||||
|
||||
double totalFixedAndMovableHolidays = calculateHolidayDays(timeOffs);
|
||||
double totalPersonalDays = calculatePersonalDays(timeOffs, isMale);
|
||||
List<Double> vacationDays = calculateVacationDays(employee);
|
||||
final List<Double> 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
|
||||
);
|
||||
|
||||
double utilizedFixedAndMovableHolidays = calculateHolidayUtilizedDays(currentYear);
|
||||
double utilizedPersonalDays = calculatePersonalDaysUtilized(isMale, currentYear);
|
||||
final List<TimeOffRequest> requests = requestService.findRequestsByEmployeeId(employee.getId());
|
||||
double utilizedFixedAndMovableHolidays = calculateHolidayUtilizedDays(requests, currentYear);
|
||||
double utilizedPersonalDays = calculatePersonalDaysUtilized(requests, isMale, currentYear);
|
||||
|
||||
remainingHolidayDays = calculateRemainingHolidayDays(
|
||||
double remainingHolidayDays = calculateRemainingHolidayDays(
|
||||
totalFixedAndMovableHolidays,
|
||||
utilizedFixedAndMovableHolidays,
|
||||
employee.getDateOfExit(),
|
||||
currentDate
|
||||
);
|
||||
remainingPersonalDays = calculateRemainingPersonalDays(
|
||||
double remainingPersonalDays = calculateRemainingPersonalDays(
|
||||
totalPersonalDays,
|
||||
utilizedPersonalDays,
|
||||
healthLicence,
|
||||
employee.getDateOfExit(),
|
||||
currentDate
|
||||
);
|
||||
remainingVacationDays = calculateRemainingVacationDays(
|
||||
double remainingVacationDays = calculateRemainingVacationDays(
|
||||
totalVacationCurrentDays,
|
||||
totalVacationPreviousDays,
|
||||
employee.getDateOfExit(),
|
||||
currentDate
|
||||
);
|
||||
|
||||
double totalAvailableDays = remainingHolidayDays + remainingPersonalDays + remainingVacationDays;
|
||||
return new ResultEmp(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 record ResultEmp(double remainingHolidayDays, double remainingPersonalDays, double remainingVacationDays) {
|
||||
}
|
||||
|
||||
private double getHealthLicence(final UUID employeeId) {
|
||||
@ -222,7 +484,8 @@ public class TimeOffRequestsByEmployeeView extends BaseView implements HasUrlPar
|
||||
return healthRequests != null && !healthRequests.isEmpty() ? healthRequests.getLast().getDaysBalance() : 2;
|
||||
}
|
||||
|
||||
private double calculateUtilizedVacationDays(final double vacationDays, final TimeOffRequestType requestType) {
|
||||
private double calculateUtilizedVacationDays(final UUID employeeId, final double vacationDays,
|
||||
final TimeOffRequestType requestType) {
|
||||
List<TimeOffRequest> vacationRequests = requestService.findByEmployeeAndCategory(employeeId, requestType);
|
||||
if (vacationRequests != null && !vacationRequests.isEmpty()) {
|
||||
return vacationRequests.getLast().getDaysBalance();
|
||||
@ -279,54 +542,27 @@ public class TimeOffRequestsByEmployeeView extends BaseView implements HasUrlPar
|
||||
.sum();
|
||||
}
|
||||
|
||||
private List<Double> calculateVacationDays(final Employee employee) {
|
||||
List<Double> 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 Set<TimeOffRequestType> 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 double calculateHolidayUtilizedDays(final int year) {
|
||||
private Set<TimeOffRequestType> getMaleSpecificExclusions() {
|
||||
return Set.of(
|
||||
TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL,
|
||||
TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL
|
||||
);
|
||||
}
|
||||
|
||||
private double calculateHolidayUtilizedDays(final List<TimeOffRequest> requests, final int year) {
|
||||
return requests.stream()
|
||||
.filter(this::verificationIsHoliday)
|
||||
.filter(req -> req.getState() == TimeOffRequestStatus.TOMADO
|
||||
@ -336,7 +572,8 @@ public class TimeOffRequestsByEmployeeView extends BaseView implements HasUrlPar
|
||||
.sum();
|
||||
}
|
||||
|
||||
private double calculatePersonalDaysUtilized(final boolean isMale, final int year) {
|
||||
private double calculatePersonalDaysUtilized(final List<TimeOffRequest> requests, final boolean isMale,
|
||||
final int year) {
|
||||
return requests.stream()
|
||||
.filter(req -> !verificationIsHoliday(req))
|
||||
.filter(req -> req.getState() == TimeOffRequestStatus.TOMADO
|
||||
@ -357,127 +594,24 @@ public class TimeOffRequestsByEmployeeView extends BaseView implements HasUrlPar
|
||||
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<TimeOffRequest> 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<TimeOffRequest> existingRequest = requestService.findByEmployeeAndCategory(employeeId, type);
|
||||
return existingRequest.isEmpty();
|
||||
}
|
||||
|
||||
private String getDateString(final LocalDate date) {
|
||||
return (date != null) ? date.toString() : "";
|
||||
}
|
||||
|
||||
private ByteArrayInputStream generatePdfReport() {
|
||||
private ByteArrayInputStream generatePdfReport(final UUID employeeId) {
|
||||
final List<TimeOffRequest> 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);
|
||||
|
||||
Employee employee = employeeService.getEmployee(employeeId);
|
||||
List<TimeOffRequest> filteredRequests = requests.stream()
|
||||
.filter(request ->
|
||||
(request.getStartDate() == null || Year.from(request.getStartDate()).equals(Year.now()))
|
||||
@ -574,14 +708,14 @@ public class TimeOffRequestsByEmployeeView extends BaseView implements HasUrlPar
|
||||
contentStream.setFont(PDType1Font.TIMES_ROMAN, 14);
|
||||
contentStream.beginText();
|
||||
contentStream.newLineAtOffset(50, currentY - 20);
|
||||
contentStream.showText("Total feriados fijos y movibles: " + remainingHolidayDays);
|
||||
contentStream.showText("Total feriados fijos y movibles: " + result.remainingHolidayDays);
|
||||
contentStream.newLineAtOffset(0, -20);
|
||||
contentStream.showText("Total días libres personales: " + remainingPersonalDays);
|
||||
contentStream.showText("Total días libres personales: " + result.remainingPersonalDays);
|
||||
contentStream.newLineAtOffset(0, -20);
|
||||
contentStream.showText("Total vacaciones pendientes de uso: " + remainingVacationDays);
|
||||
contentStream.showText("Total vacaciones pendientes de uso: " + result.remainingVacationDays);
|
||||
contentStream.newLineAtOffset(0, -20);
|
||||
contentStream.showText("TOTAL GENERAL DE DÍAS DISPONIBLES: "
|
||||
+ (remainingHolidayDays + remainingPersonalDays + remainingVacationDays));
|
||||
+ (result.remainingHolidayDays + result.remainingPersonalDays + result.remainingVacationDays));
|
||||
contentStream.endText();
|
||||
|
||||
} finally {
|
||||
@ -614,53 +748,27 @@ public class TimeOffRequestsByEmployeeView extends BaseView implements HasUrlPar
|
||||
return maxWidth * 7;
|
||||
}
|
||||
|
||||
private StreamResource generateVacationReport() {
|
||||
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();
|
||||
ByteArrayInputStream pdfStream = generatePdfReport(employeeId);
|
||||
|
||||
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 downloadEmployeeReport(final UUID employeeId) {
|
||||
StreamResource resource = generateVacationReport(employeeId);
|
||||
getUI().ifPresent(ui -> openDocumentEmployeeStream(resource, ui));
|
||||
}
|
||||
|
||||
private void openDocumentStream(final StreamResource resource, final UI ui) {
|
||||
private void openDocumentEmployeeStream(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."));
|
||||
}
|
||||
}
|
||||
}
|
@ -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<TimesheetEntry> implements HasUrlParameter<String> {
|
||||
public class TimesheetEntryView extends BaseEntityForm<TimesheetEntry> implements HasUrlParameter<String> {
|
||||
private final ComboBox<Team> team = new ComboBox<>("Equipo");
|
||||
private final ComboBox<Employee> employee = new ComboBox<>("Empleado");
|
||||
private final ComboBox<String> task = new ComboBox<>("Tarea");
|
||||
@ -45,10 +45,11 @@ public class TimesheetEntryView extends BeanValidationForm<TimesheetEntry> 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;
|
||||
|
@ -176,7 +176,7 @@ public class TimesheetListView extends BaseView {
|
||||
|
||||
private ComboBox<Employee> createEmployeeFilter() {
|
||||
employeeFilter = new ComboBox<>("Empleado");
|
||||
employeeFilter.setPlaceholder("Seleccionar empleado ...");
|
||||
employeeFilter.setPlaceholder("Seleccionar ...");
|
||||
employeeFilter.setClearButtonVisible(true);
|
||||
|
||||
List<Employee> employees = new ArrayList<>(employeeService.findAllEmployees());
|
||||
@ -214,7 +214,7 @@ public class TimesheetListView extends BaseView {
|
||||
private ComboBox<Team> createTeamFilter() {
|
||||
teamFilter = new ComboBox<>("Equipo");
|
||||
final List<Team> teams = new ArrayList<>(teamService.findAllTeams());
|
||||
teamFilter.setPlaceholder("Seleccionar equipo ...");
|
||||
teamFilter.setPlaceholder("Seleccionar ...");
|
||||
teamFilter.setClearButtonVisible(true);
|
||||
teamFilter.setItems(teams);
|
||||
teamFilter.setItemLabelGenerator(this::getTeamLabel);
|
||||
|
@ -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 extends HasSize> T withFullWidth(final T component) {
|
||||
component.setWidthFull();
|
||||
return component;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user