Telefon und Webseite des Rechnungsstellers: Adressbuch-Feld, Formularfelder, Belegung von masterdata.phone/website
- Adressbuch: neues Feld "Webseite" pro Unternehmen - Rechnungsmaske: Telefon und Webseite beim Rechnungssteller manuell erfassbar, Vorbelegung aus dem Adressbuch - masterdata.phone/masterdata.website werden aus den Formularfeldern belegt, mit Adressbuch-Fallback (bisher immer leer) - Webseite wird in Vorbelegungen mitgespeichert, Telefon fließt über Party.phone in die ZUGFeRD-Daten Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,7 @@ public class Address {
|
|||||||
private String vatId;
|
private String vatId;
|
||||||
private String email;
|
private String email;
|
||||||
private String phone;
|
private String phone;
|
||||||
|
private String website;
|
||||||
|
|
||||||
// Bankverbindung (v. a. für Rechnungssteller relevant)
|
// Bankverbindung (v. a. für Rechnungssteller relevant)
|
||||||
private String bankName;
|
private String bankName;
|
||||||
@@ -119,6 +120,14 @@ public class Address {
|
|||||||
this.phone = phone;
|
this.phone = phone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getWebsite() {
|
||||||
|
return website;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWebsite(String website) {
|
||||||
|
this.website = website;
|
||||||
|
}
|
||||||
|
|
||||||
public String getBankName() {
|
public String getBankName() {
|
||||||
return bankName;
|
return bankName;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ public record InvoiceDraft(
|
|||||||
String buyerReference,
|
String buyerReference,
|
||||||
String iban,
|
String iban,
|
||||||
String bic,
|
String bic,
|
||||||
|
String website,
|
||||||
InvoiceMetadata.Party sender,
|
InvoiceMetadata.Party sender,
|
||||||
InvoiceMetadata.Party recipient,
|
InvoiceMetadata.Party recipient,
|
||||||
List<InvoiceMetadata.LineItem> items,
|
List<InvoiceMetadata.LineItem> items,
|
||||||
|
|||||||
@@ -159,6 +159,9 @@ public class AddressBookView extends VerticalLayout {
|
|||||||
});
|
});
|
||||||
TextField phone = new TextField("Telefon");
|
TextField phone = new TextField("Telefon");
|
||||||
phone.setValue(target.getPhone() != null ? target.getPhone() : "");
|
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)
|
// Bankverbindung (optional)
|
||||||
TextField bankName = new TextField("Bank");
|
TextField bankName = new TextField("Bank");
|
||||||
@@ -170,7 +173,7 @@ public class AddressBookView extends VerticalLayout {
|
|||||||
bic.setValue(target.getBic() != null ? target.getBic() : "");
|
bic.setValue(target.getBic() != null ? target.getBic() : "");
|
||||||
|
|
||||||
FormLayout form = new FormLayout(type, name, street, zip, city, countryCode, vatId, email, phone,
|
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.setResponsiveSteps(new FormLayout.ResponsiveStep("0", 1), new FormLayout.ResponsiveStep("500px", 2));
|
||||||
form.setWidth("560px");
|
form.setWidth("560px");
|
||||||
dialog.add(form);
|
dialog.add(form);
|
||||||
@@ -231,6 +234,7 @@ public class AddressBookView extends VerticalLayout {
|
|||||||
target.setVatId(vatId.getValue().isBlank() ? "" : VatIdValidator.normalize(vatId.getValue()));
|
target.setVatId(vatId.getValue().isBlank() ? "" : VatIdValidator.normalize(vatId.getValue()));
|
||||||
target.setEmail(email.getValue().trim());
|
target.setEmail(email.getValue().trim());
|
||||||
target.setPhone(phone.getValue().trim());
|
target.setPhone(phone.getValue().trim());
|
||||||
|
target.setWebsite(website.getValue().trim());
|
||||||
target.setBankName(bankName.getValue().trim());
|
target.setBankName(bankName.getValue().trim());
|
||||||
target.setIban(iban.getValue().isBlank() ? "" : IbanValidator.normalize(iban.getValue()));
|
target.setIban(iban.getValue().isBlank() ? "" : IbanValidator.normalize(iban.getValue()));
|
||||||
target.setBic(bic.getValue().trim().toUpperCase());
|
target.setBic(bic.getValue().trim().toUpperCase());
|
||||||
|
|||||||
@@ -379,6 +379,7 @@ public class MainView extends VerticalLayout {
|
|||||||
"",
|
"",
|
||||||
sender.ibanNormalized(),
|
sender.ibanNormalized(),
|
||||||
sender.bicNormalized(),
|
sender.bicNormalized(),
|
||||||
|
sender.websiteValue(),
|
||||||
sender.toParty(), recipient.toParty(),
|
sender.toParty(), recipient.toParty(),
|
||||||
List.copyOf(lineItems),
|
List.copyOf(lineItems),
|
||||||
LocalDateTime.now());
|
LocalDateTime.now());
|
||||||
@@ -410,6 +411,8 @@ public class MainView extends VerticalLayout {
|
|||||||
currency.setValue(nullToEmpty(draft.currency()));
|
currency.setValue(nullToEmpty(draft.currency()));
|
||||||
paymentTerms.setValue(nullToEmpty(draft.paymentTerms()));
|
paymentTerms.setValue(nullToEmpty(draft.paymentTerms()));
|
||||||
applyParty(sender, draft.sender());
|
applyParty(sender, draft.sender());
|
||||||
|
sender.prefillContact(draft.sender() != null ? nullToEmpty(draft.sender().phone()) : "",
|
||||||
|
nullToEmpty(draft.website()));
|
||||||
sender.prefillBank(nullToEmpty(draft.iban()), nullToEmpty(draft.bic()));
|
sender.prefillBank(nullToEmpty(draft.iban()), nullToEmpty(draft.bic()));
|
||||||
applyParty(recipient, draft.recipient());
|
applyParty(recipient, draft.recipient());
|
||||||
lineItems.clear();
|
lineItems.clear();
|
||||||
@@ -447,6 +450,7 @@ public class MainView extends VerticalLayout {
|
|||||||
nullToEmpty(address.getZip()), nullToEmpty(address.getCity()),
|
nullToEmpty(address.getZip()), nullToEmpty(address.getCity()),
|
||||||
nullToEmpty(address.getCountryCode()), nullToEmpty(address.getVatId()),
|
nullToEmpty(address.getCountryCode()), nullToEmpty(address.getVatId()),
|
||||||
nullToEmpty(address.getEmail()));
|
nullToEmpty(address.getEmail()));
|
||||||
|
form.prefillContact(nullToEmpty(address.getPhone()), nullToEmpty(address.getWebsite()));
|
||||||
form.prefillBank(nullToEmpty(address.getIban()), nullToEmpty(address.getBic()));
|
form.prefillBank(nullToEmpty(address.getIban()), nullToEmpty(address.getBic()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -545,6 +549,7 @@ public class MainView extends VerticalLayout {
|
|||||||
"",
|
"",
|
||||||
sender.ibanNormalized(),
|
sender.ibanNormalized(),
|
||||||
sender.bicNormalized(),
|
sender.bicNormalized(),
|
||||||
|
sender.websiteValue(),
|
||||||
sender.toParty(), recipient.toParty(),
|
sender.toParty(), recipient.toParty(),
|
||||||
List.copyOf(lineItems),
|
List.copyOf(lineItems),
|
||||||
null);
|
null);
|
||||||
@@ -661,12 +666,20 @@ public class MainView extends VerticalLayout {
|
|||||||
private Map<String, String> buildTemplateVariables(InvoiceMetadata metadata) {
|
private Map<String, String> buildTemplateVariables(InvoiceMetadata metadata) {
|
||||||
Map<String, String> variables = new LinkedHashMap<>();
|
Map<String, String> variables = new LinkedHashMap<>();
|
||||||
InvoiceMetadata.Party senderParty = metadata.sender();
|
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<Address> 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.company_name", senderParty.name());
|
||||||
variables.put("masterdata.street", senderParty.street());
|
variables.put("masterdata.street", senderParty.street());
|
||||||
variables.put("masterdata.city", senderParty.zip() + " " + senderParty.city());
|
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.email", senderParty.email());
|
||||||
variables.put("masterdata.website", "");
|
variables.put("masterdata.website", website);
|
||||||
variables.put("masterdata.sender_line",
|
variables.put("masterdata.sender_line",
|
||||||
senderParty.name() + " · " + senderParty.street() + " · " + senderParty.zip() + " " + senderParty.city());
|
senderParty.name() + " · " + senderParty.street() + " · " + senderParty.zip() + " " + senderParty.city());
|
||||||
variables.put("masterdata.payment_terms", metadata.paymentTerms());
|
variables.put("masterdata.payment_terms", metadata.paymentTerms());
|
||||||
@@ -697,6 +710,22 @@ public class MainView extends VerticalLayout {
|
|||||||
return variables;
|
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<Address> 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. */
|
/** Wandelt das Quell-PDF in die ZUGFeRD-E-Rechnung um und bietet das ZIP zum Download an. */
|
||||||
private void convertAndOfferDownload(byte[] sourcePdf) {
|
private void convertAndOfferDownload(byte[] sourcePdf) {
|
||||||
try {
|
try {
|
||||||
@@ -823,6 +852,10 @@ public class MainView extends VerticalLayout {
|
|||||||
private final TextField vatId = new TextField("USt-IdNr.");
|
private final TextField vatId = new TextField("USt-IdNr.");
|
||||||
private final EmailField email = new EmailField("E-Mail");
|
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
|
// Bankverbindung; nur beim Rechnungssteller sichtbar und Pflicht
|
||||||
private final TextField iban = new TextField("IBAN");
|
private final TextField iban = new TextField("IBAN");
|
||||||
private final TextField bic = new TextField("BIC");
|
private final TextField bic = new TextField("BIC");
|
||||||
@@ -849,11 +882,12 @@ public class MainView extends VerticalLayout {
|
|||||||
email.addValueChangeListener(event -> checkEmail());
|
email.addValueChangeListener(event -> checkEmail());
|
||||||
add(name, street, zip, city, countryCode, vatId, email);
|
add(name, street, zip, city, countryCode, vatId, email);
|
||||||
if (vatRequired) {
|
if (vatRequired) {
|
||||||
|
website.setHelperText("z. B. www.assecutor.de");
|
||||||
iban.setRequired(true);
|
iban.setRequired(true);
|
||||||
iban.setHelperText("Für die Zahlungsangaben (BG-16)");
|
iban.setHelperText("Für die Zahlungsangaben (BG-16)");
|
||||||
bic.setRequired(true);
|
bic.setRequired(true);
|
||||||
bic.setHelperText("8 oder 11 Stellen, z. B. NOLADE21RZB");
|
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));
|
setResponsiveSteps(new ResponsiveStep("0", 1), new ResponsiveStep("600px", 3));
|
||||||
}
|
}
|
||||||
@@ -864,6 +898,16 @@ public class MainView extends VerticalLayout {
|
|||||||
bic.setValue(bicValue);
|
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. */
|
/** IBAN ohne Leerzeichen in Großschreibung. */
|
||||||
String ibanNormalized() {
|
String ibanNormalized() {
|
||||||
return iban.getValue().replaceAll("\\s", "").toUpperCase();
|
return iban.getValue().replaceAll("\\s", "").toUpperCase();
|
||||||
@@ -906,6 +950,8 @@ public class MainView extends VerticalLayout {
|
|||||||
countryCode.addValueChangeListener(event -> listener.run());
|
countryCode.addValueChangeListener(event -> listener.run());
|
||||||
vatId.addValueChangeListener(event -> listener.run());
|
vatId.addValueChangeListener(event -> listener.run());
|
||||||
email.addValueChangeListener(event -> listener.run());
|
email.addValueChangeListener(event -> listener.run());
|
||||||
|
phone.addValueChangeListener(event -> listener.run());
|
||||||
|
website.addValueChangeListener(event -> listener.run());
|
||||||
iban.addValueChangeListener(event -> listener.run());
|
iban.addValueChangeListener(event -> listener.run());
|
||||||
bic.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". */
|
/** Leert alle Felder; das Land behält den Standardwert "DE". */
|
||||||
void clearFields() {
|
void clearFields() {
|
||||||
prefill("", "", "", "", "DE", "", "");
|
prefill("", "", "", "", "DE", "", "");
|
||||||
|
prefillContact("", "");
|
||||||
prefillBank("", "");
|
prefillBank("", "");
|
||||||
name.setInvalid(false);
|
name.setInvalid(false);
|
||||||
street.setInvalid(false);
|
street.setInvalid(false);
|
||||||
@@ -995,7 +1042,7 @@ public class MainView extends VerticalLayout {
|
|||||||
countryCode.getValue().trim().toUpperCase(),
|
countryCode.getValue().trim().toUpperCase(),
|
||||||
vatId.isEmpty() ? "" : VatIdValidator.normalize(vatId.getValue()),
|
vatId.isEmpty() ? "" : VatIdValidator.normalize(vatId.getValue()),
|
||||||
email.getValue().trim(),
|
email.getValue().trim(),
|
||||||
""
|
phone.getValue().trim()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user