diff --git a/src/main/java/com/primefactorsolutions/views/RequestEmployeeView.java b/src/main/java/com/primefactorsolutions/views/RequestEmployeeView.java index d97cbcd..0e27062 100644 --- a/src/main/java/com/primefactorsolutions/views/RequestEmployeeView.java +++ b/src/main/java/com/primefactorsolutions/views/RequestEmployeeView.java @@ -4,6 +4,7 @@ import com.primefactorsolutions.model.*; import com.primefactorsolutions.service.EmployeeService; import com.primefactorsolutions.service.TimeOffRequestService; import com.primefactorsolutions.service.VacationService; +import com.vaadin.flow.component.UI; import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.html.Div; @@ -16,11 +17,21 @@ import com.vaadin.flow.router.BeforeEvent; import com.vaadin.flow.router.HasUrlParameter; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; +import com.vaadin.flow.server.StreamRegistration; +import com.vaadin.flow.server.StreamResource; import com.vaadin.flow.spring.annotation.SpringComponent; import jakarta.annotation.security.PermitAll; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.apache.pdfbox.pdmodel.font.PDType1Font; import org.springframework.context.annotation.Scope; import org.vaadin.firitin.components.grid.PagingGrid; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UncheckedIOException; import java.time.LocalDate; import java.time.Period; import java.util.*; @@ -345,9 +356,10 @@ public class RequestEmployeeView extends Div implements HasUrlParameter private HorizontalLayout createActionButtons() { Button viewButton = createButton("Ver", () -> navigateToViewRequest(request)); Button editButton = createButton("Editar", () -> navigateToEditRequest(request)); + Button downloadButton = new Button("Descargar reporte", event -> downloadReport()); Button closeButton = new Button("Salir", event -> navigateToRequestsListView()); - return new HorizontalLayout(viewButton, editButton, closeButton); + return new HorizontalLayout(viewButton, editButton, downloadButton, closeButton); } private Button createButton(final String caption, final Runnable action) { @@ -457,6 +469,145 @@ public class RequestEmployeeView extends Div implements HasUrlParameter return existingRequest.isEmpty(); } + private String getDateString(final LocalDate date) { + return (date != null) ? date.toString() : ""; + } + + private ByteArrayInputStream generatePdfReport() { + try (PDDocument document = new PDDocument(); ByteArrayOutputStream out = new ByteArrayOutputStream()) { + PDPage page = new PDPage(); + document.addPage(page); + Employee employee = employeeService.getEmployee(employeeId); + + PDPageContentStream contentStream = null; + try { + contentStream = new PDPageContentStream(document, page); + + contentStream.setFont(PDType1Font.TIMES_BOLD, 18); + contentStream.beginText(); + contentStream.newLineAtOffset(200, 750); + contentStream.showText("Reporte de Vacaciones"); + contentStream.endText(); + + contentStream.setFont(PDType1Font.TIMES_ROMAN, 14); + contentStream.beginText(); + contentStream.newLineAtOffset(50, 700); + contentStream.showText("Empleado: " + employee.getFirstName() + " " + employee.getLastName()); + contentStream.newLineAtOffset(0, -15); + contentStream.showText("Equipo: " + employee.getTeam().getName()); + contentStream.endText(); + + float tableTopY = 650; + float margin = 50; + float cellHeight = 20; + String[] headers = {"Categoría", "Estado", "Fecha de Inicio", "Fecha de Fin", "Días a Tomar"}; + int columns = headers.length; + + float[] columnWidths = new float[columns]; + for (int i = 0; i < columns; i++) { + columnWidths[i] = getMaxColumnWidth(headers[i], requests, i); + } + + contentStream.setFont(PDType1Font.TIMES_BOLD, 10); + float currentX = margin; + for (int i = 0; i < columns; i++) { + contentStream.addRect(currentX, tableTopY, columnWidths[i], -cellHeight); + contentStream.beginText(); + contentStream.newLineAtOffset(currentX + 5, tableTopY - 15); + contentStream.showText(headers[i]); + contentStream.endText(); + currentX += columnWidths[i]; + } + contentStream.stroke(); + + contentStream.setFont(PDType1Font.TIMES_ROMAN, 10); + float currentY = tableTopY - cellHeight; + for (TimeOffRequest request : requests) { + String startDate = getDateString(request.getStartDate()); + String endDate = getDateString(request.getEndDate()); + + String[] rowData = { + request.getCategory().name() != null ? request.getCategory().name() : "", + request.getState().name() != null ? request.getState().name() : "", + startDate, + endDate, + String.valueOf(request.getDaysToBeTake() != null ? request.getDaysToBeTake() : 0) + }; + + currentX = margin; + for (int i = 0; i < columns; i++) { + contentStream.addRect(currentX, currentY, columnWidths[i], -cellHeight); + contentStream.beginText(); + contentStream.newLineAtOffset(currentX + 5, currentY - 15); + contentStream.showText(rowData[i]); + contentStream.endText(); + currentX += columnWidths[i]; + } + contentStream.stroke(); + currentY -= cellHeight; + + if (currentY < 50) { + contentStream.close(); + page = new PDPage(); + document.addPage(page); + contentStream = new PDPageContentStream(document, page); + currentY = 750; + } + } + } finally { + if (contentStream != null) { + contentStream.close(); + } + } + document.save(out); + return new ByteArrayInputStream(out.toByteArray()); + } catch (IOException e) { + throw new UncheckedIOException("Error al generar el reporte", e); + } + } + + private float getMaxColumnWidth(final String header, final List requests, final int columnIndex) { + float maxWidth = header.length(); + for (TimeOffRequest request : requests) { + String value = switch (columnIndex) { + case 0 -> request.getCategory().name(); + case 1 -> request.getState().name(); + case 2 -> getDateString(request.getStartDate()); + case 3 -> getDateString(request.getEndDate()); + case 4 -> String.valueOf(request.getDaysToBeTake()); + default -> ""; + }; + if (value != null) { + maxWidth = Math.max(maxWidth, value.length()); + } + } + return maxWidth * 7; + } + + private StreamResource generateVacationReport() { + Employee employee = employeeService.getEmployee(employeeId); + String fileName = String.format("%s_%s-reporte_de_vacaciones_%s.pdf", + employee.getFirstName(), + employee.getLastName(), + LocalDate.now()); + + ByteArrayInputStream pdfStream = generatePdfReport(); + + return new StreamResource(fileName, () -> pdfStream) + .setContentType("application/pdf") + .setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); + } + + private void downloadReport() { + StreamResource resource = generateVacationReport(); + getUI().ifPresent(ui -> openDocumentStream(resource, ui)); + } + + private void openDocumentStream(final StreamResource resource, final UI ui) { + StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource); + ui.getPage().open(registration.getResourceUri().toString()); + } + @Override public void setParameter(final BeforeEvent event, final String parameter) { employeeId = UUID.fromString(parameter); diff --git a/src/main/java/com/primefactorsolutions/views/RequestsListView.java b/src/main/java/com/primefactorsolutions/views/RequestsListView.java index cb54b03..c911edc 100644 --- a/src/main/java/com/primefactorsolutions/views/RequestsListView.java +++ b/src/main/java/com/primefactorsolutions/views/RequestsListView.java @@ -8,9 +8,6 @@ import com.primefactorsolutions.service.VacationService; 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.dialog.Dialog; -import com.vaadin.flow.component.html.Anchor; -import com.vaadin.flow.component.html.Span; import com.vaadin.flow.component.notification.Notification; import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.router.PageTitle; @@ -28,10 +25,8 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UncheckedIOException; -import java.time.DayOfWeek; import java.time.LocalDate; import java.time.Period; -import java.time.temporal.WeekFields; import java.util.*; import java.util.stream.Collectors; @@ -434,12 +429,12 @@ public class RequestsListView extends BaseView { getUI().ifPresent(ui -> ui.navigate("requests/" + idEmployee.toString())); } - private ByteArrayInputStream generateExcelReport(List employees) { + private ByteArrayInputStream generateExcelReport(final List employees) { try (Workbook workbook = new XSSFWorkbook()) { - Sheet sheet = workbook.createSheet("Reporte Horas Trabajadas"); + Sheet sheet = workbook.createSheet("REPORTE_GENERAL_DE_VACACIONES"); Row headerRow = sheet.createRow(0); - String[] headers = {"Nombre", "Equipo", "Estado", "Total Horas"}; + String[] headers = {"Empleado", "Equipo", "Estado", "Total Horas"}; for (int i = 0; i < headers.length; i++) { Cell cell = headerRow.createCell(i); cell.setCellValue(headers[i]); @@ -465,9 +460,7 @@ public class RequestsListView extends BaseView { private StreamResource generateGeneralVacationReport() { List employees = employeeService.findAllEmployees(); - ByteArrayInputStream excelStream = generateExcelReport(employees); - return new StreamResource("reporte_general_de_vacaciones_" + LocalDate.now() + ".xlsx", () -> excelStream); }