Erweiterungen
This commit is contained in:
21
pom.xml
21
pom.xml
@@ -104,27 +104,6 @@
|
||||
<version>1.3.30</version>
|
||||
</dependency>
|
||||
|
||||
<!-- iText 7 Core -->
|
||||
<dependency>
|
||||
<groupId>com.itextpdf</groupId>
|
||||
<artifactId>itext7-core</artifactId>
|
||||
<version>7.2.5</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
|
||||
<!-- iText 7 Kernel -->
|
||||
<dependency>
|
||||
<groupId>com.itextpdf</groupId>
|
||||
<artifactId>kernel</artifactId>
|
||||
<version>7.2.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- iText 7 Layout -->
|
||||
<dependency>
|
||||
<groupId>com.itextpdf</groupId>
|
||||
<artifactId>layout</artifactId>
|
||||
<version>7.2.5</version>
|
||||
</dependency>
|
||||
<!-- Spring Boot Mail Starter -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Invoice> invoiceGrid;
|
||||
|
||||
public InvoicesView() {
|
||||
|
||||
@@ -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 PdfBoxService pdfBoxService;
|
||||
|
||||
private final PdfService pdfService;
|
||||
|
||||
public PdfTestView(PdfService pdfService) {
|
||||
this.pdfService = pdfService;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<InvoiceItem> items) {
|
||||
StringBuilder itemsHtml = new StringBuilder();
|
||||
|
||||
if (items != null) {
|
||||
for (InvoiceItem item : items) {
|
||||
itemsHtml.append("<tr>")
|
||||
.append("<td class=\"table-cell table-cell-center\" style=\"width:2cm;\">")
|
||||
.append(escapeHtml(safeString(item.getQuantity())))
|
||||
.append("</td>")
|
||||
.append("<td class=\"table-cell\" style=\"width:9.999cm;\">")
|
||||
.append(escapeHtml(safeString(item.getDescription())))
|
||||
.append("</td>")
|
||||
.append("<td class=\"table-cell table-cell-right\" style=\"width:3cm;\">")
|
||||
.append(escapeHtml(safeString(item.getUnitPrice())))
|
||||
.append("</td>")
|
||||
.append("<td class=\"table-cell table-cell-right\" style=\"width:3cm;\">")
|
||||
.append(escapeHtml(safeString(item.getTotalPrice())))
|
||||
.append("</td>")
|
||||
.append("</tr>");
|
||||
}
|
||||
}
|
||||
|
||||
// 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("<tr>")
|
||||
.append("<td class=\"table-cell\" style=\"width:2cm;\"> </td>")
|
||||
.append("<td class=\"table-cell\" style=\"width:9.999cm;\"> </td>")
|
||||
.append("<td class=\"table-cell\" style=\"width:3cm;\"> </td>")
|
||||
.append("<td class=\"table-cell\" style=\"width:3cm;\"> </td>")
|
||||
.append("</tr>");
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta content="application/xhtml+xml; charset=utf-8" http-equiv="Content-Type"/>
|
||||
<title>Rechnung {{invoiceNumber}}</title>
|
||||
<style type="text/css">
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 1.5cm 1cm 1.5cm 2cm;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 12pt;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 2px solid #1b12b9;
|
||||
max-width: 21.001cm;
|
||||
background-color: transparent;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.header {
|
||||
position: absolute;
|
||||
top: 1.5cm;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.company-name {
|
||||
font-family: Verdana;
|
||||
font-size: 24pt;
|
||||
color: #1b12b9;
|
||||
text-align: center;
|
||||
margin-left: 14cm;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.company-subtitle {
|
||||
font-family: Verdana;
|
||||
font-size: 9pt;
|
||||
color: #1b12b9;
|
||||
text-align: center;
|
||||
margin-left: 14cm;
|
||||
margin-top: 0.2cm;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
border-top: 2px solid #1b12b9;
|
||||
width: 27cm;
|
||||
position: absolute;
|
||||
margin-top: 0cm;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.recipient-box {
|
||||
height: 4.5cm;
|
||||
width: 8.999cm;
|
||||
padding: 0;
|
||||
float: left;
|
||||
left: 0cm;
|
||||
margin-top: 5.5cm;
|
||||
}
|
||||
|
||||
.sender-line {
|
||||
font-size: 8pt;
|
||||
line-height: 100%;
|
||||
margin-bottom: 0.106cm;
|
||||
margin-top: 0cm;
|
||||
font-family: Arial;
|
||||
}
|
||||
|
||||
.recipient-content {
|
||||
font-size: 12pt;
|
||||
line-height: 100%;
|
||||
margin-bottom: 0cm;
|
||||
margin-top: 0cm;
|
||||
font-family: Arial;
|
||||
}
|
||||
|
||||
.company-address {
|
||||
width: 5.701cm;
|
||||
padding: 0;
|
||||
float: right;
|
||||
margin-left: 14.25cm;
|
||||
text-align: left;
|
||||
top: 4cm;
|
||||
min-height: 3cm;
|
||||
font-size: 10pt;
|
||||
font-family: Arial;
|
||||
line-height: 100%;
|
||||
}
|
||||
|
||||
.contact-info {
|
||||
position: relative;
|
||||
left: 110px;
|
||||
margin-top: -270px;
|
||||
font-size: 8pt;
|
||||
line-height: 120%;
|
||||
font-family: Arial;
|
||||
}
|
||||
|
||||
.invoice-date {
|
||||
float: right;
|
||||
margin-right: 150px;
|
||||
font-size: 8pt;
|
||||
line-height: 120%;
|
||||
font-family: Arial;
|
||||
}
|
||||
|
||||
.subject {
|
||||
font-size: 12pt;
|
||||
line-height: 120%;
|
||||
margin-bottom: 0.64cm;
|
||||
margin-top: 100px;
|
||||
font-family: Arial;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.text-body {
|
||||
font-size: 12pt;
|
||||
font-family: Arial;
|
||||
margin-top: 0cm;
|
||||
margin-bottom: 0.212cm;
|
||||
line-height: 120%;
|
||||
}
|
||||
|
||||
.invoice-table {
|
||||
width: 18cm;
|
||||
margin-top: 0cm;
|
||||
margin-bottom: 0.635cm;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.table-header {
|
||||
background-color: #e6e6e6;
|
||||
padding: 0.097cm;
|
||||
border-left: 0.018cm solid #808080;
|
||||
font-size: 8pt;
|
||||
font-family: Arial;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.table-cell {
|
||||
padding: 0.097cm;
|
||||
border-left: 0.018cm solid #808080;
|
||||
vertical-align: top;
|
||||
font-size: 12pt;
|
||||
font-family: Arial;
|
||||
}
|
||||
|
||||
.table-cell-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.table-cell-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.summary-row {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
.page-content {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
text-align: center;
|
||||
font-size: 8pt;
|
||||
font-family: Arial;
|
||||
margin-top: auto;
|
||||
padding: 0.5cm 1cm;
|
||||
}
|
||||
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page-content">
|
||||
<div class="header">
|
||||
<p class="company-name">
|
||||
<b>{{companyName}}</b>
|
||||
</p>
|
||||
<hr>
|
||||
<p class="company-subtitle">
|
||||
<b>{{companySubtitle}}</b>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="recipient-box">
|
||||
<p class="sender-line">{{senderLine}}</p>
|
||||
<p class="recipient-content">{{recipientName}}</p>
|
||||
<p class="recipient-content">{{recipientDepartment}}</p>
|
||||
<p class="recipient-content">{{recipientStreet}}</p>
|
||||
<p class="recipient-content">{{recipientCity}}</p>
|
||||
</div>
|
||||
|
||||
<div class="company-address">
|
||||
<p>{{companyStreet}}</p>
|
||||
<p>{{companyCity}}</p>
|
||||
<p> </p>
|
||||
<p>Tel.: {{companyPhone}}</p>
|
||||
<p>{{companyWebsite}}</p>
|
||||
</div>
|
||||
|
||||
<div class="clear"></div>
|
||||
|
||||
<p class="contact-info">
|
||||
Fax
|
||||
<span style="margin-left: 60px">eMail</span>
|
||||
<br/>
|
||||
{{companyFax}}
|
||||
<span>{{companyEmail}}</span>
|
||||
</p>
|
||||
|
||||
<p class="invoice-date">
|
||||
<span>Datum<br></span>
|
||||
<span>{{invoiceDate}}</span>
|
||||
</p>
|
||||
|
||||
<p class="subject">Rechnung {{invoiceNumber}}</p>
|
||||
|
||||
<p class="text-body">{{invoiceText}}</p>
|
||||
|
||||
<table class="invoice-table">
|
||||
<tr>
|
||||
<td class="table-header" style="width:2cm;">Menge</td>
|
||||
<td class="table-header" style="width:9.999cm;">Bezeichnung</td>
|
||||
<td class="table-header" style="width:3cm;">Einzelpreis</td>
|
||||
<td class="table-header" style="width:3cm;">Gesamt</td>
|
||||
</tr>
|
||||
{{invoiceItems}}
|
||||
<tr>
|
||||
<td class="table-cell" style="width:2cm;"> </td>
|
||||
<td class="table-cell" style="width:9.999cm;"> </td>
|
||||
<td class="table-header" style="width:3cm;">Nettobetrag</td>
|
||||
<td class="table-cell summary-row table-cell-right" style="width:3cm;">{{netAmount}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="table-cell" style="width:2cm;"> </td>
|
||||
<td class="table-cell" style="width:9.999cm;"> </td>
|
||||
<td class="table-cell table-cell-right" style="width:3cm;">+ {{vatRate}}% MwSt.</td>
|
||||
<td class="table-cell table-cell-right" style="width:3cm;">{{vatAmount}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="table-cell" style="width:2cm;"> </td>
|
||||
<td class="table-cell" style="width:9.999cm;"> </td>
|
||||
<td class="table-header" style="width:3cm;">Endbetrag</td>
|
||||
<td class="table-cell summary-row table-cell-right" style="width:3cm;">{{totalAmount}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p class="text-body">{{paymentTerms}}</p>
|
||||
</div>
|
||||
<p class="footer-text">
|
||||
<span>{{footerText}}</span>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user