diff --git a/src/main/java/de/assecutor/pdftool/address/Address.java b/src/main/java/de/assecutor/pdftool/address/Address.java index 1b39fc1..b3ba59d 100644 --- a/src/main/java/de/assecutor/pdftool/address/Address.java +++ b/src/main/java/de/assecutor/pdftool/address/Address.java @@ -25,6 +25,7 @@ public class Address { private String vatId; private String email; private String phone; + private String website; // Bankverbindung (v. a. für Rechnungssteller relevant) private String bankName; @@ -119,6 +120,14 @@ public class Address { this.phone = phone; } + public String getWebsite() { + return website; + } + + public void setWebsite(String website) { + this.website = website; + } + public String getBankName() { return bankName; } diff --git a/src/main/java/de/assecutor/pdftool/invoice/InvoiceDraft.java b/src/main/java/de/assecutor/pdftool/invoice/InvoiceDraft.java index 99717e0..6fd3fa2 100644 --- a/src/main/java/de/assecutor/pdftool/invoice/InvoiceDraft.java +++ b/src/main/java/de/assecutor/pdftool/invoice/InvoiceDraft.java @@ -27,6 +27,7 @@ public record InvoiceDraft( String buyerReference, String iban, String bic, + String website, InvoiceMetadata.Party sender, InvoiceMetadata.Party recipient, List items, diff --git a/src/main/java/de/assecutor/pdftool/ui/AddressBookView.java b/src/main/java/de/assecutor/pdftool/ui/AddressBookView.java index 895e179..bef64f8 100644 --- a/src/main/java/de/assecutor/pdftool/ui/AddressBookView.java +++ b/src/main/java/de/assecutor/pdftool/ui/AddressBookView.java @@ -159,6 +159,9 @@ public class AddressBookView extends VerticalLayout { }); TextField phone = new TextField("Telefon"); phone.setValue(target.getPhone() != null ? target.getPhone() : ""); + TextField website = new TextField("Webseite"); + website.setValue(target.getWebsite() != null ? target.getWebsite() : ""); + website.setHelperText("z. B. www.assecutor.de"); // Bankverbindung (optional) TextField bankName = new TextField("Bank"); @@ -170,7 +173,7 @@ public class AddressBookView extends VerticalLayout { bic.setValue(target.getBic() != null ? target.getBic() : ""); FormLayout form = new FormLayout(type, name, street, zip, city, countryCode, vatId, email, phone, - bankName, iban, bic); + website, bankName, iban, bic); form.setResponsiveSteps(new FormLayout.ResponsiveStep("0", 1), new FormLayout.ResponsiveStep("500px", 2)); form.setWidth("560px"); dialog.add(form); @@ -231,6 +234,7 @@ public class AddressBookView extends VerticalLayout { target.setVatId(vatId.getValue().isBlank() ? "" : VatIdValidator.normalize(vatId.getValue())); target.setEmail(email.getValue().trim()); target.setPhone(phone.getValue().trim()); + target.setWebsite(website.getValue().trim()); target.setBankName(bankName.getValue().trim()); target.setIban(iban.getValue().isBlank() ? "" : IbanValidator.normalize(iban.getValue())); target.setBic(bic.getValue().trim().toUpperCase()); diff --git a/src/main/java/de/assecutor/pdftool/ui/MainView.java b/src/main/java/de/assecutor/pdftool/ui/MainView.java index 99219be..41a6aba 100644 --- a/src/main/java/de/assecutor/pdftool/ui/MainView.java +++ b/src/main/java/de/assecutor/pdftool/ui/MainView.java @@ -379,6 +379,7 @@ public class MainView extends VerticalLayout { "", sender.ibanNormalized(), sender.bicNormalized(), + sender.websiteValue(), sender.toParty(), recipient.toParty(), List.copyOf(lineItems), LocalDateTime.now()); @@ -410,6 +411,8 @@ public class MainView extends VerticalLayout { currency.setValue(nullToEmpty(draft.currency())); paymentTerms.setValue(nullToEmpty(draft.paymentTerms())); applyParty(sender, draft.sender()); + sender.prefillContact(draft.sender() != null ? nullToEmpty(draft.sender().phone()) : "", + nullToEmpty(draft.website())); sender.prefillBank(nullToEmpty(draft.iban()), nullToEmpty(draft.bic())); applyParty(recipient, draft.recipient()); lineItems.clear(); @@ -447,6 +450,7 @@ public class MainView extends VerticalLayout { nullToEmpty(address.getZip()), nullToEmpty(address.getCity()), nullToEmpty(address.getCountryCode()), nullToEmpty(address.getVatId()), nullToEmpty(address.getEmail())); + form.prefillContact(nullToEmpty(address.getPhone()), nullToEmpty(address.getWebsite())); form.prefillBank(nullToEmpty(address.getIban()), nullToEmpty(address.getBic())); } }); @@ -545,6 +549,7 @@ public class MainView extends VerticalLayout { "", sender.ibanNormalized(), sender.bicNormalized(), + sender.websiteValue(), sender.toParty(), recipient.toParty(), List.copyOf(lineItems), null); @@ -661,12 +666,20 @@ public class MainView extends VerticalLayout { private Map buildTemplateVariables(InvoiceMetadata metadata) { Map variables = new LinkedHashMap<>(); InvoiceMetadata.Party senderParty = metadata.sender(); + // Telefon und Webseite kommen aus den Formularfeldern des + // Rechnungsstellers; sind sie leer (z. B. alte Vorbelegung), springt + // der Adressbuch-Eintrag des Rechnungsstellers ein. + Optional
senderAddress = findSenderAddress(senderParty.name()); + String phone = !senderParty.phone().isBlank() ? senderParty.phone() + : senderAddress.map(address -> nullToEmpty(address.getPhone())).orElse(""); + String website = !sender.websiteValue().isBlank() ? sender.websiteValue() + : senderAddress.map(address -> nullToEmpty(address.getWebsite())).orElse(""); variables.put("masterdata.company_name", senderParty.name()); variables.put("masterdata.street", senderParty.street()); variables.put("masterdata.city", senderParty.zip() + " " + senderParty.city()); - variables.put("masterdata.phone", senderParty.phone()); + variables.put("masterdata.phone", phone); variables.put("masterdata.email", senderParty.email()); - variables.put("masterdata.website", ""); + variables.put("masterdata.website", website); variables.put("masterdata.sender_line", senderParty.name() + " · " + senderParty.street() + " · " + senderParty.zip() + " " + senderParty.city()); variables.put("masterdata.payment_terms", metadata.paymentTerms()); @@ -697,6 +710,22 @@ public class MainView extends VerticalLayout { return variables; } + /** + * @return der Adressbuch-Eintrag des Rechnungsstellers: die Auswahl in + * "Aus dem Adressbuch übernehmen", sonst der Namenstreffer unter + * den Rechnungssteller-Adressen (z. B. nach Laden einer Vorbelegung). + */ + private Optional
findSenderAddress(String senderName) { + Address selected = senderAddressSelect.getValue(); + if (selected != null) { + return Optional.of(selected); + } + String normalized = senderName == null ? "" : senderName.trim(); + return addressRepository.findByType(AddressType.RECHNUNGSSTELLER).stream() + .filter(address -> normalized.equalsIgnoreCase(nullToEmpty(address.getName()).trim())) + .findFirst(); + } + /** Wandelt das Quell-PDF in die ZUGFeRD-E-Rechnung um und bietet das ZIP zum Download an. */ private void convertAndOfferDownload(byte[] sourcePdf) { try { @@ -823,6 +852,10 @@ public class MainView extends VerticalLayout { private final TextField vatId = new TextField("USt-IdNr."); private final EmailField email = new EmailField("E-Mail"); + // Kontaktangaben; nur beim Rechnungssteller sichtbar und optional + private final TextField phone = new TextField("Telefon"); + private final TextField website = new TextField("Webseite"); + // Bankverbindung; nur beim Rechnungssteller sichtbar und Pflicht private final TextField iban = new TextField("IBAN"); private final TextField bic = new TextField("BIC"); @@ -849,11 +882,12 @@ public class MainView extends VerticalLayout { email.addValueChangeListener(event -> checkEmail()); add(name, street, zip, city, countryCode, vatId, email); if (vatRequired) { + website.setHelperText("z. B. www.assecutor.de"); iban.setRequired(true); iban.setHelperText("Für die Zahlungsangaben (BG-16)"); bic.setRequired(true); bic.setHelperText("8 oder 11 Stellen, z. B. NOLADE21RZB"); - add(iban, bic); + add(phone, website, iban, bic); } setResponsiveSteps(new ResponsiveStep("0", 1), new ResponsiveStep("600px", 3)); } @@ -864,6 +898,16 @@ public class MainView extends VerticalLayout { bic.setValue(bicValue); } + /** Belegt Telefon und Webseite vor, z. B. aus dem Adressbuch. */ + void prefillContact(String phoneValue, String websiteValue) { + phone.setValue(phoneValue); + website.setValue(websiteValue); + } + + String websiteValue() { + return website.getValue().trim(); + } + /** IBAN ohne Leerzeichen in Großschreibung. */ String ibanNormalized() { return iban.getValue().replaceAll("\\s", "").toUpperCase(); @@ -906,6 +950,8 @@ public class MainView extends VerticalLayout { countryCode.addValueChangeListener(event -> listener.run()); vatId.addValueChangeListener(event -> listener.run()); email.addValueChangeListener(event -> listener.run()); + phone.addValueChangeListener(event -> listener.run()); + website.addValueChangeListener(event -> listener.run()); iban.addValueChangeListener(event -> listener.run()); bic.addValueChangeListener(event -> listener.run()); } @@ -913,6 +959,7 @@ public class MainView extends VerticalLayout { /** Leert alle Felder; das Land behält den Standardwert "DE". */ void clearFields() { prefill("", "", "", "", "DE", "", ""); + prefillContact("", ""); prefillBank("", ""); name.setInvalid(false); street.setInvalid(false); @@ -995,7 +1042,7 @@ public class MainView extends VerticalLayout { countryCode.getValue().trim().toUpperCase(), vatId.isEmpty() ? "" : VatIdValidator.normalize(vatId.getValue()), email.getValue().trim(), - "" + phone.getValue().trim() ); } }