From a1b4b3035ddd939512aa35e1c7657d3b3303abcf Mon Sep 17 00:00:00 2001 From: Sven Carstensen Date: Fri, 19 Sep 2025 14:31:51 +0200 Subject: [PATCH] Erweiterungen --- pom.xml | 21 -- .../votianlt/pages/view/EditProfileView.java | 38 +-- .../votianlt/pages/view/InvoicesView.java | 3 + .../votianlt/pages/view/PdfTestView.java | 38 +-- .../votianlt/service/PdfService.java | 146 ---------- .../resources/templates/invoice-template.html | 270 ------------------ 6 files changed, 24 insertions(+), 492 deletions(-) delete mode 100644 src/main/java/de/assecutor/votianlt/service/PdfService.java delete mode 100644 src/main/resources/templates/invoice-template.html diff --git a/pom.xml b/pom.xml index 045894f..c64bf6e 100644 --- a/pom.xml +++ b/pom.xml @@ -104,27 +104,6 @@ 1.3.30 - - - com.itextpdf - itext7-core - 7.2.5 - pom - - - - - com.itextpdf - kernel - 7.2.5 - - - - - com.itextpdf - layout - 7.2.5 - org.springframework.boot diff --git a/src/main/java/de/assecutor/votianlt/pages/view/EditProfileView.java b/src/main/java/de/assecutor/votianlt/pages/view/EditProfileView.java index a5174a4..0e3d74d 100644 --- a/src/main/java/de/assecutor/votianlt/pages/view/EditProfileView.java +++ b/src/main/java/de/assecutor/votianlt/pages/view/EditProfileView.java @@ -420,32 +420,20 @@ public class EditProfileView extends HorizontalLayout { } } - // Einfache PDF-Vorschau generieren (kann später durch echte Logik ersetzt - // werden) + // Einfache Text-Vorschau generieren (PDF-Funktionalität entfernt) private byte[] generatePreviewPdf() { - try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { - 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("Rechnungsvorschau")); - document.add(new com.lowagie.text.Paragraph(" ")); - document.add(new com.lowagie.text.Paragraph("Rechnungs Präfix: " + safe(prefixField))); - document.add(new com.lowagie.text.Paragraph("USt-IdNr.: " + safe(ustIdField))); - document.add(new com.lowagie.text.Paragraph("Steuernummer: " + safe(taxNumberField))); - document.add(new com.lowagie.text.Paragraph("Bankname: " + safe(bankNameField))); - document.add(new com.lowagie.text.Paragraph("IBAN: " + safe(ibanField))); - document.add(new com.lowagie.text.Paragraph("Steuersatz: " + safe(taxRateField))); - document.add(new com.lowagie.text.Paragraph(" ")); - document.add(new com.lowagie.text.Paragraph("Einleitungstext:")); - document.add(new com.lowagie.text.Paragraph(safe(introTextArea))); - document.add(new com.lowagie.text.Paragraph(" ")); - document.add(new com.lowagie.text.Paragraph("Zahlungsbedingungen:")); - document.add(new com.lowagie.text.Paragraph(safe(termsTextArea))); - document.close(); - return out.toByteArray(); - } catch (Exception e) { - throw new RuntimeException("PDF-Generierung fehlgeschlagen: " + e.getMessage(), e); - } + StringBuilder content = new StringBuilder(); + content.append("RECHNUNGSVORSCHAU\n\n"); + content.append("Rechnungs Präfix: ").append(safe(prefixField)).append("\n"); + content.append("USt-IdNr.: ").append(safe(ustIdField)).append("\n"); + content.append("Steuernummer: ").append(safe(taxNumberField)).append("\n"); + content.append("Bankname: ").append(safe(bankNameField)).append("\n"); + content.append("IBAN: ").append(safe(ibanField)).append("\n"); + content.append("Steuersatz: ").append(safe(taxRateField)).append("\n\n"); + content.append("Einleitungstext:\n").append(safe(introTextArea)).append("\n\n"); + content.append("Zahlungsbedingungen:\n").append(safe(termsTextArea)).append("\n"); + + return content.toString().getBytes(java.nio.charset.StandardCharsets.UTF_8); } // Utility: safe getter für TextField/TextArea diff --git a/src/main/java/de/assecutor/votianlt/pages/view/InvoicesView.java b/src/main/java/de/assecutor/votianlt/pages/view/InvoicesView.java index c9b476f..f13973f 100644 --- a/src/main/java/de/assecutor/votianlt/pages/view/InvoicesView.java +++ b/src/main/java/de/assecutor/votianlt/pages/view/InvoicesView.java @@ -9,6 +9,7 @@ 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 jakarta.annotation.security.RolesAllowed; import java.io.ByteArrayInputStream; @@ -18,12 +19,14 @@ 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) @RolesAllowed({ "USER", "ADMIN" }) public class InvoicesView extends VerticalLayout { + private final Grid invoiceGrid; public InvoicesView() { diff --git a/src/main/java/de/assecutor/votianlt/pages/view/PdfTestView.java b/src/main/java/de/assecutor/votianlt/pages/view/PdfTestView.java index b4c2eca..937e8ee 100644 --- a/src/main/java/de/assecutor/votianlt/pages/view/PdfTestView.java +++ b/src/main/java/de/assecutor/votianlt/pages/view/PdfTestView.java @@ -9,21 +9,19 @@ import com.vaadin.flow.router.Route; import com.vaadin.flow.server.StreamResource; import de.assecutor.votianlt.model.InvoiceData; import de.assecutor.votianlt.pages.base.ui.view.AdminLayout; -import de.assecutor.votianlt.service.PdfService; +import de.assecutor.votianlt.service.PdfBoxService; import jakarta.annotation.security.RolesAllowed; import java.io.ByteArrayInputStream; -import java.nio.charset.StandardCharsets; @Route(value = "pdf-test", layout = AdminLayout.class) @PageTitle("PDF Test") @RolesAllowed("ADMIN") public class PdfTestView extends VerticalLayout { - - private final PdfService pdfService; - - public PdfTestView(PdfService pdfService) { - this.pdfService = pdfService; + private final PdfBoxService pdfBoxService; + + public PdfTestView(PdfBoxService pdfBoxService) { + this.pdfBoxService = pdfBoxService; setSpacing(false); setPadding(false); @@ -36,16 +34,13 @@ public class PdfTestView extends VerticalLayout { Button generatePdfButton = new Button("Test-Rechnung PDF generieren"); generatePdfButton.addClickListener(e -> generateTestPdf()); - Button generateHtmlButton = new Button("HTML-Vorschau generieren"); - generateHtmlButton.addClickListener(e -> generateTestHtml()); - - add(generatePdfButton, generateHtmlButton); + add(generatePdfButton); } private void generateTestPdf() { try { - InvoiceData sampleData = pdfService.createSampleInvoiceData(); - byte[] pdfBytes = pdfService.generateInvoicePdf(sampleData); + InvoiceData sampleData = pdfBoxService.createSampleInvoiceData(); + byte[] pdfBytes = pdfBoxService.generateInvoicePdf(sampleData); StreamResource resource = new StreamResource("test-rechnung.pdf", () -> new ByteArrayInputStream(pdfBytes)); @@ -63,22 +58,5 @@ public class PdfTestView extends VerticalLayout { } } - private void generateTestHtml() { - try { - InvoiceData sampleData = pdfService.createSampleInvoiceData(); - byte[] htmlBytes = pdfService.generateInvoiceHtml(sampleData); - // Create anchor for download - String htmlContent = new String(htmlBytes, StandardCharsets.UTF_8); - String dataUrl = "data:text/html;charset=utf-8," + - java.net.URLEncoder.encode(htmlContent, StandardCharsets.UTF_8); - - getUI().ifPresent(ui -> ui.getPage().open(dataUrl, "_blank")); - - Notification.show("HTML-Vorschau erfolgreich generiert!", 3000, Notification.Position.BOTTOM_CENTER); - } catch (Exception ex) { - Notification.show("Fehler beim Generieren der Vorschau: " + ex.getMessage(), - 5000, Notification.Position.BOTTOM_CENTER); - } - } } \ No newline at end of file diff --git a/src/main/java/de/assecutor/votianlt/service/PdfService.java b/src/main/java/de/assecutor/votianlt/service/PdfService.java deleted file mode 100644 index ac176d9..0000000 --- a/src/main/java/de/assecutor/votianlt/service/PdfService.java +++ /dev/null @@ -1,146 +0,0 @@ -package de.assecutor.votianlt.service; - -import de.assecutor.votianlt.model.InvoiceData; -import de.assecutor.votianlt.model.InvoiceItem; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.ClassPathResource; -import org.springframework.stereotype.Service; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; - -@Service -public class PdfService { - - @Autowired - private PdfBoxService pdfBoxService; - - public byte[] generateInvoicePdf(InvoiceData invoiceData) throws IOException { - // Use PDFBox for actual PDF generation - return pdfBoxService.generateInvoicePdf(invoiceData); - } - - public byte[] generateInvoiceHtml(InvoiceData invoiceData) throws IOException { - // Keep HTML generation for preview purposes - String htmlTemplate = loadTemplate(); - String processedHtml = processTemplate(htmlTemplate, invoiceData); - return processedHtml.getBytes(StandardCharsets.UTF_8); - } - - private String loadTemplate() throws IOException { - ClassPathResource resource = new ClassPathResource("templates/invoice-template.html"); - try (InputStream inputStream = resource.getInputStream()) { - return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); - } - } - - private String processTemplate(String template, InvoiceData data) { - String processed = template; - - // Replace company information - processed = processed.replace("{{companyName}}", escapeHtml(safeString(data.getCompanyName()))); - processed = processed.replace("{{companySubtitle}}", escapeHtml(safeString(data.getCompanySubtitle()))); - processed = processed.replace("{{companyStreet}}", escapeHtml(safeString(data.getCompanyStreet()))); - processed = processed.replace("{{companyCity}}", escapeHtml(safeString(data.getCompanyCity()))); - processed = processed.replace("{{companyPhone}}", escapeHtml(safeString(data.getCompanyPhone()))); - processed = processed.replace("{{companyFax}}", escapeHtml(safeString(data.getCompanyFax()))); - processed = processed.replace("{{companyEmail}}", escapeHtml(safeString(data.getCompanyEmail()))); - processed = processed.replace("{{companyWebsite}}", escapeHtml(safeString(data.getCompanyWebsite()))); - - // Replace invoice details - processed = processed.replace("{{invoiceNumber}}", escapeHtml(safeString(data.getInvoiceNumber()))); - processed = processed.replace("{{invoiceDate}}", escapeHtml(safeString(data.getInvoiceDate()))); - processed = processed.replace("{{invoiceText}}", escapeHtml(safeString(data.getInvoiceText()))); - - // Replace recipient information - processed = processed.replace("{{senderLine}}", escapeHtml(safeString(data.getSenderLine()))); - processed = processed.replace("{{recipientName}}", escapeHtml(safeString(data.getRecipientName()))); - processed = processed.replace("{{recipientDepartment}}", escapeHtml(safeString(data.getRecipientDepartment()))); - processed = processed.replace("{{recipientStreet}}", escapeHtml(safeString(data.getRecipientStreet()))); - processed = processed.replace("{{recipientCity}}", escapeHtml(safeString(data.getRecipientCity()))); - - // Replace financial information - processed = processed.replace("{{netAmount}}", escapeHtml(safeString(data.getNetAmount()))); - processed = processed.replace("{{vatRate}}", escapeHtml(safeString(data.getVatRate()))); - processed = processed.replace("{{vatAmount}}", escapeHtml(safeString(data.getVatAmount()))); - processed = processed.replace("{{totalAmount}}", escapeHtml(safeString(data.getTotalAmount()))); - - // Replace terms and footer (allow HTML for line breaks) - processed = processed.replace("{{paymentTerms}}", safeString(data.getPaymentTerms())); - processed = processed.replace("{{footerText}}", safeString(data.getFooterText())); - - // Process invoice items - processed = processInvoiceItems(processed, data.getInvoiceItems()); - - return processed; - } - - private String processInvoiceItems(String template, List items) { - StringBuilder itemsHtml = new StringBuilder(); - - if (items != null) { - for (InvoiceItem item : items) { - itemsHtml.append("") - .append("") - .append(escapeHtml(safeString(item.getQuantity()))) - .append("") - .append("") - .append(escapeHtml(safeString(item.getDescription()))) - .append("") - .append("") - .append(escapeHtml(safeString(item.getUnitPrice()))) - .append("") - .append("") - .append(escapeHtml(safeString(item.getTotalPrice()))) - .append("") - .append(""); - } - } - - // Add empty rows to fill the table (up to 12 rows total as in original) - int emptyRowsNeeded = Math.max(0, 12 - (items != null ? items.size() : 0)); - StringBuilder emptyRowsHtml = new StringBuilder(); - for (int i = 0; i < emptyRowsNeeded; i++) { - emptyRowsHtml.append("") - .append(" ") - .append(" ") - .append(" ") - .append(" ") - .append(""); - } - - // Remove template placeholders - String result = template.replace("{{#invoiceItems}}", "") - .replace("{{/invoiceItems}}", "") - .replace("{{#emptyRows}}", "") - .replace("{{/emptyRows}}", ""); - - // Replace the placeholder with actual items - String itemsPlaceholder = "{{invoiceItems}}"; - if (result.contains(itemsPlaceholder)) { - result = result.replace(itemsPlaceholder, itemsHtml.toString() + emptyRowsHtml.toString()); - } - - return result; - } - - private String safeString(String value) { - return value != null ? value : ""; - } - - private String escapeHtml(String value) { - if (value == null) return ""; - return value.replace("&", "&") - .replace("<", "<") - .replace(">", ">") - .replace("\"", """) - .replace("'", "'"); - } - - public InvoiceData createSampleInvoiceData() { - return pdfBoxService.createSampleInvoiceData(); - } -} \ No newline at end of file diff --git a/src/main/resources/templates/invoice-template.html b/src/main/resources/templates/invoice-template.html deleted file mode 100644 index ed336bb..0000000 --- a/src/main/resources/templates/invoice-template.html +++ /dev/null @@ -1,270 +0,0 @@ - - - - - Rechnung {{invoiceNumber}} - - - -
-
-

- {{companyName}} -

-
-

- {{companySubtitle}} -

-
- -
-

{{senderLine}}

-

{{recipientName}}

-

{{recipientDepartment}}

-

{{recipientStreet}}

-

{{recipientCity}}

-
- -
-

{{companyStreet}}

-

{{companyCity}}

-

-

Tel.: {{companyPhone}}

-

{{companyWebsite}}

-
- -
- -

- Fax - eMail -
- {{companyFax}} - {{companyEmail}} -

- -

- Datum
- {{invoiceDate}} -

- -

Rechnung {{invoiceNumber}}

- -

{{invoiceText}}

- - - - - - - - - {{invoiceItems}} - - - - - - - - - - - - - - - - - - -
MengeBezeichnungEinzelpreisGesamt
Nettobetrag{{netAmount}}
+ {{vatRate}}% MwSt.{{vatAmount}}
Endbetrag{{totalAmount}}
- -

{{paymentTerms}}

-
- - -