Erweiterungen
This commit is contained in:
21
pom.xml
21
pom.xml
@@ -104,27 +104,6 @@
|
|||||||
<version>1.3.30</version>
|
<version>1.3.30</version>
|
||||||
</dependency>
|
</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 -->
|
<!-- Spring Boot Mail Starter -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<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
|
// Einfache Text-Vorschau generieren (PDF-Funktionalität entfernt)
|
||||||
// werden)
|
|
||||||
private byte[] generatePreviewPdf() {
|
private byte[] generatePreviewPdf() {
|
||||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
StringBuilder content = new StringBuilder();
|
||||||
com.lowagie.text.Document document = new com.lowagie.text.Document();
|
content.append("RECHNUNGSVORSCHAU\n\n");
|
||||||
com.lowagie.text.pdf.PdfWriter.getInstance(document, out);
|
content.append("Rechnungs Präfix: ").append(safe(prefixField)).append("\n");
|
||||||
document.open();
|
content.append("USt-IdNr.: ").append(safe(ustIdField)).append("\n");
|
||||||
document.add(new com.lowagie.text.Paragraph("Rechnungsvorschau"));
|
content.append("Steuernummer: ").append(safe(taxNumberField)).append("\n");
|
||||||
document.add(new com.lowagie.text.Paragraph(" "));
|
content.append("Bankname: ").append(safe(bankNameField)).append("\n");
|
||||||
document.add(new com.lowagie.text.Paragraph("Rechnungs Präfix: " + safe(prefixField)));
|
content.append("IBAN: ").append(safe(ibanField)).append("\n");
|
||||||
document.add(new com.lowagie.text.Paragraph("USt-IdNr.: " + safe(ustIdField)));
|
content.append("Steuersatz: ").append(safe(taxRateField)).append("\n\n");
|
||||||
document.add(new com.lowagie.text.Paragraph("Steuernummer: " + safe(taxNumberField)));
|
content.append("Einleitungstext:\n").append(safe(introTextArea)).append("\n\n");
|
||||||
document.add(new com.lowagie.text.Paragraph("Bankname: " + safe(bankNameField)));
|
content.append("Zahlungsbedingungen:\n").append(safe(termsTextArea)).append("\n");
|
||||||
document.add(new com.lowagie.text.Paragraph("IBAN: " + safe(ibanField)));
|
|
||||||
document.add(new com.lowagie.text.Paragraph("Steuersatz: " + safe(taxRateField)));
|
return content.toString().getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility: safe getter für TextField/TextArea
|
// 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.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.Invoice;
|
||||||
|
import de.assecutor.votianlt.service.PdfBoxService;
|
||||||
import jakarta.annotation.security.RolesAllowed;
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
@@ -18,12 +19,14 @@ 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)
|
||||||
@RolesAllowed({ "USER", "ADMIN" })
|
@RolesAllowed({ "USER", "ADMIN" })
|
||||||
public class InvoicesView extends VerticalLayout {
|
public class InvoicesView extends VerticalLayout {
|
||||||
|
|
||||||
|
|
||||||
private final Grid<Invoice> invoiceGrid;
|
private final Grid<Invoice> invoiceGrid;
|
||||||
|
|
||||||
public InvoicesView() {
|
public InvoicesView() {
|
||||||
|
|||||||
@@ -9,21 +9,19 @@ 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.InvoiceData;
|
||||||
import de.assecutor.votianlt.pages.base.ui.view.AdminLayout;
|
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 jakarta.annotation.security.RolesAllowed;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
@Route(value = "pdf-test", layout = AdminLayout.class)
|
@Route(value = "pdf-test", layout = AdminLayout.class)
|
||||||
@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 PdfService pdfService;
|
public PdfTestView(PdfBoxService pdfBoxService) {
|
||||||
|
this.pdfBoxService = pdfBoxService;
|
||||||
public PdfTestView(PdfService pdfService) {
|
|
||||||
this.pdfService = pdfService;
|
|
||||||
|
|
||||||
setSpacing(false);
|
setSpacing(false);
|
||||||
setPadding(false);
|
setPadding(false);
|
||||||
@@ -36,16 +34,13 @@ public class PdfTestView extends VerticalLayout {
|
|||||||
Button generatePdfButton = new Button("Test-Rechnung PDF generieren");
|
Button generatePdfButton = new Button("Test-Rechnung PDF generieren");
|
||||||
generatePdfButton.addClickListener(e -> generateTestPdf());
|
generatePdfButton.addClickListener(e -> generateTestPdf());
|
||||||
|
|
||||||
Button generateHtmlButton = new Button("HTML-Vorschau generieren");
|
add(generatePdfButton);
|
||||||
generateHtmlButton.addClickListener(e -> generateTestHtml());
|
|
||||||
|
|
||||||
add(generatePdfButton, generateHtmlButton);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateTestPdf() {
|
private void generateTestPdf() {
|
||||||
try {
|
try {
|
||||||
InvoiceData sampleData = pdfService.createSampleInvoiceData();
|
InvoiceData sampleData = pdfBoxService.createSampleInvoiceData();
|
||||||
byte[] pdfBytes = pdfService.generateInvoicePdf(sampleData);
|
byte[] pdfBytes = pdfBoxService.generateInvoicePdf(sampleData);
|
||||||
|
|
||||||
StreamResource resource = new StreamResource("test-rechnung.pdf",
|
StreamResource resource = new StreamResource("test-rechnung.pdf",
|
||||||
() -> new ByteArrayInputStream(pdfBytes));
|
() -> 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