Erweiterungen
This commit is contained in:
@@ -12,6 +12,7 @@ import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Document(collection = "jobs")
|
||||
@@ -141,6 +142,14 @@ public class Job {
|
||||
@Field("time_in_15min_units")
|
||||
private Integer timeIn15MinUnits;
|
||||
|
||||
// Service-IDs für die Rechnung
|
||||
@Field("service_ids")
|
||||
private List<String> serviceIds;
|
||||
|
||||
// Streckeninformation für die Rechnung (in km)
|
||||
@Field("route_distance_km")
|
||||
private Double routeDistanceKm;
|
||||
|
||||
/**
|
||||
* Returns the ObjectId as string for JSON serialization. This ensures that the
|
||||
* job id is returned as a string when jobs are retrieved via API.
|
||||
|
||||
@@ -77,7 +77,6 @@ public final class AdminLayout extends AppLayout {
|
||||
|
||||
// Only admin-specific menu items
|
||||
SideNavItem dashboard = new SideNavItem("Dashboard", "admin-dashboard", new Icon(VaadinIcon.DASHBOARD));
|
||||
SideNavItem pdfTest = new SideNavItem("PDF Test", "pdf-test", new Icon(VaadinIcon.FILE_TEXT_O));
|
||||
SideNavItem invoiceGenerator = new SideNavItem("Rechnungsgenerator", "invoice-generator",
|
||||
new Icon(VaadinIcon.FILE_PROCESS));
|
||||
SideNavItem priceTable = new SideNavItem("Preis-Tabelle", "admin-price-table", new Icon(VaadinIcon.COG));
|
||||
@@ -89,7 +88,6 @@ public final class AdminLayout extends AppLayout {
|
||||
// Icon(VaadinIcon.FILE_TEXT));
|
||||
|
||||
nav.addItem(dashboard);
|
||||
nav.addItem(pdfTest);
|
||||
nav.addItem(invoiceGenerator);
|
||||
nav.addItem(priceTable);
|
||||
// nav.addItem(systemSettings);
|
||||
|
||||
@@ -123,8 +123,6 @@ public class AddressValidationService {
|
||||
String locationType = geometry.path("location_type").asText();
|
||||
boolean isPrecise = "ROOFTOP".equals(locationType) || "RANGE_INTERPOLATED".equals(locationType);
|
||||
|
||||
// Adresskomponenten prüfen
|
||||
boolean hasStreetNumber = false;
|
||||
boolean hasPostalCode = false;
|
||||
|
||||
JsonNode addressComponents = firstResult.path("address_components");
|
||||
@@ -133,7 +131,6 @@ public class AddressValidationService {
|
||||
for (JsonNode type : types) {
|
||||
String typeStr = type.asText();
|
||||
if ("street_number".equals(typeStr)) {
|
||||
hasStreetNumber = true;
|
||||
} else if ("postal_code".equals(typeStr)) {
|
||||
hasPostalCode = true;
|
||||
}
|
||||
|
||||
@@ -675,11 +675,22 @@ public class AddJobView extends Main {
|
||||
return "";
|
||||
}).setHeader("Berechnung").setSortable(true);
|
||||
servicesGrid.addColumn(service -> {
|
||||
if (service.getEffectivePrice() != null) {
|
||||
return service.getEffectivePrice().setScale(2, RoundingMode.HALF_UP) + " €";
|
||||
// Get route distance for distance-based calculations
|
||||
Double routeDistance = (routeCalculationResult != null && routeCalculationResult.isValid())
|
||||
? routeCalculationResult.getDistanceKm()
|
||||
: null;
|
||||
BigDecimal price = calculateServicePrice(service, routeDistance);
|
||||
if (price.compareTo(BigDecimal.ZERO) > 0) {
|
||||
return price.setScale(2, RoundingMode.HALF_UP) + " €";
|
||||
}
|
||||
return "";
|
||||
}).setHeader("Preis").setSortable(true);
|
||||
// Show price info if no route calculated yet
|
||||
if (service.getCalculationBasis() == Service.CalculationBasis.DISTANCE && routeDistance == null) {
|
||||
return service.getPricePerKilometer().setScale(2, RoundingMode.HALF_UP) + " €/km (Route fehlt)";
|
||||
}
|
||||
return service.getEffectivePrice() != null
|
||||
? service.getEffectivePrice().setScale(2, RoundingMode.HALF_UP) + " €"
|
||||
: "";
|
||||
}).setHeader("Preis").setSortable(false);
|
||||
servicesGrid.addColumn(service -> {
|
||||
if (service.getVatRate() != null) {
|
||||
return service.getVatRate().multiply(new BigDecimal("100")).setScale(0, RoundingMode.HALF_UP) + " %";
|
||||
@@ -687,6 +698,10 @@ public class AddJobView extends Main {
|
||||
return "";
|
||||
}).setHeader("MwSt").setSortable(true);
|
||||
servicesGrid.addComponentColumn(service -> {
|
||||
// Verbindliche Leistungen können nicht gelöscht werden
|
||||
if (service.isMandatory()) {
|
||||
return new Span(""); // Leeres Element statt Löschen-Button
|
||||
}
|
||||
Button removeButton = new Button(new Icon(VaadinIcon.TRASH));
|
||||
removeButton.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY,
|
||||
ButtonVariant.LUMO_SMALL);
|
||||
@@ -822,8 +837,13 @@ public class AddJobView extends Main {
|
||||
BigDecimal vatTotal = BigDecimal.ZERO;
|
||||
BigDecimal grossTotal = BigDecimal.ZERO;
|
||||
|
||||
// Get route distance for distance-based calculations
|
||||
Double routeDistance = (routeCalculationResult != null && routeCalculationResult.isValid())
|
||||
? routeCalculationResult.getDistanceKm()
|
||||
: null;
|
||||
|
||||
for (Service service : selectedServices) {
|
||||
BigDecimal price = service.getEffectivePrice() != null ? service.getEffectivePrice() : BigDecimal.ZERO;
|
||||
BigDecimal price = calculateServicePrice(service, routeDistance);
|
||||
BigDecimal vatRate = service.getVatRate() != null ? service.getVatRate() : BigDecimal.ZERO;
|
||||
|
||||
netTotal = netTotal.add(price);
|
||||
@@ -837,6 +857,35 @@ public class AddJobView extends Main {
|
||||
grossTotalLabel.setText(grossTotal.setScale(2, RoundingMode.HALF_UP).toString().replace(".", ",") + " €");
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the actual price for a service based on its calculation basis and
|
||||
* route distance (for distance-based services).
|
||||
*/
|
||||
private BigDecimal calculateServicePrice(Service service, Double routeDistance) {
|
||||
if (service.getCalculationBasis() == null) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
switch (service.getCalculationBasis()) {
|
||||
case FLAT_RATE:
|
||||
return service.getPrice() != null ? service.getPrice() : BigDecimal.ZERO;
|
||||
|
||||
case DISTANCE:
|
||||
if (service.getPricePerKilometer() != null && routeDistance != null && routeDistance > 0) {
|
||||
return service.getPricePerKilometer().multiply(BigDecimal.valueOf(routeDistance));
|
||||
}
|
||||
return BigDecimal.ZERO;
|
||||
|
||||
case TIME:
|
||||
// For time-based services, we would need time units
|
||||
// For now, return the price per 15 minutes as base value
|
||||
return service.getPricePer15Minutes() != null ? service.getPricePer15Minutes() : BigDecimal.ZERO;
|
||||
|
||||
default:
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
private VerticalLayout createPickupSection() {
|
||||
VerticalLayout section = new VerticalLayout();
|
||||
section.setSpacing(true);
|
||||
@@ -1407,8 +1456,13 @@ public class AddJobView extends Main {
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
job.setPrice(totalPrice);
|
||||
|
||||
// Store selected service IDs in job (optional - if Job has serviceIds field)
|
||||
// job.setServiceIds(selectedServices.stream().map(Service::getId).toList());
|
||||
// Store selected service IDs in job for invoice creation
|
||||
job.setServiceIds(selectedServices.stream().map(Service::getId).toList());
|
||||
|
||||
// Store route distance in job for invoice creation
|
||||
if (routeCalculationResult != null && routeCalculationResult.isValid()) {
|
||||
job.setRouteDistanceKm(routeCalculationResult.getDistanceKm());
|
||||
}
|
||||
|
||||
// Validate all required fields using the binder
|
||||
if (binder.writeBeanIfValid(job)) {
|
||||
@@ -2802,10 +2856,6 @@ public class AddJobView extends Main {
|
||||
return field.getValue() != null ? field.getValue().trim() : "";
|
||||
}
|
||||
|
||||
private String getComboValueOrEmpty(ComboBox<String> field) {
|
||||
return field.getValue() != null ? field.getValue().trim() : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeigt den Adressvalidierungsdialog an. Die Prüfung erfolgt im Hintergrund und
|
||||
* der Dialog wird aktualisiert, sobald die Ergebnisse vorliegen.
|
||||
@@ -3034,6 +3084,12 @@ public class AddJobView extends Main {
|
||||
routeDistanceLabel.setText(String.format("%.1f km", routeCalculationResult.getDistanceKm()));
|
||||
routeDurationLabel.setText(routeCalculationResult.getFormattedDurationLong());
|
||||
routeInfoBox.setVisible(true);
|
||||
|
||||
// Update price summary and grid with new route distance
|
||||
updatePriceSummary();
|
||||
if (servicesGrid != null) {
|
||||
servicesGrid.getDataProvider().refreshAll();
|
||||
}
|
||||
} else {
|
||||
routeInfoBox.setVisible(false);
|
||||
}
|
||||
|
||||
@@ -43,10 +43,7 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
||||
|
||||
private Job currentJob;
|
||||
private List<ServiceRow> gridRows = new ArrayList<>();
|
||||
private List<Service> allUserServices;
|
||||
private Grid<ServiceRow> servicesGrid;
|
||||
private IntegerField kilometersField;
|
||||
private IntegerField timeField;
|
||||
private Div servicesSection;
|
||||
|
||||
/**
|
||||
@@ -88,6 +85,20 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
||||
setSpacing(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lädt die Services, die beim Job-Erstellen ausgewählt wurden.
|
||||
*/
|
||||
private void loadSelectedServicesFromJob() {
|
||||
if (currentJob.getServiceIds() != null && !currentJob.getServiceIds().isEmpty()) {
|
||||
gridRows.clear();
|
||||
for (String serviceId : currentJob.getServiceIds()) {
|
||||
serviceRepository.findById(serviceId).ifPresent(service -> {
|
||||
gridRows.add(new ServiceRow(service));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(BeforeEvent event, String jobIdHex) {
|
||||
try {
|
||||
@@ -116,13 +127,18 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
||||
H2 title = new H2("Rechnung erstellen für Auftrag " + currentJob.getJobNumber());
|
||||
add(title);
|
||||
|
||||
// Load previously selected services from job
|
||||
loadSelectedServicesFromJob();
|
||||
|
||||
// Job Details Section
|
||||
Div jobDetailsSection = createJobDetailsSection();
|
||||
add(jobDetailsSection);
|
||||
|
||||
// Performance Data Section
|
||||
Div performanceDataSection = createPerformanceDataSection();
|
||||
add(performanceDataSection);
|
||||
// Route Information Section (if available)
|
||||
if (currentJob.getRouteDistanceKm() != null && currentJob.getRouteDistanceKm() > 0) {
|
||||
Div routeInfoSection = createRouteInfoSection();
|
||||
add(routeInfoSection);
|
||||
}
|
||||
|
||||
// Services Selection Section
|
||||
Div servicesSection = createServicesSelectionSection();
|
||||
@@ -164,46 +180,28 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
||||
return section;
|
||||
}
|
||||
|
||||
private Div createPerformanceDataSection() {
|
||||
private Div createRouteInfoSection() {
|
||||
Div section = new Div();
|
||||
section.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)")
|
||||
section.getStyle().set("border", "1px solid var(--lumo-primary-color-50pct)")
|
||||
.set("border-radius", "var(--lumo-border-radius-m)").set("padding", "var(--lumo-space-m)")
|
||||
.set("margin-bottom", "var(--lumo-space-m)").set("width", "100%").set("box-sizing", "border-box");
|
||||
.set("margin-bottom", "var(--lumo-space-m)").set("width", "100%").set("box-sizing", "border-box")
|
||||
.set("background-color", "var(--lumo-primary-color-10pct)");
|
||||
|
||||
H3 sectionTitle = new H3("Leistungsdaten");
|
||||
H3 sectionTitle = new H3("Streckeninformation");
|
||||
sectionTitle.getStyle().set("color", "var(--lumo-primary-text-color)");
|
||||
section.add(sectionTitle);
|
||||
|
||||
VerticalLayout performanceLayout = new VerticalLayout();
|
||||
performanceLayout.setSpacing(true);
|
||||
performanceLayout.setWidthFull();
|
||||
VerticalLayout routeInfo = new VerticalLayout();
|
||||
routeInfo.setSpacing(true);
|
||||
routeInfo.setWidthFull();
|
||||
|
||||
// Kilometers field
|
||||
HorizontalLayout kilometersLayout = new HorizontalLayout();
|
||||
kilometersLayout.setWidthFull();
|
||||
Span kilometersLabel = new Span("Gefahrene Kilometer:");
|
||||
kilometersLabel.getStyle().set("width", "200px");
|
||||
kilometersField = new IntegerField();
|
||||
kilometersField.setWidth("150px");
|
||||
kilometersField.setMin(0);
|
||||
kilometersField.setValue(currentJob.getKilometersDriven() != null ? currentJob.getKilometersDriven() : 0);
|
||||
kilometersField.addValueChangeListener(e -> updateSummarySection());
|
||||
kilometersLayout.add(kilometersLabel, kilometersField);
|
||||
performanceLayout.add(kilometersLayout);
|
||||
Double distance = currentJob.getRouteDistanceKm();
|
||||
if (distance != null) {
|
||||
routeInfo.add(new HorizontalLayout(new Span("Berechnete Entfernung:"),
|
||||
new Span(String.format("%.1f km", distance))));
|
||||
}
|
||||
|
||||
// Time field (in 15-minute units)
|
||||
HorizontalLayout timeLayout = new HorizontalLayout();
|
||||
timeLayout.setWidthFull();
|
||||
Span timeLabel = new Span("Arbeitszeit (15-Minuten-Einheiten):");
|
||||
timeLabel.getStyle().set("width", "200px");
|
||||
timeField = new IntegerField();
|
||||
timeField.setWidth("150px");
|
||||
timeField.setMin(0);
|
||||
timeField.setValue(currentJob.getTimeIn15MinUnits() != null ? currentJob.getTimeIn15MinUnits() : 0);
|
||||
timeField.addValueChangeListener(e -> updateSummarySection());
|
||||
timeLayout.add(timeLabel, timeField);
|
||||
performanceLayout.add(timeLayout);
|
||||
|
||||
section.add(performanceLayout);
|
||||
section.add(routeInfo);
|
||||
return section;
|
||||
}
|
||||
|
||||
@@ -213,46 +211,23 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
||||
.set("border-radius", "var(--lumo-border-radius-m)").set("padding", "var(--lumo-space-m)")
|
||||
.set("margin-bottom", "var(--lumo-space-m)").set("width", "100%").set("box-sizing", "border-box");
|
||||
|
||||
H3 sectionTitle = new H3("Leistungen auswählen");
|
||||
H3 sectionTitle = new H3("Leistungen");
|
||||
servicesSection.add(sectionTitle);
|
||||
|
||||
// Load services for current user (only once)
|
||||
if (allUserServices == null) {
|
||||
String currentUserId = securityService.getCurrentUserId().toHexString();
|
||||
allUserServices = serviceRepository.findByUserId(currentUserId);
|
||||
}
|
||||
|
||||
// Initialize with 2 empty rows if gridRows is empty
|
||||
if (gridRows.isEmpty()) {
|
||||
gridRows.add(new ServiceRow());
|
||||
gridRows.add(new ServiceRow());
|
||||
}
|
||||
|
||||
// Create grid with editable rows
|
||||
// Create grid with read-only rows
|
||||
servicesGrid = new Grid<>();
|
||||
servicesGrid.setWidthFull();
|
||||
servicesGrid.setAllRowsVisible(true);
|
||||
|
||||
// Service selection column (ComboBox)
|
||||
servicesGrid.addComponentColumn(row -> {
|
||||
ComboBox<Service> serviceCombo = new ComboBox<>();
|
||||
serviceCombo.setItems(allUserServices);
|
||||
serviceCombo.setItemLabelGenerator(Service::getName);
|
||||
serviceCombo.setPlaceholder("Leistung auswählen...");
|
||||
serviceCombo.setWidthFull();
|
||||
serviceCombo.setValue(row.getService());
|
||||
|
||||
serviceCombo.addValueChangeListener(event -> {
|
||||
row.setService(event.getValue());
|
||||
// Refresh the grid to show updated calculation basis and price
|
||||
servicesGrid.getDataProvider().refreshItem(row);
|
||||
updateSummarySection();
|
||||
});
|
||||
|
||||
return serviceCombo;
|
||||
// Service name column (read-only)
|
||||
servicesGrid.addColumn(row -> {
|
||||
if (row.getService() != null) {
|
||||
return row.getService().getName();
|
||||
}
|
||||
return "";
|
||||
}).setHeader("Leistung").setAutoWidth(true).setFlexGrow(2);
|
||||
|
||||
// Calculation basis column
|
||||
// Calculation basis column (read-only)
|
||||
servicesGrid.addColumn(row -> {
|
||||
if (row.getService() != null && row.getService().getCalculationBasis() != null) {
|
||||
return switch (row.getService().getCalculationBasis()) {
|
||||
@@ -264,7 +239,7 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
||||
return "";
|
||||
}).setHeader("Berechnungsgrundlage").setAutoWidth(true).setFlexGrow(1);
|
||||
|
||||
// Price column
|
||||
// Price column (read-only)
|
||||
servicesGrid.addColumn(row -> {
|
||||
if (row.getService() != null) {
|
||||
BigDecimal price = calculateServicePrice(row.getService());
|
||||
@@ -278,15 +253,6 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
||||
servicesGrid.setItems(gridRows);
|
||||
servicesSection.add(servicesGrid);
|
||||
|
||||
// Add button to add new row
|
||||
Button addButton = new Button("Leistung hinzufügen", e -> {
|
||||
ServiceRow newRow = new ServiceRow();
|
||||
gridRows.add(newRow);
|
||||
servicesGrid.getDataProvider().refreshAll();
|
||||
});
|
||||
addButton.getStyle().set("margin-top", "var(--lumo-space-m)");
|
||||
servicesSection.add(addButton);
|
||||
|
||||
return servicesSection;
|
||||
}
|
||||
|
||||
@@ -342,13 +308,12 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
||||
if (service.getCalculationBasis() == Service.CalculationBasis.FLAT_RATE && service.getPrice() != null) {
|
||||
return service.getPrice();
|
||||
} else if (service.getCalculationBasis() == Service.CalculationBasis.DISTANCE
|
||||
&& service.getPricePerKilometer() != null && kilometersField != null
|
||||
&& kilometersField.getValue() != null) {
|
||||
BigDecimal kilometers = new BigDecimal(kilometersField.getValue());
|
||||
&& service.getPricePerKilometer() != null && currentJob.getRouteDistanceKm() != null) {
|
||||
BigDecimal kilometers = BigDecimal.valueOf(currentJob.getRouteDistanceKm());
|
||||
return service.getPricePerKilometer().multiply(kilometers);
|
||||
} else if (service.getCalculationBasis() == Service.CalculationBasis.TIME
|
||||
&& service.getPricePer15Minutes() != null && timeField != null && timeField.getValue() != null) {
|
||||
BigDecimal timeUnits = new BigDecimal(timeField.getValue());
|
||||
&& service.getPricePer15Minutes() != null && currentJob.getTimeIn15MinUnits() != null) {
|
||||
BigDecimal timeUnits = new BigDecimal(currentJob.getTimeIn15MinUnits());
|
||||
return service.getPricePer15Minutes().multiply(timeUnits);
|
||||
}
|
||||
|
||||
@@ -362,14 +327,13 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
||||
if (service.getCalculationBasis() == Service.CalculationBasis.FLAT_RATE && service.getPrice() != null) {
|
||||
total = total.add(service.getPrice());
|
||||
} else if (service.getCalculationBasis() == Service.CalculationBasis.DISTANCE
|
||||
&& service.getPricePerKilometer() != null && kilometersField != null
|
||||
&& kilometersField.getValue() != null) {
|
||||
BigDecimal kilometers = new BigDecimal(kilometersField.getValue());
|
||||
&& service.getPricePerKilometer() != null && currentJob.getRouteDistanceKm() != null) {
|
||||
BigDecimal kilometers = BigDecimal.valueOf(currentJob.getRouteDistanceKm());
|
||||
BigDecimal serviceTotal = service.getPricePerKilometer().multiply(kilometers);
|
||||
total = total.add(serviceTotal);
|
||||
} else if (service.getCalculationBasis() == Service.CalculationBasis.TIME
|
||||
&& service.getPricePer15Minutes() != null && timeField != null && timeField.getValue() != null) {
|
||||
BigDecimal timeUnits = new BigDecimal(timeField.getValue());
|
||||
&& service.getPricePer15Minutes() != null && currentJob.getTimeIn15MinUnits() != null) {
|
||||
BigDecimal timeUnits = new BigDecimal(currentJob.getTimeIn15MinUnits());
|
||||
BigDecimal serviceTotal = service.getPricePer15Minutes().multiply(timeUnits);
|
||||
total = total.add(serviceTotal);
|
||||
}
|
||||
@@ -402,14 +366,6 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
||||
}
|
||||
|
||||
private void updateSummarySection() {
|
||||
// Update the job with new values
|
||||
if (kilometersField != null && kilometersField.getValue() != null) {
|
||||
currentJob.setKilometersDriven(kilometersField.getValue());
|
||||
}
|
||||
if (timeField != null && timeField.getValue() != null) {
|
||||
currentJob.setTimeIn15MinUnits(timeField.getValue());
|
||||
}
|
||||
|
||||
// Refresh the services grid to update calculated prices
|
||||
refreshServicesGrid();
|
||||
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
package de.assecutor.votianlt.pages.view;
|
||||
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.html.H2;
|
||||
import com.vaadin.flow.component.html.IFrame;
|
||||
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.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.server.StreamResource;
|
||||
import de.assecutor.votianlt.pages.base.ui.view.AdminLayout;
|
||||
import de.assecutor.votianlt.service.CustomerInvoiceService;
|
||||
import de.assecutor.votianlt.service.SystemInvoiceService;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
@Route(value = "pdf-test", layout = AdminLayout.class)
|
||||
@PageTitle("PDF Test")
|
||||
@RolesAllowed("ADMIN")
|
||||
public class PdfTestView extends VerticalLayout {
|
||||
private final SystemInvoiceService systemInvoiceService;
|
||||
private final CustomerInvoiceService customerInvoiceService;
|
||||
|
||||
public PdfTestView(SystemInvoiceService systemInvoiceService, CustomerInvoiceService customerInvoiceService) {
|
||||
this.systemInvoiceService = systemInvoiceService;
|
||||
this.customerInvoiceService = customerInvoiceService;
|
||||
|
||||
setSpacing(false);
|
||||
setPadding(false);
|
||||
getStyle().set("margin", "14px");
|
||||
setWidth("90%");
|
||||
|
||||
H2 title = new H2("PDF Test");
|
||||
add(title);
|
||||
|
||||
Button generateHtmlPdfButton = new Button("PDF aus system_invoice.html generieren");
|
||||
generateHtmlPdfButton.addClickListener(e -> generateHtmlPdf());
|
||||
|
||||
Button generateCustomerInvoicePdfButton = new Button("PDF aus customer_invoice.html generieren");
|
||||
generateCustomerInvoicePdfButton.addClickListener(e -> generateCustomerInvoicePdf());
|
||||
|
||||
// Create button layout
|
||||
HorizontalLayout buttonLayout = new HorizontalLayout();
|
||||
buttonLayout.add(generateHtmlPdfButton, generateCustomerInvoicePdfButton);
|
||||
buttonLayout.setSpacing(true);
|
||||
|
||||
// Initialize PDF viewer
|
||||
IFrame pdfViewer = new IFrame();
|
||||
pdfViewer.setWidth("100%");
|
||||
pdfViewer.setHeight("800px");
|
||||
pdfViewer.getStyle().set("border", "1px solid #ccc");
|
||||
pdfViewer.setVisible(false);
|
||||
|
||||
add(buttonLayout);
|
||||
add(pdfViewer);
|
||||
}
|
||||
|
||||
private void generateHtmlPdf() {
|
||||
try {
|
||||
byte[] pdfBytes = systemInvoiceService.generateInvoicePdfFromHtml();
|
||||
|
||||
StreamResource resource = new StreamResource("vlt-invoice.pdf", () -> new ByteArrayInputStream(pdfBytes));
|
||||
resource.setContentType("application/pdf");
|
||||
|
||||
getUI().ifPresent(ui -> {
|
||||
var registration = ui.getSession().getResourceRegistry().registerResource(resource);
|
||||
ui.getPage().open(registration.getResourceUri().toString(), "_blank");
|
||||
});
|
||||
|
||||
Notification.show("PDF aus HTML erfolgreich generiert!", 3000, Notification.Position.BOTTOM_CENTER);
|
||||
} catch (Exception ex) {
|
||||
Notification.show("Fehler beim Generieren des PDFs aus HTML: " + ex.getMessage(), 5000,
|
||||
Notification.Position.BOTTOM_CENTER);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateCustomerInvoicePdf() {
|
||||
try {
|
||||
byte[] pdfBytes = customerInvoiceService.generateCustomerInvoicePdf();
|
||||
|
||||
StreamResource resource = new StreamResource("customer-invoice.pdf",
|
||||
() -> new ByteArrayInputStream(pdfBytes));
|
||||
resource.setContentType("application/pdf");
|
||||
|
||||
getUI().ifPresent(ui -> {
|
||||
var registration = ui.getSession().getResourceRegistry().registerResource(resource);
|
||||
ui.getPage().open(registration.getResourceUri().toString(), "_blank");
|
||||
});
|
||||
|
||||
Notification.show("Customer PDF erfolgreich generiert!", 3000, Notification.Position.BOTTOM_CENTER);
|
||||
} catch (Exception ex) {
|
||||
Notification.show("Fehler beim Generieren des Customer PDFs: " + ex.getMessage(), 5000,
|
||||
Notification.Position.BOTTOM_CENTER);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user