From 431c99143bc406524501756cb87799705d0fba86 Mon Sep 17 00:00:00 2001 From: Sven Carstensen Date: Sat, 20 Sep 2025 12:36:14 +0200 Subject: [PATCH] Erweiterungen --- .claude/settings.local.json | 4 +- customer-invoice-test.pdf | Bin 0 -> 4166 bytes .../model/invoices/CustomerInvoice.java | 320 +++++++++- .../model/invoices/CustomerInvoiceData.java | 344 +++++++++-- .../model/invoices/CustomerInvoiceItem.java | 98 ++- .../service/CustomerInvoiceService.java | 211 +++++-- .../resources/templates/customer_invoice.html | 560 ++++++++++++++---- 7 files changed, 1270 insertions(+), 267 deletions(-) create mode 100644 customer-invoice-test.pdf diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 4d991e7..74b6784 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -5,7 +5,9 @@ "mcp__ide__getDiagnostics", "Bash(find:*)", "Bash(./mvnw:*)", - "Bash(rm:*)" + "Bash(rm:*)", + "Bash(lsof:*)", + "Bash(xargs kill:*)" ], "deny": [], "ask": [] diff --git a/customer-invoice-test.pdf b/customer-invoice-test.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f9ddc64888042458a7d868b4990469a5e16852f2 GIT binary patch literal 4166 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 U4{`a6g(T`)B0^i+%)uP-PpA}`-T(jq literal 0 HcmV?d00001 diff --git a/src/main/java/de/assecutor/votianlt/model/invoices/CustomerInvoice.java b/src/main/java/de/assecutor/votianlt/model/invoices/CustomerInvoice.java index cedbb1b..9f3c146 100644 --- a/src/main/java/de/assecutor/votianlt/model/invoices/CustomerInvoice.java +++ b/src/main/java/de/assecutor/votianlt/model/invoices/CustomerInvoice.java @@ -2,27 +2,71 @@ package de.assecutor.votianlt.model.invoices; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; +import java.time.LocalDate; +import java.math.BigDecimal; +import java.util.List; @Document(collection = "customerInvoices") public class CustomerInvoice { @Id private String id; - - private String invoiceNumber; - private String date; - private String text; - - // Add other fields as needed - + + // Pflichtangaben nach §14 UStG (German VAT law) + private String invoiceNumber; // Fortlaufende Rechnungsnummer + private LocalDate invoiceDate; // Rechnungsdatum + private LocalDate deliveryDate; // Leistungsdatum + + // Rechnungssteller (Sender) + private String senderName; + private String senderAddress; + private String senderPostcode; + private String senderCity; + private String senderCountry; + private String senderTaxNumber; // Steuernummer + private String senderVatId; // USt-IdNr. + private String senderPhone; + private String senderEmail; + private String senderWebsite; + + // Rechnungsempfänger (Recipient) + private String recipientName; + private String recipientCompany; + private String recipientAddress; + private String recipientPostcode; + private String recipientCity; + private String recipientCountry; + private String recipientVatId; // USt-IdNr. des Empfängers (falls vorhanden) + + // Rechnungsdetails + private String description; // Beschreibung der Leistung + private List items; + + // Beträge + private BigDecimal netAmount; // Nettobetrag + private BigDecimal vatRate; // Steuersatz (z.B. 19% = 0.19) + private BigDecimal vatAmount; // Steuerbetrag + private BigDecimal totalAmount; // Bruttobetrag + + // Zahlungsdetails + private String paymentTerms; // Zahlungsbedingungen + private LocalDate paymentDueDate; // Fälligkeitsdatum + private String bankAccount; // Bankverbindung + private String iban; + private String bic; + + // Zusätzliche rechtliche Angaben + private String legalNotes; // Rechtliche Hinweise + private String reverseChargeNote; // Hinweis auf Reverse Charge (falls zutreffend) + // Constructors public CustomerInvoice() { } - public CustomerInvoice(String invoiceNumber, String date, String text) { + public CustomerInvoice(String invoiceNumber, LocalDate invoiceDate, String description) { this.invoiceNumber = invoiceNumber; - this.date = date; - this.text = text; + this.invoiceDate = invoiceDate; + this.description = description; } // Getters and Setters @@ -42,19 +86,259 @@ public class CustomerInvoice { this.invoiceNumber = invoiceNumber; } - public String getDate() { - return date; + public LocalDate getInvoiceDate() { + return invoiceDate; } - public void setDate(String date) { - this.date = date; + public void setInvoiceDate(LocalDate invoiceDate) { + this.invoiceDate = invoiceDate; } - public String getText() { - return text; + public LocalDate getDeliveryDate() { + return deliveryDate; } - public void setText(String text) { - this.text = text; + public void setDeliveryDate(LocalDate deliveryDate) { + this.deliveryDate = deliveryDate; + } + + public String getSenderName() { + return senderName; + } + + public void setSenderName(String senderName) { + this.senderName = senderName; + } + + public String getSenderAddress() { + return senderAddress; + } + + public void setSenderAddress(String senderAddress) { + this.senderAddress = senderAddress; + } + + public String getSenderPostcode() { + return senderPostcode; + } + + public void setSenderPostcode(String senderPostcode) { + this.senderPostcode = senderPostcode; + } + + public String getSenderCity() { + return senderCity; + } + + public void setSenderCity(String senderCity) { + this.senderCity = senderCity; + } + + public String getSenderCountry() { + return senderCountry; + } + + public void setSenderCountry(String senderCountry) { + this.senderCountry = senderCountry; + } + + public String getSenderTaxNumber() { + return senderTaxNumber; + } + + public void setSenderTaxNumber(String senderTaxNumber) { + this.senderTaxNumber = senderTaxNumber; + } + + public String getSenderVatId() { + return senderVatId; + } + + public void setSenderVatId(String senderVatId) { + this.senderVatId = senderVatId; + } + + public String getSenderPhone() { + return senderPhone; + } + + public void setSenderPhone(String senderPhone) { + this.senderPhone = senderPhone; + } + + public String getSenderEmail() { + return senderEmail; + } + + public void setSenderEmail(String senderEmail) { + this.senderEmail = senderEmail; + } + + public String getSenderWebsite() { + return senderWebsite; + } + + public void setSenderWebsite(String senderWebsite) { + this.senderWebsite = senderWebsite; + } + + public String getRecipientName() { + return recipientName; + } + + public void setRecipientName(String recipientName) { + this.recipientName = recipientName; + } + + public String getRecipientCompany() { + return recipientCompany; + } + + public void setRecipientCompany(String recipientCompany) { + this.recipientCompany = recipientCompany; + } + + public String getRecipientAddress() { + return recipientAddress; + } + + public void setRecipientAddress(String recipientAddress) { + this.recipientAddress = recipientAddress; + } + + public String getRecipientPostcode() { + return recipientPostcode; + } + + public void setRecipientPostcode(String recipientPostcode) { + this.recipientPostcode = recipientPostcode; + } + + public String getRecipientCity() { + return recipientCity; + } + + public void setRecipientCity(String recipientCity) { + this.recipientCity = recipientCity; + } + + public String getRecipientCountry() { + return recipientCountry; + } + + public void setRecipientCountry(String recipientCountry) { + this.recipientCountry = recipientCountry; + } + + public String getRecipientVatId() { + return recipientVatId; + } + + public void setRecipientVatId(String recipientVatId) { + this.recipientVatId = recipientVatId; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } + + public BigDecimal getNetAmount() { + return netAmount; + } + + public void setNetAmount(BigDecimal netAmount) { + this.netAmount = netAmount; + } + + public BigDecimal getVatRate() { + return vatRate; + } + + public void setVatRate(BigDecimal vatRate) { + this.vatRate = vatRate; + } + + public BigDecimal getVatAmount() { + return vatAmount; + } + + public void setVatAmount(BigDecimal vatAmount) { + this.vatAmount = vatAmount; + } + + public BigDecimal getTotalAmount() { + return totalAmount; + } + + public void setTotalAmount(BigDecimal totalAmount) { + this.totalAmount = totalAmount; + } + + public String getPaymentTerms() { + return paymentTerms; + } + + public void setPaymentTerms(String paymentTerms) { + this.paymentTerms = paymentTerms; + } + + public LocalDate getPaymentDueDate() { + return paymentDueDate; + } + + public void setPaymentDueDate(LocalDate paymentDueDate) { + this.paymentDueDate = paymentDueDate; + } + + public String getBankAccount() { + return bankAccount; + } + + public void setBankAccount(String bankAccount) { + this.bankAccount = bankAccount; + } + + public String getIban() { + return iban; + } + + public void setIban(String iban) { + this.iban = iban; + } + + public String getBic() { + return bic; + } + + public void setBic(String bic) { + this.bic = bic; + } + + public String getLegalNotes() { + return legalNotes; + } + + public void setLegalNotes(String legalNotes) { + this.legalNotes = legalNotes; + } + + public String getReverseChargeNote() { + return reverseChargeNote; + } + + public void setReverseChargeNote(String reverseChargeNote) { + this.reverseChargeNote = reverseChargeNote; } } diff --git a/src/main/java/de/assecutor/votianlt/model/invoices/CustomerInvoiceData.java b/src/main/java/de/assecutor/votianlt/model/invoices/CustomerInvoiceData.java index dccbbaf..6861963 100644 --- a/src/main/java/de/assecutor/votianlt/model/invoices/CustomerInvoiceData.java +++ b/src/main/java/de/assecutor/votianlt/model/invoices/CustomerInvoiceData.java @@ -1,37 +1,70 @@ package de.assecutor.votianlt.model.invoices; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.math.BigDecimal; +import java.text.NumberFormat; import java.util.List; +import java.util.Locale; public class CustomerInvoiceData { - + private String invoiceNumber; - private String date; - private String text; - - private String recipientName; - private String recipientDepartment; - private String recipientStreet; - private String recipientCity; - + private LocalDate invoiceDate; + private LocalDate deliveryDate; + private String description; + + // Rechnungssteller private String senderName; private String senderAddress; + private String senderPostcode; + private String senderCity; + private String senderCountry; + private String senderTaxNumber; + private String senderVatId; private String senderPhone; + private String senderEmail; private String senderWebsite; - + + // Rechnungsempfänger + private String recipientName; + private String recipientCompany; + private String recipientAddress; + private String recipientPostcode; + private String recipientCity; + private String recipientCountry; + private String recipientVatId; + private List items; - - private String netAmount; - private String vatAmount; - private String totalAmount; + + private BigDecimal netAmount; + private BigDecimal vatRate; + private BigDecimal vatAmount; + private BigDecimal totalAmount; + + // Zahlungsdetails + private String paymentTerms; + private LocalDate paymentDueDate; + private String bankAccount; + private String iban; + private String bic; + + // Rechtliche Angaben + private String legalNotes; + private String reverseChargeNote; + + // Number formatter for German locale + private static final NumberFormat CURRENCY_FORMAT = NumberFormat.getCurrencyInstance(Locale.GERMANY); + private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd.MM.yyyy"); // Constructors public CustomerInvoiceData() { } - public CustomerInvoiceData(String invoiceNumber, String date, String text) { + public CustomerInvoiceData(String invoiceNumber, LocalDate invoiceDate, String description) { this.invoiceNumber = invoiceNumber; - this.date = date; - this.text = text; + this.invoiceDate = invoiceDate; + this.description = description; } // Getters and Setters @@ -43,52 +76,28 @@ public class CustomerInvoiceData { this.invoiceNumber = invoiceNumber; } - public String getDate() { - return date; + public LocalDate getInvoiceDate() { + return invoiceDate; } - public void setDate(String date) { - this.date = date; + public void setInvoiceDate(LocalDate invoiceDate) { + this.invoiceDate = invoiceDate; } - public String getText() { - return text; + public LocalDate getDeliveryDate() { + return deliveryDate; } - public void setText(String text) { - this.text = text; + public void setDeliveryDate(LocalDate deliveryDate) { + this.deliveryDate = deliveryDate; } - public String getRecipientName() { - return recipientName; + public String getDescription() { + return description; } - public void setRecipientName(String recipientName) { - this.recipientName = recipientName; - } - - public String getRecipientDepartment() { - return recipientDepartment; - } - - public void setRecipientDepartment(String recipientDepartment) { - this.recipientDepartment = recipientDepartment; - } - - public String getRecipientStreet() { - return recipientStreet; - } - - public void setRecipientStreet(String recipientStreet) { - this.recipientStreet = recipientStreet; - } - - public String getRecipientCity() { - return recipientCity; - } - - public void setRecipientCity(String recipientCity) { - this.recipientCity = recipientCity; + public void setDescription(String description) { + this.description = description; } public String getSenderName() { @@ -107,6 +116,46 @@ public class CustomerInvoiceData { this.senderAddress = senderAddress; } + public String getSenderPostcode() { + return senderPostcode; + } + + public void setSenderPostcode(String senderPostcode) { + this.senderPostcode = senderPostcode; + } + + public String getSenderCity() { + return senderCity; + } + + public void setSenderCity(String senderCity) { + this.senderCity = senderCity; + } + + public String getSenderCountry() { + return senderCountry; + } + + public void setSenderCountry(String senderCountry) { + this.senderCountry = senderCountry; + } + + public String getSenderTaxNumber() { + return senderTaxNumber; + } + + public void setSenderTaxNumber(String senderTaxNumber) { + this.senderTaxNumber = senderTaxNumber; + } + + public String getSenderVatId() { + return senderVatId; + } + + public void setSenderVatId(String senderVatId) { + this.senderVatId = senderVatId; + } + public String getSenderPhone() { return senderPhone; } @@ -115,6 +164,14 @@ public class CustomerInvoiceData { this.senderPhone = senderPhone; } + public String getSenderEmail() { + return senderEmail; + } + + public void setSenderEmail(String senderEmail) { + this.senderEmail = senderEmail; + } + public String getSenderWebsite() { return senderWebsite; } @@ -123,6 +180,62 @@ public class CustomerInvoiceData { this.senderWebsite = senderWebsite; } + public String getRecipientName() { + return recipientName; + } + + public void setRecipientName(String recipientName) { + this.recipientName = recipientName; + } + + public String getRecipientCompany() { + return recipientCompany; + } + + public void setRecipientCompany(String recipientCompany) { + this.recipientCompany = recipientCompany; + } + + public String getRecipientAddress() { + return recipientAddress; + } + + public void setRecipientAddress(String recipientAddress) { + this.recipientAddress = recipientAddress; + } + + public String getRecipientPostcode() { + return recipientPostcode; + } + + public void setRecipientPostcode(String recipientPostcode) { + this.recipientPostcode = recipientPostcode; + } + + public String getRecipientCity() { + return recipientCity; + } + + public void setRecipientCity(String recipientCity) { + this.recipientCity = recipientCity; + } + + public String getRecipientCountry() { + return recipientCountry; + } + + public void setRecipientCountry(String recipientCountry) { + this.recipientCountry = recipientCountry; + } + + public String getRecipientVatId() { + return recipientVatId; + } + + public void setRecipientVatId(String recipientVatId) { + this.recipientVatId = recipientVatId; + } + public List getItems() { return items; } @@ -131,27 +244,140 @@ public class CustomerInvoiceData { this.items = items; } - public String getNetAmount() { + public BigDecimal getNetAmount() { return netAmount; } - public void setNetAmount(String netAmount) { + public void setNetAmount(BigDecimal netAmount) { this.netAmount = netAmount; } - public String getVatAmount() { + public BigDecimal getVatRate() { + return vatRate; + } + + public void setVatRate(BigDecimal vatRate) { + this.vatRate = vatRate; + } + + public BigDecimal getVatAmount() { return vatAmount; } - public void setVatAmount(String vatAmount) { + public void setVatAmount(BigDecimal vatAmount) { this.vatAmount = vatAmount; } - public String getTotalAmount() { + public BigDecimal getTotalAmount() { return totalAmount; } - public void setTotalAmount(String totalAmount) { + public void setTotalAmount(BigDecimal totalAmount) { this.totalAmount = totalAmount; } + + public String getPaymentTerms() { + return paymentTerms; + } + + public void setPaymentTerms(String paymentTerms) { + this.paymentTerms = paymentTerms; + } + + public LocalDate getPaymentDueDate() { + return paymentDueDate; + } + + public void setPaymentDueDate(LocalDate paymentDueDate) { + this.paymentDueDate = paymentDueDate; + } + + public String getBankAccount() { + return bankAccount; + } + + public void setBankAccount(String bankAccount) { + this.bankAccount = bankAccount; + } + + public String getIban() { + return iban; + } + + public void setIban(String iban) { + this.iban = iban; + } + + public String getBic() { + return bic; + } + + public void setBic(String bic) { + this.bic = bic; + } + + public String getLegalNotes() { + return legalNotes; + } + + public void setLegalNotes(String legalNotes) { + this.legalNotes = legalNotes; + } + + public String getReverseChargeNote() { + return reverseChargeNote; + } + + public void setReverseChargeNote(String reverseChargeNote) { + this.reverseChargeNote = reverseChargeNote; + } + + // Formatting methods for PDF generation + public String getFormattedInvoiceDate() { + return invoiceDate != null ? invoiceDate.format(DATE_FORMAT) : ""; + } + + public String getFormattedDeliveryDate() { + return deliveryDate != null ? deliveryDate.format(DATE_FORMAT) : ""; + } + + public String getFormattedPaymentDueDate() { + return paymentDueDate != null ? paymentDueDate.format(DATE_FORMAT) : ""; + } + + public String getFormattedNetAmount() { + return netAmount != null ? CURRENCY_FORMAT.format(netAmount) : "0,00 €"; + } + + public String getFormattedVatAmount() { + return vatAmount != null ? CURRENCY_FORMAT.format(vatAmount) : "0,00 €"; + } + + public String getFormattedTotalAmount() { + return totalAmount != null ? CURRENCY_FORMAT.format(totalAmount) : "0,00 €"; + } + + public String getFormattedVatRate() { + if (vatRate != null) { + return String.format("%.0f%%", vatRate.multiply(new BigDecimal("100"))); + } + return "19%"; + } + + // Legacy methods for backward compatibility + public String getDate() { + return getFormattedInvoiceDate(); + } + + public String getText() { + return description; + } + + public String getRecipientDepartment() { + return recipientCompany; + } + + public String getRecipientStreet() { + return recipientAddress; + } } diff --git a/src/main/java/de/assecutor/votianlt/model/invoices/CustomerInvoiceItem.java b/src/main/java/de/assecutor/votianlt/model/invoices/CustomerInvoiceItem.java index 903d8d9..76a2634 100644 --- a/src/main/java/de/assecutor/votianlt/model/invoices/CustomerInvoiceItem.java +++ b/src/main/java/de/assecutor/votianlt/model/invoices/CustomerInvoiceItem.java @@ -1,30 +1,57 @@ package de.assecutor.votianlt.model.invoices; +import java.math.BigDecimal; + public class CustomerInvoiceItem { - - private String quantity; + + private BigDecimal quantity; + private String unit; // Einheit (Stk., h, kg, etc.) private String description; - private String price; - private String total; + private BigDecimal unitPrice; // Einzelpreis netto + private BigDecimal netTotal; // Gesamtpreis netto + private BigDecimal vatRate; // Steuersatz + private BigDecimal vatAmount; // Steuerbetrag + private BigDecimal grossTotal; // Gesamtpreis brutto // Constructors public CustomerInvoiceItem() { } - public CustomerInvoiceItem(String quantity, String description, String unitPrice, String total) { + public CustomerInvoiceItem(BigDecimal quantity, String unit, String description, BigDecimal unitPrice, BigDecimal vatRate) { this.quantity = quantity; + this.unit = unit; this.description = description; - this.price = unitPrice; - this.total = total; + this.unitPrice = unitPrice; + this.vatRate = vatRate; + calculateTotals(); + } + + private void calculateTotals() { + if (quantity != null && unitPrice != null) { + this.netTotal = quantity.multiply(unitPrice); + if (vatRate != null) { + this.vatAmount = netTotal.multiply(vatRate); + this.grossTotal = netTotal.add(vatAmount); + } + } } // Getters and Setters - public String getQuantity() { + public BigDecimal getQuantity() { return quantity; } - public void setQuantity(String quantity) { + public void setQuantity(BigDecimal quantity) { this.quantity = quantity; + calculateTotals(); + } + + public String getUnit() { + return unit; + } + + public void setUnit(String unit) { + this.unit = unit; } public String getDescription() { @@ -35,19 +62,54 @@ public class CustomerInvoiceItem { this.description = description; } - public String getPrice() { - return price; + public BigDecimal getUnitPrice() { + return unitPrice; } - public void setPrice(String price) { - this.price = price; + public void setUnitPrice(BigDecimal unitPrice) { + this.unitPrice = unitPrice; + calculateTotals(); + } + + public BigDecimal getNetTotal() { + return netTotal; + } + + public void setNetTotal(BigDecimal netTotal) { + this.netTotal = netTotal; + } + + public BigDecimal getVatRate() { + return vatRate; + } + + public void setVatRate(BigDecimal vatRate) { + this.vatRate = vatRate; + calculateTotals(); + } + + public BigDecimal getVatAmount() { + return vatAmount; + } + + public void setVatAmount(BigDecimal vatAmount) { + this.vatAmount = vatAmount; + } + + public BigDecimal getGrossTotal() { + return grossTotal; + } + + public void setGrossTotal(BigDecimal grossTotal) { + this.grossTotal = grossTotal; + } + + // Legacy methods for backward compatibility + public String getPrice() { + return unitPrice != null ? unitPrice.toString() : "0.00"; } public String getTotal() { - return total; - } - - public void setTotal(String total) { - this.total = total; + return grossTotal != null ? grossTotal.toString() : "0.00"; } } diff --git a/src/main/java/de/assecutor/votianlt/service/CustomerInvoiceService.java b/src/main/java/de/assecutor/votianlt/service/CustomerInvoiceService.java index 71fba8b..ba3a95c 100644 --- a/src/main/java/de/assecutor/votianlt/service/CustomerInvoiceService.java +++ b/src/main/java/de/assecutor/votianlt/service/CustomerInvoiceService.java @@ -12,56 +12,118 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; +import java.time.LocalDate; +import java.math.BigDecimal; +import java.text.NumberFormat; +import java.util.Locale; @Service public class CustomerInvoiceService { public CustomerInvoice createCustomerInvoice(String customerId, String jobId) { - // This is a placeholder implementation - // In a real application, this would fetch customer and job data and create an invoice - CustomerInvoice invoice = new CustomerInvoice(); - //invoice.setCustomerId(customerId); - //invoice.setJobId(jobId); - + + // Set basic invoice details + invoice.setInvoiceNumber("INV-" + System.currentTimeMillis()); + invoice.setInvoiceDate(LocalDate.now()); + invoice.setDeliveryDate(LocalDate.now()); + invoice.setDescription("Logistikdienstleistungen für Auftrag " + jobId); + + // Set sender details (your company) + invoice.setSenderName("Assecutor GmbH"); + invoice.setSenderAddress("Hauptstraße 456"); + invoice.setSenderPostcode("12345"); + invoice.setSenderCity("Berlin"); + invoice.setSenderCountry("Deutschland"); + invoice.setSenderTaxNumber("12/345/67890"); + invoice.setSenderVatId("DE123456789"); + invoice.setSenderPhone("+49 30 12345678"); + invoice.setSenderEmail("info@assecutor.de"); + invoice.setSenderWebsite("https://www.assecutor.de"); + + // Payment terms + invoice.setPaymentTerms("Zahlbar innerhalb von 14 Tagen netto ohne Abzug."); + invoice.setPaymentDueDate(LocalDate.now().plusDays(14)); + invoice.setIban("DE89 3704 0044 0532 0130 00"); + invoice.setBic("COBADEFFXXX"); + + // Legal information + invoice.setLegalNotes("Geschäftsführer: Max Mustermann | Registergericht: Berlin | HRB 123456"); + return invoice; } public CustomerInvoiceData createCustomerInvoiceData(String customerId, String jobId) { - // This is a placeholder implementation - // In a real application, this would fetch customer and job data and create invoice data - CustomerInvoiceData invoiceData = new CustomerInvoiceData(); + + // Rechnungsdetails invoiceData.setInvoiceNumber("INV-" + System.currentTimeMillis()); - invoiceData.setDate("2025-09-20"); - invoiceData.setText("Rechnung für Auftrag " + jobId); - - // Set recipient details - invoiceData.setRecipientName("Kunde Name"); - invoiceData.setRecipientDepartment("Abt. XYZ"); - invoiceData.setRecipientStreet("Musterstraße 123"); - invoiceData.setRecipientCity("12345 Musterstadt"); - - // Set sender details - Testdaten für den Rechnungssteller + invoiceData.setInvoiceDate(LocalDate.now()); + invoiceData.setDeliveryDate(LocalDate.now()); + invoiceData.setDescription("Logistikdienstleistungen für Auftrag " + jobId); + + // Rechnungssteller (vollständige Angaben nach deutschem Recht) invoiceData.setSenderName("Assecutor GmbH"); invoiceData.setSenderAddress("Hauptstraße 456"); - invoiceData.setSenderPhone("+49 123 4567890"); + invoiceData.setSenderPostcode("12345"); + invoiceData.setSenderCity("Berlin"); + invoiceData.setSenderCountry("Deutschland"); + invoiceData.setSenderTaxNumber("12/345/67890"); + invoiceData.setSenderVatId("DE123456789"); + invoiceData.setSenderPhone("+49 30 12345678"); + invoiceData.setSenderEmail("info@assecutor.de"); invoiceData.setSenderWebsite("https://www.assecutor.de"); - - // Create sample items + + // Rechnungsempfänger + invoiceData.setRecipientName("Max Mustermann"); + invoiceData.setRecipientCompany("Musterfirma GmbH"); + invoiceData.setRecipientAddress("Musterstraße 123"); + invoiceData.setRecipientPostcode("54321"); + invoiceData.setRecipientCity("Hamburg"); + invoiceData.setRecipientCountry("Deutschland"); + invoiceData.setRecipientVatId("DE987654321"); + + // Rechnungsposten List items = new ArrayList<>(); - CustomerInvoiceItem item1 = new CustomerInvoiceItem("1", "Dienstleistung XYZ", "100,00 €", "100,00 €"); - CustomerInvoiceItem item2 = new CustomerInvoiceItem("2", "Material ABC", "50,00 €", "100,00 €"); + BigDecimal vatRate = new BigDecimal("0.19"); // 19% MwSt. + + CustomerInvoiceItem item1 = new CustomerInvoiceItem( + new BigDecimal("2"), "Std.", "Transportdienstleistung", + new BigDecimal("85.00"), vatRate); + CustomerInvoiceItem item2 = new CustomerInvoiceItem( + new BigDecimal("1"), "Stk.", "Logistikkoordination", + new BigDecimal("120.00"), vatRate); + CustomerInvoiceItem item3 = new CustomerInvoiceItem( + new BigDecimal("50"), "km", "Kilometergebühr", + new BigDecimal("0.60"), vatRate); + items.add(item1); items.add(item2); - + items.add(item3); invoiceData.setItems(items); - - // Set amounts - invoiceData.setNetAmount("200,00 €"); - invoiceData.setVatAmount("36,00 €"); - invoiceData.setTotalAmount("236,00 €"); - + + // Beträge berechnen + BigDecimal netAmount = items.stream() + .map(CustomerInvoiceItem::getNetTotal) + .reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal vatAmount = netAmount.multiply(vatRate); + BigDecimal totalAmount = netAmount.add(vatAmount); + + invoiceData.setNetAmount(netAmount); + invoiceData.setVatRate(vatRate); + invoiceData.setVatAmount(vatAmount); + invoiceData.setTotalAmount(totalAmount); + + // Zahlungsdetails + invoiceData.setPaymentTerms("Zahlbar innerhalb von 14 Tagen netto ohne Abzug."); + invoiceData.setPaymentDueDate(LocalDate.now().plusDays(14)); + invoiceData.setBankAccount("Assecutor GmbH"); + invoiceData.setIban("DE89 3704 0044 0532 0130 00"); + invoiceData.setBic("COBADEFFXXX"); + + // Rechtliche Hinweise + invoiceData.setLegalNotes("Geschäftsführer: Max Mustermann | Registergericht: Berlin | HRB 123456"); + return invoiceData; } @@ -84,48 +146,91 @@ public class CustomerInvoiceService { } private String fillCustomerInvoiceHtmlWithInvoiceData(String html, CustomerInvoiceData data) { - // Replace placeholders in HTML with actual invoice data String filledHtml = html; + NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(Locale.GERMANY); - // Replace invoice data placeholders - filledHtml = filledHtml.replace("${invoiceData.invoiceNumber}", data.getInvoiceNumber() != null ? data.getInvoiceNumber() : ""); - filledHtml = filledHtml.replace("${invoiceData.date}", data.getDate() != null ? data.getDate() : ""); - filledHtml = filledHtml.replace("${invoiceData.text}", data.getText() != null ? data.getText() : ""); + // Replace invoice header data + filledHtml = filledHtml.replace("${invoiceData.invoiceNumber}", nvl(data.getInvoiceNumber())); + filledHtml = filledHtml.replace("${invoiceData.invoiceDate}", nvl(data.getFormattedInvoiceDate())); + filledHtml = filledHtml.replace("${invoiceData.deliveryDate}", nvl(data.getFormattedDeliveryDate())); + filledHtml = filledHtml.replace("${invoiceData.description}", nvl(data.getDescription())); - // Replace recipient address - filledHtml = filledHtml.replace("${invoiceData.recipientName}", data.getRecipientName() != null ? data.getRecipientName() : ""); - filledHtml = filledHtml.replace("${invoiceData.recipientDepartment}", data.getRecipientDepartment() != null ? data.getRecipientDepartment() : ""); - filledHtml = filledHtml.replace("${invoiceData.recipientStreet}", data.getRecipientStreet() != null ? data.getRecipientStreet() : ""); - filledHtml = filledHtml.replace("${invoiceData.recipientCity}", data.getRecipientCity() != null ? data.getRecipientCity() : ""); + // Replace sender details (complete legal information) + filledHtml = filledHtml.replace("${invoiceData.senderName}", nvl(data.getSenderName())); + filledHtml = filledHtml.replace("${invoiceData.senderAddress}", nvl(data.getSenderAddress())); + filledHtml = filledHtml.replace("${invoiceData.senderPostcode}", nvl(data.getSenderPostcode())); + filledHtml = filledHtml.replace("${invoiceData.senderCity}", nvl(data.getSenderCity())); + filledHtml = filledHtml.replace("${invoiceData.senderCountry}", nvl(data.getSenderCountry())); + filledHtml = filledHtml.replace("${invoiceData.senderTaxNumber}", nvl(data.getSenderTaxNumber())); + filledHtml = filledHtml.replace("${invoiceData.senderVatId}", nvl(data.getSenderVatId())); + filledHtml = filledHtml.replace("${invoiceData.senderPhone}", nvl(data.getSenderPhone())); + filledHtml = filledHtml.replace("${invoiceData.senderEmail}", nvl(data.getSenderEmail())); + filledHtml = filledHtml.replace("${invoiceData.senderWebsite}", nvl(data.getSenderWebsite())); - // Replace sender details - filledHtml = filledHtml.replace("${invoiceData.senderName}", data.getSenderName() != null ? data.getSenderName() : ""); - filledHtml = filledHtml.replace("${invoiceData.senderAddress}", data.getSenderAddress() != null ? data.getSenderAddress() : ""); - filledHtml = filledHtml.replace("${invoiceData.senderPhone}", data.getSenderPhone() != null ? data.getSenderPhone() : ""); - filledHtml = filledHtml.replace("${invoiceData.senderWebsite}", data.getSenderWebsite() != null ? data.getSenderWebsite() : ""); + // Replace recipient details + filledHtml = filledHtml.replace("${invoiceData.recipientName}", nvl(data.getRecipientName())); + filledHtml = filledHtml.replace("${invoiceData.recipientCompany}", nvl(data.getRecipientCompany())); + filledHtml = filledHtml.replace("${invoiceData.recipientAddress}", nvl(data.getRecipientAddress())); + filledHtml = filledHtml.replace("${invoiceData.recipientPostcode}", nvl(data.getRecipientPostcode())); + filledHtml = filledHtml.replace("${invoiceData.recipientCity}", nvl(data.getRecipientCity())); + filledHtml = filledHtml.replace("${invoiceData.recipientCountry}", nvl(data.getRecipientCountry())); + filledHtml = filledHtml.replace("${invoiceData.recipientVatId}", nvl(data.getRecipientVatId())); // Replace invoice items if (data.getItems() != null && !data.getItems().isEmpty()) { StringBuilder itemRows = new StringBuilder(); for (CustomerInvoiceItem item : data.getItems()) { itemRows.append(""); - itemRows.append("").append(item.getQuantity()).append(""); - itemRows.append("").append(item.getDescription()).append(""); - itemRows.append("").append(item.getPrice()).append(""); - itemRows.append("").append(item.getTotal()).append(""); + itemRows.append("") + .append(formatDecimal(item.getQuantity())) + .append(" ").append(nvl(item.getUnit())) + .append(""); + itemRows.append("").append(nvl(item.getDescription())).append(""); + itemRows.append("") + .append(formatCurrency(item.getUnitPrice())) + .append(""); + itemRows.append("") + .append(formatCurrency(item.getNetTotal())) + .append(""); itemRows.append(""); } filledHtml = filledHtml.replace("", itemRows.toString()); } // Replace amounts - filledHtml = filledHtml.replace("${invoiceData.netAmount}", data.getNetAmount() != null ? data.getNetAmount() : ""); - filledHtml = filledHtml.replace("${invoiceData.vatAmount}", data.getVatAmount() != null ? data.getVatAmount() : ""); - filledHtml = filledHtml.replace("${invoiceData.totalAmount}", data.getTotalAmount() != null ? data.getTotalAmount() : ""); + filledHtml = filledHtml.replace("${invoiceData.netAmount}", nvl(data.getFormattedNetAmount())); + filledHtml = filledHtml.replace("${invoiceData.vatRate}", nvl(data.getFormattedVatRate())); + filledHtml = filledHtml.replace("${invoiceData.vatAmount}", nvl(data.getFormattedVatAmount())); + filledHtml = filledHtml.replace("${invoiceData.totalAmount}", nvl(data.getFormattedTotalAmount())); + + // Replace payment details + filledHtml = filledHtml.replace("${invoiceData.paymentTerms}", nvl(data.getPaymentTerms())); + filledHtml = filledHtml.replace("${invoiceData.paymentDueDate}", nvl(data.getFormattedPaymentDueDate())); + filledHtml = filledHtml.replace("${invoiceData.bankAccount}", nvl(data.getBankAccount())); + filledHtml = filledHtml.replace("${invoiceData.iban}", nvl(data.getIban())); + filledHtml = filledHtml.replace("${invoiceData.bic}", nvl(data.getBic())); + + // Replace legal notes + filledHtml = filledHtml.replace("${invoiceData.legalNotes}", nvl(data.getLegalNotes())); + filledHtml = filledHtml.replace("${invoiceData.reverseChargeNote}", nvl(data.getReverseChargeNote())); return filledHtml; } + private String nvl(String value) { + return value != null ? value : ""; + } + + private String formatCurrency(BigDecimal amount) { + if (amount == null) return "0,00 €"; + return NumberFormat.getCurrencyInstance(Locale.GERMANY).format(amount); + } + + private String formatDecimal(BigDecimal value) { + if (value == null) return "0"; + return NumberFormat.getNumberInstance(Locale.GERMANY).format(value); + } + private byte[] generatePdfFromHtmlString(String htmlContent) throws Exception { // Create PDF using iText library ByteArrayOutputStream baos = new ByteArrayOutputStream(); diff --git a/src/main/resources/templates/customer_invoice.html b/src/main/resources/templates/customer_invoice.html index 434f6c1..2d708e1 100644 --- a/src/main/resources/templates/customer_invoice.html +++ b/src/main/resources/templates/customer_invoice.html @@ -1,144 +1,468 @@ - + - Rechnung + + Rechnung ${invoiceData.invoiceNumber} -
-
-
-

${invoiceData.senderName}

-

${invoiceData.senderAddress}

-

${invoiceData.senderCity}

-

Telefon: ${invoiceData.senderPhone}

-

Web: ${invoiceData.senderWebsite}

+
+ +
+
+ +
+ ${invoiceData.senderAddress}
+ ${invoiceData.senderPostcode} ${invoiceData.senderCity}
+ ${invoiceData.senderCountry}
+
+ Tel: ${invoiceData.senderPhone}
+ E-Mail: ${invoiceData.senderEmail}
+ Web: ${invoiceData.senderWebsite}
+
+ Steuernr.: ${invoiceData.senderTaxNumber}
+ USt-IdNr.: ${invoiceData.senderVatId} +
+
+
+
RECHNUNG
+
+ Nr. ${invoiceData.invoiceNumber}
+ vom ${invoiceData.invoiceDate} +
+
+ + +
+
+ ${invoiceData.senderName} · ${invoiceData.senderAddress} · ${invoiceData.senderPostcode} ${invoiceData.senderCity} +
+
+
${invoiceData.recipientCompany}
+
${invoiceData.recipientName}
+
${invoiceData.recipientAddress}
+
${invoiceData.recipientPostcode} ${invoiceData.recipientCity}
+
${invoiceData.recipientCountry}
+
+
+ +
-

Rechnung

-

Rechnungsnummer: INV-123456789

-

Datum: 2025-09-20

+
+
+ Rechnungsdatum: + ${invoiceData.invoiceDate} +
+
+ Leistungsdatum: + ${invoiceData.deliveryDate} +
+
+ Kundennummer: + ${invoiceData.recipientVatId} +
+
+
+
+ Fälligkeitsdatum: + ${invoiceData.paymentDueDate} +
+
+ Zahlungsziel: + 14 Tage netto +
+
+
+ + +
+ Leistungsbeschreibung:
+ ${invoiceData.description} +
+ + +
+ + + + + + + + + + + + +
MengeBeschreibungEinzelpreis
(netto)
Gesamtpreis
(netto)
+
+ + +
+
+ +
+
+ + + + + + + + + + + + + +
Nettobetrag:${invoiceData.netAmount}
zzgl. ${invoiceData.vatRate} MwSt.:${invoiceData.vatAmount}
Rechnungsbetrag:${invoiceData.totalAmount}
+
+
+ + +
+
+ Zahlungsbedingungen:
+ ${invoiceData.paymentTerms} + Fälligkeitsdatum: ${invoiceData.paymentDueDate} +
+ +
+
+ Bankverbindung:
+ ${invoiceData.bankAccount}
+ IBAN: ${invoiceData.iban}
+ BIC: ${invoiceData.bic} +
+
+ Verwendungszweck:
+ Rechnung ${invoiceData.invoiceNumber} +
+
+
+ + +
- -

Rechnung

- -
-

Empfänger:

-

Kunde Name

-

Abt. XYZ

-

Musterstraße 123

-

12345 Musterstadt

-
- - - - - - - - - - - - - - - - - - -
MengeBeschreibungEinzelpreisGesamt
1Dienstleistung XYZ100,00 €100,00 €
- -
- - - - - - - - - - - - - -
Netto:200,00 €
USt (19%):36,00 €
Gesamt:236,00 €
-
- -
-

Für Assecutor GmbH

-
-

Unterschrift

-
-
-
- + \ No newline at end of file