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.Data;
@@ -9,7 +9,7 @@ import java.time.LocalDate;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Invoice {
public class SystemInvoice {
private String id;
private String kunde;
private LocalDate datum;

View File

@@ -1,8 +1,8 @@
package de.assecutor.votianlt.model;
package de.assecutor.votianlt.model.invoices;
import java.util.List;
public class InvoiceData {
public class SystemInvoiceData {
private String companyName = "Assecutor";
private String companySubtitle = "Data Service GmbH";
private String companyStreet = "Gerhart-Hauptmann-Weg 14";
@@ -22,7 +22,7 @@ public class InvoiceData {
private String recipientStreet;
private String recipientCity;
private List<InvoiceItem> invoiceItems;
private List<SystemInvoiceItem> systemInvoiceItems;
private String netAmount;
private String vatRate = "19";
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>" +
"Bankverbindung: Hamburger Sparkasse · IBAN DE67200505501217139888 · BIC HASPDEHHXXX";
public InvoiceData() {
public SystemInvoiceData() {
}
public String getCompanyName() { return companyName; }
@@ -85,8 +85,8 @@ public class InvoiceData {
public String getRecipientCity() { return recipientCity; }
public void setRecipientCity(String recipientCity) { this.recipientCity = recipientCity; }
public List<InvoiceItem> getInvoiceItems() { return invoiceItems; }
public void setInvoiceItems(List<InvoiceItem> invoiceItems) { this.invoiceItems = invoiceItems; }
public List<SystemInvoiceItem> getInvoiceItems() { return systemInvoiceItems; }
public void setInvoiceItems(List<SystemInvoiceItem> systemInvoiceItems) { this.systemInvoiceItems = systemInvoiceItems; }
public String getNetAmount() { return 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 description;
private String unitPrice;
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.description = description;
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.Route;
import com.vaadin.flow.component.UI;
import de.assecutor.votianlt.model.Invoice;
import de.assecutor.votianlt.service.PdfBoxService;
import de.assecutor.votianlt.model.invoices.SystemInvoice;
import jakarta.annotation.security.RolesAllowed;
import java.io.ByteArrayInputStream;
@@ -19,7 +18,6 @@ import java.util.List;
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.server.StreamRegistration;
import org.springframework.beans.factory.annotation.Autowired;
@PageTitle("Rechnungen")
@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 {
private final Grid<Invoice> invoiceGrid;
private final Grid<SystemInvoice> invoiceGrid;
public InvoicesView() {
setSizeFull();
@@ -39,39 +37,39 @@ public class InvoicesView extends VerticalLayout {
H2 title = new H2("Rechnungen");
add(title);
invoiceGrid = new Grid<>(Invoice.class, false);
invoiceGrid.addColumn(Invoice::getId).setHeader("Rechnungsnummer").setAutoWidth(true);
invoiceGrid.addColumn(Invoice::getKunde).setHeader("Kunde").setAutoWidth(true);
invoiceGrid.addColumn(Invoice::getDatum).setHeader("Datum").setAutoWidth(true);
invoiceGrid.addColumn(Invoice::getBetrag).setHeader("Betrag").setAutoWidth(true);
invoiceGrid.addColumn(Invoice::getBeschreibung).setHeader("Beschreibung").setAutoWidth(true);
invoiceGrid = new Grid<>(SystemInvoice.class, false);
invoiceGrid.addColumn(SystemInvoice::getId).setHeader("Rechnungsnummer").setAutoWidth(true);
invoiceGrid.addColumn(SystemInvoice::getKunde).setHeader("Kunde").setAutoWidth(true);
invoiceGrid.addColumn(SystemInvoice::getDatum).setHeader("Datum").setAutoWidth(true);
invoiceGrid.addColumn(SystemInvoice::getBetrag).setHeader("Betrag").setAutoWidth(true);
invoiceGrid.addColumn(SystemInvoice::getBeschreibung).setHeader("Beschreibung").setAutoWidth(true);
invoiceGrid.setSelectionMode(Grid.SelectionMode.SINGLE);
invoiceGrid.getStyle().set("cursor", "pointer");
// Testdaten
List<Invoice> testInvoices = List.of(
new Invoice("R-2024-001", "Max Mustermann", LocalDate.now().minusDays(2), 199.99,
List<SystemInvoice> testSystemInvoices = List.of(
new SystemInvoice("R-2024-001", "Max Mustermann", LocalDate.now().minusDays(2), 199.99,
"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"),
new Invoice("R-2024-003", "Hans Beispiel", LocalDate.now(), 149.00, "Standard Leipzig-Dresden"));
invoiceGrid.setItems(testInvoices);
new SystemInvoice("R-2024-003", "Hans Beispiel", LocalDate.now(), 149.00, "Standard Leipzig-Dresden"));
invoiceGrid.setItems(testSystemInvoices);
invoiceGrid.addItemClickListener(event -> {
Invoice invoice = event.getItem();
if (invoice != null) {
downloadInvoicePdf(invoice);
SystemInvoice systemInvoice = event.getItem();
if (systemInvoice != null) {
downloadInvoicePdf(systemInvoice);
}
});
add(invoiceGrid);
}
private void downloadInvoicePdf(Invoice invoice) {
private void downloadInvoicePdf(SystemInvoice systemInvoice) {
try {
// PDF generieren
byte[] pdfBytes = generatePdf(invoice);
StreamResource resource = new StreamResource(invoice.getId() + ".pdf",
byte[] pdfBytes = generatePdf(systemInvoice);
StreamResource resource = new StreamResource(systemInvoice.getId() + ".pdf",
() -> new ByteArrayInputStream(pdfBytes));
resource.setContentType("application/pdf");
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()) {
// Einfache PDF mit iText (oder OpenPDF, falls iText nicht verfügbar)
com.lowagie.text.Document document = new com.lowagie.text.Document();
com.lowagie.text.pdf.PdfWriter.getInstance(document, out);
document.open();
document.add(new com.lowagie.text.Paragraph("Rechnung: " + invoice.getId()));
document.add(new com.lowagie.text.Paragraph("Kunde: " + invoice.getKunde()));
document.add(new com.lowagie.text.Paragraph("Datum: " + invoice.getDatum()));
document.add(new com.lowagie.text.Paragraph("Betrag: " + invoice.getBetrag() + " EUR"));
document.add(new com.lowagie.text.Paragraph("Beschreibung: " + invoice.getBeschreibung()));
document.add(new com.lowagie.text.Paragraph("Rechnung: " + systemInvoice.getId()));
document.add(new com.lowagie.text.Paragraph("Kunde: " + systemInvoice.getKunde()));
document.add(new com.lowagie.text.Paragraph("Datum: " + systemInvoice.getDatum()));
document.add(new com.lowagie.text.Paragraph("Betrag: " + systemInvoice.getBetrag() + " EUR"));
document.add(new com.lowagie.text.Paragraph("Beschreibung: " + systemInvoice.getBeschreibung()));
document.close();
return out.toByteArray();
} 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.Route;
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.service.PdfBoxService;
import de.assecutor.votianlt.service.SystemInvoiceService;
import jakarta.annotation.security.RolesAllowed;
import java.io.ByteArrayInputStream;
@@ -18,10 +18,10 @@ import java.io.ByteArrayInputStream;
@PageTitle("PDF Test")
@RolesAllowed("ADMIN")
public class PdfTestView extends VerticalLayout {
private final PdfBoxService pdfBoxService;
private final SystemInvoiceService systemInvoiceService;
public PdfTestView(PdfBoxService pdfBoxService) {
this.pdfBoxService = pdfBoxService;
public PdfTestView(SystemInvoiceService systemInvoiceService) {
this.systemInvoiceService = systemInvoiceService;
setSpacing(false);
setPadding(false);
@@ -39,8 +39,8 @@ public class PdfTestView extends VerticalLayout {
private void generateTestPdf() {
try {
InvoiceData sampleData = pdfBoxService.createSampleInvoiceData();
byte[] pdfBytes = pdfBoxService.generateInvoicePdf(sampleData);
SystemInvoiceData sampleData = systemInvoiceService.createSampleInvoiceData();
byte[] pdfBytes = systemInvoiceService.generateInvoicePdf(sampleData);
StreamResource resource = new StreamResource("test-rechnung.pdf",
() -> new ByteArrayInputStream(pdfBytes));

View File

@@ -1,7 +1,7 @@
package de.assecutor.votianlt.service;
import de.assecutor.votianlt.model.InvoiceData;
import de.assecutor.votianlt.model.InvoiceItem;
import de.assecutor.votianlt.model.invoices.SystemInvoiceData;
import de.assecutor.votianlt.model.invoices.SystemInvoiceItem;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
@@ -16,7 +16,7 @@ import java.util.ArrayList;
import java.util.List;
@Service
public class PdfBoxService {
public class SystemInvoiceService {
private static final float MARGIN = 50;
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 LINE_HEIGHT = 15;
public byte[] generateInvoicePdf(InvoiceData invoiceData) throws IOException {
public byte[] generateInvoicePdf(SystemInvoiceData systemInvoiceData) throws IOException {
try (PDDocument document = new PDDocument();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
@@ -35,30 +35,30 @@ public class PdfBoxService {
float yPosition = page.getMediaBox().getHeight() - MARGIN;
// Draw header
yPosition = drawHeader(contentStream, yPosition, invoiceData);
yPosition = drawHeader(contentStream, yPosition, systemInvoiceData);
yPosition -= 30;
// Draw company and recipient info
yPosition = drawAddresses(contentStream, yPosition, invoiceData);
yPosition = drawAddresses(contentStream, yPosition, systemInvoiceData);
yPosition -= 30;
// Draw invoice details
yPosition = drawInvoiceDetails(contentStream, yPosition, invoiceData);
yPosition = drawInvoiceDetails(contentStream, yPosition, systemInvoiceData);
yPosition -= 30;
// Draw items table
yPosition = drawItemsTable(contentStream, yPosition, invoiceData);
yPosition = drawItemsTable(contentStream, yPosition, systemInvoiceData);
yPosition -= 30;
// Draw totals
yPosition = drawTotals(contentStream, yPosition, invoiceData);
yPosition = drawTotals(contentStream, yPosition, systemInvoiceData);
yPosition -= 30;
// Draw payment terms
yPosition = drawPaymentTerms(contentStream, yPosition, invoiceData);
yPosition = drawPaymentTerms(contentStream, yPosition, systemInvoiceData);
// Draw footer at bottom of page
drawFooter(contentStream, page, invoiceData);
drawFooter(contentStream, page, systemInvoiceData);
}
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
contentStream.beginText();
contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD), TITLE_FONT_SIZE);
@@ -97,7 +97,7 @@ public class PdfBoxService {
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.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), FONT_SIZE);
@@ -152,7 +152,7 @@ public class PdfBoxService {
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
contentStream.beginText();
@@ -194,7 +194,7 @@ public class PdfBoxService {
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 rowHeight = 20;
@@ -248,7 +248,7 @@ public class PdfBoxService {
contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), 10);
if (data.getInvoiceItems() != null) {
for (InvoiceItem item : data.getInvoiceItems()) {
for (SystemInvoiceItem item : data.getInvoiceItems()) {
xOffset = MARGIN + 5;
contentStream.beginText();
@@ -286,7 +286,7 @@ public class PdfBoxService {
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;
contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), FONT_SIZE);
@@ -332,7 +332,7 @@ public class PdfBoxService {
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);
// Payment terms with text wrapping
@@ -385,7 +385,7 @@ public class PdfBoxService {
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);
// Footer text (company details) - positioned at absolute bottom
@@ -410,8 +410,8 @@ public class PdfBoxService {
}
}
public InvoiceData createSampleInvoiceData() {
InvoiceData data = new InvoiceData();
public SystemInvoiceData createSampleInvoiceData() {
SystemInvoiceData data = new SystemInvoiceData();
data.setInvoiceNumber("HHA-2021-007");
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:");
@@ -421,8 +421,8 @@ public class PdfBoxService {
data.setRecipientStreet("Steinstraße 20");
data.setRecipientCity("20095 Hamburg");
List<InvoiceItem> items = new ArrayList<>();
items.add(new InvoiceItem("1", "Mtl. Lizenzgebühr »ILLT«", "5.639,00 €", "5.639,00 €"));
List<SystemInvoiceItem> items = new ArrayList<>();
items.add(new SystemInvoiceItem("1", "Mtl. Lizenzgebühr »ILLT«", "5.639,00 €", "5.639,00 €"));
data.setInvoiceItems(items);
data.setNetAmount("5.639,00 €");