Erweiterungen

This commit is contained in:
2025-09-19 14:47:27 +02:00
parent 82958afe61
commit 215e933f44
6 changed files with 67 additions and 69 deletions

View File

@@ -1,4 +1,4 @@
package de.assecutor.votianlt.model; package de.assecutor.votianlt.model.invoices;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
@@ -9,7 +9,7 @@ import java.time.LocalDate;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class Invoice { public class SystemInvoice {
private String id; private String id;
private String kunde; private String kunde;
private LocalDate datum; private LocalDate datum;

View File

@@ -1,8 +1,8 @@
package de.assecutor.votianlt.model; package de.assecutor.votianlt.model.invoices;
import java.util.List; import java.util.List;
public class InvoiceData { public class SystemInvoiceData {
private String companyName = "Assecutor"; private String companyName = "Assecutor";
private String companySubtitle = "Data Service GmbH"; private String companySubtitle = "Data Service GmbH";
private String companyStreet = "Gerhart-Hauptmann-Weg 14"; private String companyStreet = "Gerhart-Hauptmann-Weg 14";
@@ -22,7 +22,7 @@ public class InvoiceData {
private String recipientStreet; private String recipientStreet;
private String recipientCity; private String recipientCity;
private List<InvoiceItem> invoiceItems; private List<SystemInvoiceItem> systemInvoiceItems;
private String netAmount; private String netAmount;
private String vatRate = "19"; private String vatRate = "19";
private String vatAmount; private String vatAmount;
@@ -34,7 +34,7 @@ public class InvoiceData {
"Steuernummer: 22 294 53099 · USt-IdNr.: DE261094748 · Sitz: Geesthacht · Handelsregister: Lübeck HRB 8595<br>" + "Steuernummer: 22 294 53099 · USt-IdNr.: DE261094748 · Sitz: Geesthacht · Handelsregister: Lübeck HRB 8595<br>" +
"Bankverbindung: Hamburger Sparkasse · IBAN DE67200505501217139888 · BIC HASPDEHHXXX"; "Bankverbindung: Hamburger Sparkasse · IBAN DE67200505501217139888 · BIC HASPDEHHXXX";
public InvoiceData() { public SystemInvoiceData() {
} }
public String getCompanyName() { return companyName; } public String getCompanyName() { return companyName; }
@@ -85,8 +85,8 @@ public class InvoiceData {
public String getRecipientCity() { return recipientCity; } public String getRecipientCity() { return recipientCity; }
public void setRecipientCity(String recipientCity) { this.recipientCity = recipientCity; } public void setRecipientCity(String recipientCity) { this.recipientCity = recipientCity; }
public List<InvoiceItem> getInvoiceItems() { return invoiceItems; } public List<SystemInvoiceItem> getInvoiceItems() { return systemInvoiceItems; }
public void setInvoiceItems(List<InvoiceItem> invoiceItems) { this.invoiceItems = invoiceItems; } public void setInvoiceItems(List<SystemInvoiceItem> systemInvoiceItems) { this.systemInvoiceItems = systemInvoiceItems; }
public String getNetAmount() { return netAmount; } public String getNetAmount() { return netAmount; }
public void setNetAmount(String netAmount) { this.netAmount = netAmount; } public void setNetAmount(String netAmount) { this.netAmount = netAmount; }

View File

@@ -1,15 +1,15 @@
package de.assecutor.votianlt.model; package de.assecutor.votianlt.model.invoices;
public class InvoiceItem { public class SystemInvoiceItem {
private String quantity; private String quantity;
private String description; private String description;
private String unitPrice; private String unitPrice;
private String totalPrice; private String totalPrice;
public InvoiceItem() { public SystemInvoiceItem() {
} }
public InvoiceItem(String quantity, String description, String unitPrice, String totalPrice) { public SystemInvoiceItem(String quantity, String description, String unitPrice, String totalPrice) {
this.quantity = quantity; this.quantity = quantity;
this.description = description; this.description = description;
this.unitPrice = unitPrice; this.unitPrice = unitPrice;

View File

@@ -8,8 +8,7 @@ import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import com.vaadin.flow.component.UI; import com.vaadin.flow.component.UI;
import de.assecutor.votianlt.model.Invoice; import de.assecutor.votianlt.model.invoices.SystemInvoice;
import de.assecutor.votianlt.service.PdfBoxService;
import jakarta.annotation.security.RolesAllowed; import jakarta.annotation.security.RolesAllowed;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@@ -19,7 +18,6 @@ import java.util.List;
import com.vaadin.flow.server.StreamResource; import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.server.StreamRegistration; import com.vaadin.flow.server.StreamRegistration;
import org.springframework.beans.factory.annotation.Autowired;
@PageTitle("Rechnungen") @PageTitle("Rechnungen")
@Route(value = "invoices", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class) @Route(value = "invoices", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
@@ -27,7 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired;
public class InvoicesView extends VerticalLayout { public class InvoicesView extends VerticalLayout {
private final Grid<Invoice> invoiceGrid; private final Grid<SystemInvoice> invoiceGrid;
public InvoicesView() { public InvoicesView() {
setSizeFull(); setSizeFull();
@@ -39,39 +37,39 @@ public class InvoicesView extends VerticalLayout {
H2 title = new H2("Rechnungen"); H2 title = new H2("Rechnungen");
add(title); add(title);
invoiceGrid = new Grid<>(Invoice.class, false); invoiceGrid = new Grid<>(SystemInvoice.class, false);
invoiceGrid.addColumn(Invoice::getId).setHeader("Rechnungsnummer").setAutoWidth(true); invoiceGrid.addColumn(SystemInvoice::getId).setHeader("Rechnungsnummer").setAutoWidth(true);
invoiceGrid.addColumn(Invoice::getKunde).setHeader("Kunde").setAutoWidth(true); invoiceGrid.addColumn(SystemInvoice::getKunde).setHeader("Kunde").setAutoWidth(true);
invoiceGrid.addColumn(Invoice::getDatum).setHeader("Datum").setAutoWidth(true); invoiceGrid.addColumn(SystemInvoice::getDatum).setHeader("Datum").setAutoWidth(true);
invoiceGrid.addColumn(Invoice::getBetrag).setHeader("Betrag").setAutoWidth(true); invoiceGrid.addColumn(SystemInvoice::getBetrag).setHeader("Betrag").setAutoWidth(true);
invoiceGrid.addColumn(Invoice::getBeschreibung).setHeader("Beschreibung").setAutoWidth(true); invoiceGrid.addColumn(SystemInvoice::getBeschreibung).setHeader("Beschreibung").setAutoWidth(true);
invoiceGrid.setSelectionMode(Grid.SelectionMode.SINGLE); invoiceGrid.setSelectionMode(Grid.SelectionMode.SINGLE);
invoiceGrid.getStyle().set("cursor", "pointer"); invoiceGrid.getStyle().set("cursor", "pointer");
// Testdaten // Testdaten
List<Invoice> testInvoices = List.of( List<SystemInvoice> testSystemInvoices = List.of(
new Invoice("R-2024-001", "Max Mustermann", LocalDate.now().minusDays(2), 199.99, new SystemInvoice("R-2024-001", "Max Mustermann", LocalDate.now().minusDays(2), 199.99,
"Transport Hamburg-Berlin"), "Transport Hamburg-Berlin"),
new Invoice("R-2024-002", "Erika Musterfrau", LocalDate.now().minusDays(1), 299.49, new SystemInvoice("R-2024-002", "Erika Musterfrau", LocalDate.now().minusDays(1), 299.49,
"Express München-Köln"), "Express München-Köln"),
new Invoice("R-2024-003", "Hans Beispiel", LocalDate.now(), 149.00, "Standard Leipzig-Dresden")); new SystemInvoice("R-2024-003", "Hans Beispiel", LocalDate.now(), 149.00, "Standard Leipzig-Dresden"));
invoiceGrid.setItems(testInvoices); invoiceGrid.setItems(testSystemInvoices);
invoiceGrid.addItemClickListener(event -> { invoiceGrid.addItemClickListener(event -> {
Invoice invoice = event.getItem(); SystemInvoice systemInvoice = event.getItem();
if (invoice != null) { if (systemInvoice != null) {
downloadInvoicePdf(invoice); downloadInvoicePdf(systemInvoice);
} }
}); });
add(invoiceGrid); add(invoiceGrid);
} }
private void downloadInvoicePdf(Invoice invoice) { private void downloadInvoicePdf(SystemInvoice systemInvoice) {
try { try {
// PDF generieren // PDF generieren
byte[] pdfBytes = generatePdf(invoice); byte[] pdfBytes = generatePdf(systemInvoice);
StreamResource resource = new StreamResource(invoice.getId() + ".pdf", StreamResource resource = new StreamResource(systemInvoice.getId() + ".pdf",
() -> new ByteArrayInputStream(pdfBytes)); () -> new ByteArrayInputStream(pdfBytes));
resource.setContentType("application/pdf"); resource.setContentType("application/pdf");
resource.setCacheTime(0); resource.setCacheTime(0);
@@ -86,17 +84,17 @@ public class InvoicesView extends VerticalLayout {
} }
} }
private byte[] generatePdf(Invoice invoice) { private byte[] generatePdf(SystemInvoice systemInvoice) {
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
// Einfache PDF mit iText (oder OpenPDF, falls iText nicht verfügbar) // Einfache PDF mit iText (oder OpenPDF, falls iText nicht verfügbar)
com.lowagie.text.Document document = new com.lowagie.text.Document(); com.lowagie.text.Document document = new com.lowagie.text.Document();
com.lowagie.text.pdf.PdfWriter.getInstance(document, out); com.lowagie.text.pdf.PdfWriter.getInstance(document, out);
document.open(); document.open();
document.add(new com.lowagie.text.Paragraph("Rechnung: " + invoice.getId())); document.add(new com.lowagie.text.Paragraph("Rechnung: " + systemInvoice.getId()));
document.add(new com.lowagie.text.Paragraph("Kunde: " + invoice.getKunde())); document.add(new com.lowagie.text.Paragraph("Kunde: " + systemInvoice.getKunde()));
document.add(new com.lowagie.text.Paragraph("Datum: " + invoice.getDatum())); document.add(new com.lowagie.text.Paragraph("Datum: " + systemInvoice.getDatum()));
document.add(new com.lowagie.text.Paragraph("Betrag: " + invoice.getBetrag() + " EUR")); document.add(new com.lowagie.text.Paragraph("Betrag: " + systemInvoice.getBetrag() + " EUR"));
document.add(new com.lowagie.text.Paragraph("Beschreibung: " + invoice.getBeschreibung())); document.add(new com.lowagie.text.Paragraph("Beschreibung: " + systemInvoice.getBeschreibung()));
document.close(); document.close();
return out.toByteArray(); return out.toByteArray();
} catch (Exception e) { } catch (Exception e) {

View File

@@ -7,9 +7,9 @@ import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.StreamResource; import com.vaadin.flow.server.StreamResource;
import de.assecutor.votianlt.model.InvoiceData; import de.assecutor.votianlt.model.invoices.SystemInvoiceData;
import de.assecutor.votianlt.pages.base.ui.view.AdminLayout; import de.assecutor.votianlt.pages.base.ui.view.AdminLayout;
import de.assecutor.votianlt.service.PdfBoxService; import de.assecutor.votianlt.service.SystemInvoiceService;
import jakarta.annotation.security.RolesAllowed; import jakarta.annotation.security.RolesAllowed;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@@ -18,10 +18,10 @@ import java.io.ByteArrayInputStream;
@PageTitle("PDF Test") @PageTitle("PDF Test")
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
public class PdfTestView extends VerticalLayout { public class PdfTestView extends VerticalLayout {
private final PdfBoxService pdfBoxService; private final SystemInvoiceService systemInvoiceService;
public PdfTestView(PdfBoxService pdfBoxService) { public PdfTestView(SystemInvoiceService systemInvoiceService) {
this.pdfBoxService = pdfBoxService; this.systemInvoiceService = systemInvoiceService;
setSpacing(false); setSpacing(false);
setPadding(false); setPadding(false);
@@ -39,8 +39,8 @@ public class PdfTestView extends VerticalLayout {
private void generateTestPdf() { private void generateTestPdf() {
try { try {
InvoiceData sampleData = pdfBoxService.createSampleInvoiceData(); SystemInvoiceData sampleData = systemInvoiceService.createSampleInvoiceData();
byte[] pdfBytes = pdfBoxService.generateInvoicePdf(sampleData); byte[] pdfBytes = systemInvoiceService.generateInvoicePdf(sampleData);
StreamResource resource = new StreamResource("test-rechnung.pdf", StreamResource resource = new StreamResource("test-rechnung.pdf",
() -> new ByteArrayInputStream(pdfBytes)); () -> new ByteArrayInputStream(pdfBytes));

View File

@@ -1,7 +1,7 @@
package de.assecutor.votianlt.service; package de.assecutor.votianlt.service;
import de.assecutor.votianlt.model.InvoiceData; import de.assecutor.votianlt.model.invoices.SystemInvoiceData;
import de.assecutor.votianlt.model.InvoiceItem; import de.assecutor.votianlt.model.invoices.SystemInvoiceItem;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.PDPageContentStream;
@@ -16,7 +16,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
@Service @Service
public class PdfBoxService { public class SystemInvoiceService {
private static final float MARGIN = 50; private static final float MARGIN = 50;
private static final float FONT_SIZE = 12; private static final float FONT_SIZE = 12;
@@ -24,7 +24,7 @@ public class PdfBoxService {
private static final float SUBTITLE_FONT_SIZE = 16; private static final float SUBTITLE_FONT_SIZE = 16;
private static final float LINE_HEIGHT = 15; private static final float LINE_HEIGHT = 15;
public byte[] generateInvoicePdf(InvoiceData invoiceData) throws IOException { public byte[] generateInvoicePdf(SystemInvoiceData systemInvoiceData) throws IOException {
try (PDDocument document = new PDDocument(); try (PDDocument document = new PDDocument();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
@@ -35,30 +35,30 @@ public class PdfBoxService {
float yPosition = page.getMediaBox().getHeight() - MARGIN; float yPosition = page.getMediaBox().getHeight() - MARGIN;
// Draw header // Draw header
yPosition = drawHeader(contentStream, yPosition, invoiceData); yPosition = drawHeader(contentStream, yPosition, systemInvoiceData);
yPosition -= 30; yPosition -= 30;
// Draw company and recipient info // Draw company and recipient info
yPosition = drawAddresses(contentStream, yPosition, invoiceData); yPosition = drawAddresses(contentStream, yPosition, systemInvoiceData);
yPosition -= 30; yPosition -= 30;
// Draw invoice details // Draw invoice details
yPosition = drawInvoiceDetails(contentStream, yPosition, invoiceData); yPosition = drawInvoiceDetails(contentStream, yPosition, systemInvoiceData);
yPosition -= 30; yPosition -= 30;
// Draw items table // Draw items table
yPosition = drawItemsTable(contentStream, yPosition, invoiceData); yPosition = drawItemsTable(contentStream, yPosition, systemInvoiceData);
yPosition -= 30; yPosition -= 30;
// Draw totals // Draw totals
yPosition = drawTotals(contentStream, yPosition, invoiceData); yPosition = drawTotals(contentStream, yPosition, systemInvoiceData);
yPosition -= 30; yPosition -= 30;
// Draw payment terms // Draw payment terms
yPosition = drawPaymentTerms(contentStream, yPosition, invoiceData); yPosition = drawPaymentTerms(contentStream, yPosition, systemInvoiceData);
// Draw footer at bottom of page // Draw footer at bottom of page
drawFooter(contentStream, page, invoiceData); drawFooter(contentStream, page, systemInvoiceData);
} }
document.save(outputStream); document.save(outputStream);
@@ -66,7 +66,7 @@ public class PdfBoxService {
} }
} }
private float drawHeader(PDPageContentStream contentStream, float yPosition, InvoiceData data) throws IOException { private float drawHeader(PDPageContentStream contentStream, float yPosition, SystemInvoiceData data) throws IOException {
// Company name // Company name
contentStream.beginText(); contentStream.beginText();
contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD), TITLE_FONT_SIZE); contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD), TITLE_FONT_SIZE);
@@ -97,7 +97,7 @@ public class PdfBoxService {
return yPosition - 10; return yPosition - 10;
} }
private float drawAddresses(PDPageContentStream contentStream, float yPosition, InvoiceData data) throws IOException { private float drawAddresses(PDPageContentStream contentStream, float yPosition, SystemInvoiceData data) throws IOException {
contentStream.setNonStrokingColor(0f, 0f, 0f); // Black contentStream.setNonStrokingColor(0f, 0f, 0f); // Black
contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), FONT_SIZE); contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), FONT_SIZE);
@@ -152,7 +152,7 @@ public class PdfBoxService {
return Math.min(yPosition, rightYPosition) - 10; return Math.min(yPosition, rightYPosition) - 10;
} }
private float drawInvoiceDetails(PDPageContentStream contentStream, float yPosition, InvoiceData data) throws IOException { private float drawInvoiceDetails(PDPageContentStream contentStream, float yPosition, SystemInvoiceData data) throws IOException {
// Invoice title // Invoice title
contentStream.beginText(); contentStream.beginText();
@@ -194,7 +194,7 @@ public class PdfBoxService {
return yPosition - 40; return yPosition - 40;
} }
private float drawItemsTable(PDPageContentStream contentStream, float yPosition, InvoiceData data) throws IOException { private float drawItemsTable(PDPageContentStream contentStream, float yPosition, SystemInvoiceData data) throws IOException {
float[] columnWidths = {60, 300, 100, 100}; // Menge, Bezeichnung, Einzelpreis, Gesamt float[] columnWidths = {60, 300, 100, 100}; // Menge, Bezeichnung, Einzelpreis, Gesamt
float rowHeight = 20; float rowHeight = 20;
@@ -248,7 +248,7 @@ public class PdfBoxService {
contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), 10); contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), 10);
if (data.getInvoiceItems() != null) { if (data.getInvoiceItems() != null) {
for (InvoiceItem item : data.getInvoiceItems()) { for (SystemInvoiceItem item : data.getInvoiceItems()) {
xOffset = MARGIN + 5; xOffset = MARGIN + 5;
contentStream.beginText(); contentStream.beginText();
@@ -286,7 +286,7 @@ public class PdfBoxService {
return yPosition; return yPosition;
} }
private float drawTotals(PDPageContentStream contentStream, float yPosition, InvoiceData data) throws IOException { private float drawTotals(PDPageContentStream contentStream, float yPosition, SystemInvoiceData data) throws IOException {
float rightColumn = 400; float rightColumn = 400;
contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), FONT_SIZE); contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), FONT_SIZE);
@@ -332,7 +332,7 @@ public class PdfBoxService {
return yPosition - 30; return yPosition - 30;
} }
private float drawPaymentTerms(PDPageContentStream contentStream, float yPosition, InvoiceData data) throws IOException { private float drawPaymentTerms(PDPageContentStream contentStream, float yPosition, SystemInvoiceData data) throws IOException {
contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), FONT_SIZE); contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), FONT_SIZE);
// Payment terms with text wrapping // Payment terms with text wrapping
@@ -385,7 +385,7 @@ public class PdfBoxService {
return text.length() * fontSize * 0.6f; return text.length() * fontSize * 0.6f;
} }
private void drawFooter(PDPageContentStream contentStream, PDPage page, InvoiceData data) throws IOException { private void drawFooter(PDPageContentStream contentStream, PDPage page, SystemInvoiceData data) throws IOException {
contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), 8); contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), 8);
// Footer text (company details) - positioned at absolute bottom // Footer text (company details) - positioned at absolute bottom
@@ -410,8 +410,8 @@ public class PdfBoxService {
} }
} }
public InvoiceData createSampleInvoiceData() { public SystemInvoiceData createSampleInvoiceData() {
InvoiceData data = new InvoiceData(); SystemInvoiceData data = new SystemInvoiceData();
data.setInvoiceNumber("HHA-2021-007"); data.setInvoiceNumber("HHA-2021-007");
data.setInvoiceDate("19.07.2021"); data.setInvoiceDate("19.07.2021");
data.setInvoiceText("Gemäß unserem Nutzungsvertrag zu der Bestellnummer 45519389 berechnen wir Ihnen für den Monat Juli 2021 wie folgt:"); data.setInvoiceText("Gemäß unserem Nutzungsvertrag zu der Bestellnummer 45519389 berechnen wir Ihnen für den Monat Juli 2021 wie folgt:");
@@ -421,8 +421,8 @@ public class PdfBoxService {
data.setRecipientStreet("Steinstraße 20"); data.setRecipientStreet("Steinstraße 20");
data.setRecipientCity("20095 Hamburg"); data.setRecipientCity("20095 Hamburg");
List<InvoiceItem> items = new ArrayList<>(); List<SystemInvoiceItem> items = new ArrayList<>();
items.add(new InvoiceItem("1", "Mtl. Lizenzgebühr »ILLT«", "5.639,00 €", "5.639,00 €")); items.add(new SystemInvoiceItem("1", "Mtl. Lizenzgebühr »ILLT«", "5.639,00 €", "5.639,00 €"));
data.setInvoiceItems(items); data.setInvoiceItems(items);
data.setNetAmount("5.639,00 €"); data.setNetAmount("5.639,00 €");