Version 0.9.14: E-Mail-Feld in Stationsdialogen und Kundenvalidierung
- E-Mail-Feld in Abhol- und Zustellstationsdialogen hinzugefügt - E-Mail-Pflichtfeld bei "Adresse speichern" mit Validierung - Kundenvalidierung im Backend (E-Mail Pflicht und Formatprüfung) - "Adresse speichern" wird bei Auswahl existierender Kunden deaktiviert - Verbessertes Kunden-Matching über alle Felder inkl. E-Mail - Übersetzung "Template" → "Vorlage" in messages_de.properties
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<revision>0.9.13</revision>
|
||||
<revision>0.9.14</revision>
|
||||
<java.version>21</java.version>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
|
||||
@@ -46,6 +46,7 @@ public class DeliveryStationDialog extends Dialog {
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private String phone;
|
||||
private String mail;
|
||||
private String street;
|
||||
private String houseNumber;
|
||||
private String addressAddition;
|
||||
@@ -112,6 +113,14 @@ public class DeliveryStationDialog extends Dialog {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getMail() {
|
||||
return mail;
|
||||
}
|
||||
|
||||
public void setMail(String mail) {
|
||||
this.mail = mail;
|
||||
}
|
||||
|
||||
public String getStreet() {
|
||||
return street;
|
||||
}
|
||||
@@ -185,6 +194,7 @@ public class DeliveryStationDialog extends Dialog {
|
||||
private final TextField firstName;
|
||||
private final TextField lastName;
|
||||
private final TextField phone;
|
||||
private final TextField mail;
|
||||
private final TextField street;
|
||||
private final TextField houseNumber;
|
||||
private final TextField addressAddition;
|
||||
@@ -258,6 +268,12 @@ public class DeliveryStationDialog extends Dialog {
|
||||
phone.setWidthFull();
|
||||
formLayout.add(phone);
|
||||
|
||||
// E-Mail
|
||||
mail = new TextField(translationHelper.getTranslation("customers.column.email"));
|
||||
mail.setPlaceholder(translationHelper.getTranslation("customers.column.email"));
|
||||
mail.setWidthFull();
|
||||
formLayout.add(mail);
|
||||
|
||||
// Street + house number
|
||||
street = new TextField(translationHelper.getTranslation("profile.street"));
|
||||
street.setPlaceholder(translationHelper.getTranslation("profile.street"));
|
||||
@@ -307,12 +323,41 @@ public class DeliveryStationDialog extends Dialog {
|
||||
|
||||
// Clear error styling on value change for required fields and update tab
|
||||
// indicators
|
||||
firstName.addValueChangeListener(ev -> validateRequiredFields());
|
||||
lastName.addValueChangeListener(ev -> validateRequiredFields());
|
||||
street.addValueChangeListener(ev -> validateRequiredFields());
|
||||
houseNumber.addValueChangeListener(ev -> validateRequiredFields());
|
||||
zip.addValueChangeListener(ev -> validateRequiredFields());
|
||||
city.addValueChangeListener(ev -> validateRequiredFields());
|
||||
firstName.addValueChangeListener(ev -> {
|
||||
validateRequiredFields();
|
||||
updateSaveAddressState();
|
||||
});
|
||||
lastName.addValueChangeListener(ev -> {
|
||||
validateRequiredFields();
|
||||
updateSaveAddressState();
|
||||
});
|
||||
street.addValueChangeListener(ev -> {
|
||||
validateRequiredFields();
|
||||
updateSaveAddressState();
|
||||
});
|
||||
houseNumber.addValueChangeListener(ev -> {
|
||||
validateRequiredFields();
|
||||
updateSaveAddressState();
|
||||
});
|
||||
zip.addValueChangeListener(ev -> {
|
||||
validateRequiredFields();
|
||||
updateSaveAddressState();
|
||||
});
|
||||
city.addValueChangeListener(ev -> {
|
||||
validateRequiredFields();
|
||||
updateSaveAddressState();
|
||||
});
|
||||
salutation.addValueChangeListener(ev -> updateSaveAddressState());
|
||||
phone.addValueChangeListener(ev -> updateSaveAddressState());
|
||||
addressAddition.addValueChangeListener(ev -> updateSaveAddressState());
|
||||
mail.addValueChangeListener(ev -> {
|
||||
validateRequiredFields();
|
||||
updateSaveAddressState();
|
||||
});
|
||||
saveAddress.addValueChangeListener(ev -> {
|
||||
updateMailRequirement();
|
||||
validateRequiredFields();
|
||||
});
|
||||
|
||||
// TabSheet with address and tasks tabs
|
||||
TabSheet tabSheet = new TabSheet();
|
||||
@@ -453,6 +498,10 @@ public class DeliveryStationDialog extends Dialog {
|
||||
lastName.setValue(data.getLastName());
|
||||
if (data.getPhone() != null)
|
||||
phone.setValue(data.getPhone());
|
||||
if (data.getMail() != null)
|
||||
mail.setValue(data.getMail());
|
||||
else
|
||||
mail.clear();
|
||||
if (data.getStreet() != null)
|
||||
street.setValue(data.getStreet());
|
||||
if (data.getHouseNumber() != null)
|
||||
@@ -464,7 +513,7 @@ public class DeliveryStationDialog extends Dialog {
|
||||
if (data.getCity() != null)
|
||||
city.setValue(data.getCity());
|
||||
saveAddress.setValue(customerSelectedFromOptions ? false : data.isSaveAddress());
|
||||
updateSaveAddressState(customerSelectedFromOptions);
|
||||
updateSaveAddressState();
|
||||
|
||||
// Load tasks into dialog state
|
||||
if (data.getTasks() != null && !data.getTasks().isEmpty()) {
|
||||
@@ -482,6 +531,7 @@ public class DeliveryStationDialog extends Dialog {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private DeliveryData collectData() {
|
||||
@@ -491,6 +541,7 @@ public class DeliveryStationDialog extends Dialog {
|
||||
data.setFirstName(firstName.getValue());
|
||||
data.setLastName(lastName.getValue());
|
||||
data.setPhone(phone.getValue());
|
||||
data.setMail(mail.getValue());
|
||||
data.setStreet(street.getValue());
|
||||
data.setHouseNumber(houseNumber.getValue());
|
||||
data.setAddressAddition(addressAddition.getValue());
|
||||
@@ -510,6 +561,7 @@ public class DeliveryStationDialog extends Dialog {
|
||||
addressValid &= validateTextField(houseNumber);
|
||||
addressValid &= validateTextField(zip);
|
||||
addressValid &= validateTextField(city);
|
||||
addressValid &= validateMailField();
|
||||
addressTabError.setVisible(!addressValid);
|
||||
|
||||
// Tasks tab validation
|
||||
@@ -545,6 +597,17 @@ public class DeliveryStationDialog extends Dialog {
|
||||
return !empty;
|
||||
}
|
||||
|
||||
private boolean validateMailField() {
|
||||
String value = mail.getValue();
|
||||
String normalizedValue = value == null ? "" : value.trim();
|
||||
boolean empty = normalizedValue.isEmpty();
|
||||
boolean required = Boolean.TRUE.equals(saveAddress.getValue());
|
||||
boolean invalid = !empty && !normalizedValue.contains("@");
|
||||
boolean hasError = invalid || (required && empty);
|
||||
applyErrorStyling(mail, hasError);
|
||||
return !hasError;
|
||||
}
|
||||
|
||||
private void applyErrorStyling(com.vaadin.flow.component.Component field, boolean error) {
|
||||
if (error) {
|
||||
field.getElement().getStyle().set("--vaadin-input-field-background", "rgba(255, 0, 0, 0.1)");
|
||||
@@ -584,8 +647,8 @@ public class DeliveryStationDialog extends Dialog {
|
||||
|
||||
companyField.addValueChangeListener(event -> {
|
||||
Customer customer = companyAddressOptions.get(event.getValue());
|
||||
updateSaveAddressState(customer != null);
|
||||
if (customer == null) {
|
||||
updateSaveAddressState();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -600,6 +663,8 @@ public class DeliveryStationDialog extends Dialog {
|
||||
lastName.setValue(customer.getLastName());
|
||||
if (customer.getTelephone() != null)
|
||||
phone.setValue(customer.getTelephone());
|
||||
if (customer.getMail() != null)
|
||||
mail.setValue(customer.getMail());
|
||||
if (customer.getStreet() != null)
|
||||
street.setValue(customer.getStreet());
|
||||
if (customer.getHouseNumber() != null)
|
||||
@@ -610,22 +675,32 @@ public class DeliveryStationDialog extends Dialog {
|
||||
zip.setValue(customer.getZip());
|
||||
if (customer.getCity() != null)
|
||||
city.setValue(customer.getCity());
|
||||
updateSaveAddressState();
|
||||
});
|
||||
|
||||
companyField.addCustomValueSetListener(event -> {
|
||||
companyField.setValue(event.getDetail());
|
||||
updateSaveAddressState(false);
|
||||
updateSaveAddressState();
|
||||
});
|
||||
}
|
||||
|
||||
private void updateSaveAddressState(boolean customerSelectedFromOptions) {
|
||||
private void updateSaveAddressState() {
|
||||
Customer selectedCustomer = companyAddressOptions.get(company.getValue());
|
||||
boolean customerSelectedFromOptions = selectedCustomer != null && matchesCurrentCustomer(selectedCustomer);
|
||||
|
||||
if (customerSelectedFromOptions) {
|
||||
saveAddress.setValue(false);
|
||||
saveAddress.setEnabled(false);
|
||||
updateMailRequirement();
|
||||
return;
|
||||
}
|
||||
|
||||
saveAddress.setEnabled(true);
|
||||
updateMailRequirement();
|
||||
}
|
||||
|
||||
private void updateMailRequirement() {
|
||||
mail.setRequiredIndicatorVisible(Boolean.TRUE.equals(saveAddress.getValue()));
|
||||
}
|
||||
|
||||
private String buildCompanyAddressLabel(Customer customer) {
|
||||
@@ -687,12 +762,32 @@ public class DeliveryStationDialog extends Dialog {
|
||||
|
||||
private boolean matchesCustomer(Customer customer, DeliveryData data) {
|
||||
return equalsNormalized(customer.getCompanyName(), data.getCompany())
|
||||
&& equalsNormalized(customer.getTitle(), data.getSalutation())
|
||||
&& equalsNormalized(customer.getFirstname(), data.getFirstName())
|
||||
&& equalsNormalized(customer.getLastName(), data.getLastName())
|
||||
&& equalsNormalized(customer.getTelephone(), data.getPhone())
|
||||
&& equalsNormalized(customer.getMail(), data.getMail())
|
||||
&& equalsNormalized(customer.getStreet(), data.getStreet())
|
||||
&& equalsNormalized(customer.getAddressAddition(), data.getAddressAddition())
|
||||
&& equalsNormalized(customer.getHouseNumber(), data.getHouseNumber())
|
||||
&& equalsNormalized(customer.getZip(), data.getZip())
|
||||
&& equalsNormalized(customer.getCity(), data.getCity());
|
||||
}
|
||||
|
||||
private boolean matchesCurrentCustomer(Customer customer) {
|
||||
return equalsNormalized(customer.getCompanyName(), resolveCompanyValue(company.getValue()))
|
||||
&& equalsNormalized(customer.getTitle(), salutation.getValue())
|
||||
&& equalsNormalized(customer.getFirstname(), firstName.getValue())
|
||||
&& equalsNormalized(customer.getLastName(), lastName.getValue())
|
||||
&& equalsNormalized(customer.getTelephone(), phone.getValue())
|
||||
&& equalsNormalized(customer.getMail(), mail.getValue())
|
||||
&& equalsNormalized(customer.getStreet(), street.getValue())
|
||||
&& equalsNormalized(customer.getAddressAddition(), addressAddition.getValue())
|
||||
&& equalsNormalized(customer.getHouseNumber(), houseNumber.getValue())
|
||||
&& equalsNormalized(customer.getZip(), zip.getValue())
|
||||
&& equalsNormalized(customer.getCity(), city.getValue());
|
||||
}
|
||||
|
||||
private boolean equalsNormalized(String left, String right) {
|
||||
String normalizedLeft = left != null ? left.trim() : "";
|
||||
String normalizedRight = right != null ? right.trim() : "";
|
||||
@@ -756,8 +851,9 @@ public class DeliveryStationDialog extends Dialog {
|
||||
tasksList.setPadding(false);
|
||||
tasksList.setSpacing(true);
|
||||
|
||||
// Add 1 example row
|
||||
// Add 1 example row, then append a signature task once on initial setup
|
||||
createTaskRow();
|
||||
ensureTrailingSignatureTask();
|
||||
|
||||
Button addTaskBtn = new Button(translationHelper.getTranslation("addjob.tasks.add"), new Icon(VaadinIcon.PLUS));
|
||||
addTaskBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
@@ -791,14 +887,7 @@ public class DeliveryStationDialog extends Dialog {
|
||||
Button deleteXButton = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
|
||||
deleteXButton.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
|
||||
deleteXButton.addClassName("dialog-floating-delete");
|
||||
deleteXButton.addClickListener(e -> {
|
||||
int idx = tasksList.getChildren().toList().indexOf(taskContainer);
|
||||
if (idx >= 0 && idx < tasksState.size()) {
|
||||
tasksState.remove(idx);
|
||||
reorderTasksAfterDeletion();
|
||||
}
|
||||
tasksList.remove(taskContainer);
|
||||
});
|
||||
deleteXButton.addClickListener(e -> removeTaskRow(taskContainer));
|
||||
|
||||
taskContainer.add(taskTypeCombo, configContainer);
|
||||
taskContainer.add(deleteXButton);
|
||||
@@ -810,6 +899,9 @@ public class DeliveryStationDialog extends Dialog {
|
||||
|
||||
final BaseTask[] currentTask = { task };
|
||||
|
||||
taskTypeCombo.setValue(TaskType.CONFIRMATION);
|
||||
updateTaskConfiguration(configContainer, currentTask[0]);
|
||||
|
||||
taskTypeCombo.addValueChangeListener(ev -> {
|
||||
TaskType selectedType = ev.getValue();
|
||||
if (selectedType != null) {
|
||||
@@ -856,11 +948,8 @@ public class DeliveryStationDialog extends Dialog {
|
||||
}
|
||||
});
|
||||
|
||||
// Set initial configuration
|
||||
taskTypeCombo.setValue(TaskType.CONFIRMATION);
|
||||
updateTaskConfiguration(configContainer, currentTask[0]);
|
||||
|
||||
tasksList.add(taskContainer);
|
||||
updateTaskDeleteAvailability();
|
||||
}
|
||||
|
||||
private void createTaskRowFromTask(BaseTask task) {
|
||||
@@ -882,14 +971,7 @@ public class DeliveryStationDialog extends Dialog {
|
||||
Button deleteXButton = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
|
||||
deleteXButton.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
|
||||
deleteXButton.addClassName("dialog-floating-delete");
|
||||
deleteXButton.addClickListener(e -> {
|
||||
int idx = tasksList.getChildren().toList().indexOf(taskContainer);
|
||||
if (idx >= 0 && idx < tasksState.size()) {
|
||||
tasksState.remove(idx);
|
||||
reorderTasksAfterDeletion();
|
||||
}
|
||||
tasksList.remove(taskContainer);
|
||||
});
|
||||
deleteXButton.addClickListener(e -> removeTaskRow(taskContainer));
|
||||
|
||||
taskContainer.add(taskTypeCombo, configContainer);
|
||||
taskContainer.add(deleteXButton);
|
||||
@@ -951,6 +1033,7 @@ public class DeliveryStationDialog extends Dialog {
|
||||
updateTaskConfiguration(configContainer, task);
|
||||
|
||||
tasksList.add(taskContainer);
|
||||
updateTaskDeleteAvailability();
|
||||
}
|
||||
|
||||
private BaseTask createTaskByType(TaskType taskType) {
|
||||
@@ -973,6 +1056,56 @@ public class DeliveryStationDialog extends Dialog {
|
||||
}
|
||||
}
|
||||
|
||||
private void removeTaskRow(VerticalLayout taskContainer) {
|
||||
if (tasksList == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<com.vaadin.flow.component.Component> taskRows = tasksList.getChildren().toList();
|
||||
int idx = taskRows.indexOf(taskContainer);
|
||||
if (idx < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (idx < tasksState.size()) {
|
||||
tasksState.remove(idx);
|
||||
}
|
||||
tasksList.remove(taskContainer);
|
||||
reorderTasksAfterDeletion();
|
||||
updateTaskDeleteAvailability();
|
||||
}
|
||||
|
||||
private void ensureTrailingSignatureTask() {
|
||||
BaseTask lastTask = tasksState.isEmpty() ? null : tasksState.get(tasksState.size() - 1);
|
||||
if (lastTask instanceof SignatureTask) {
|
||||
return;
|
||||
}
|
||||
|
||||
SignatureTask signatureTask = new SignatureTask();
|
||||
signatureTask.setTaskOrder(tasksState.size());
|
||||
tasksState.add(signatureTask);
|
||||
if (tasksList != null) {
|
||||
createTaskRowFromTask(signatureTask);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTaskDeleteAvailability() {
|
||||
if (tasksList == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean deletable = tasksList.getChildren().count() > 1;
|
||||
tasksList.getChildren()
|
||||
.filter(VerticalLayout.class::isInstance)
|
||||
.map(VerticalLayout.class::cast)
|
||||
.forEach(taskContainer -> taskContainer.getChildren()
|
||||
.filter(Button.class::isInstance)
|
||||
.map(Button.class::cast)
|
||||
.filter(button -> button.getClassNames().contains("dialog-floating-delete"))
|
||||
.findFirst()
|
||||
.ifPresent(button -> button.setEnabled(deletable)));
|
||||
}
|
||||
|
||||
private void updateTaskConfiguration(VerticalLayout configContainer, BaseTask task) {
|
||||
configContainer.removeAll();
|
||||
|
||||
@@ -1315,16 +1448,22 @@ public class DeliveryStationDialog extends Dialog {
|
||||
}
|
||||
|
||||
private void loadTasksFromTemplate(TaskTemplate template, ComboBox<TaskTemplate> templateComboBox) {
|
||||
ConfirmDialog confirmDialog = new ConfirmDialog();
|
||||
confirmDialog.setHeader(translationHelper.getTranslation("addjob.tasks.template.load.title"));
|
||||
confirmDialog.setText(
|
||||
translationHelper.getTranslation("addjob.tasks.template.load.text", template.getTemplateName()));
|
||||
confirmDialog.setCancelable(true);
|
||||
confirmDialog.setCancelText(translationHelper.getTranslation("button.cancel"));
|
||||
confirmDialog.setConfirmText(translationHelper.getTranslation("addjob.tasks.template.load.confirm"));
|
||||
confirmDialog.setConfirmButtonTheme("primary");
|
||||
Dialog confirmDialog = DialogStylingHelper
|
||||
.createStyledDialog(translationHelper.getTranslation("addjob.tasks.template.load.title"), "560px");
|
||||
confirmDialog.setCloseOnOutsideClick(false);
|
||||
confirmDialog.addDialogCloseActionListener(e -> templateComboBox.clear());
|
||||
|
||||
confirmDialog.addConfirmListener(e -> {
|
||||
VerticalLayout dialogContent = DialogStylingHelper.createContentLayout("420px");
|
||||
dialogContent.add(new Span(
|
||||
translationHelper.getTranslation("addjob.tasks.template.load.text", template.getTemplateName())));
|
||||
|
||||
Button cancelButton = new Button(translationHelper.getTranslation("button.cancel"), e -> {
|
||||
templateComboBox.clear();
|
||||
confirmDialog.close();
|
||||
});
|
||||
cancelButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
||||
|
||||
Button confirmButton = new Button(translationHelper.getTranslation("addjob.tasks.template.load.confirm"), e -> {
|
||||
tasksState.clear();
|
||||
tasksList.removeAll();
|
||||
|
||||
@@ -1338,16 +1477,19 @@ public class DeliveryStationDialog extends Dialog {
|
||||
}
|
||||
}
|
||||
|
||||
ensureTrailingSignatureTask();
|
||||
templateComboBox.clear();
|
||||
validateRequiredFields();
|
||||
|
||||
Notification.show(
|
||||
translationHelper.getTranslation("addjob.tasks.template.loaded", template.getTemplateName()), 3000,
|
||||
Notification.Position.BOTTOM_END);
|
||||
confirmDialog.close();
|
||||
});
|
||||
confirmButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
|
||||
confirmDialog.addCancelListener(e -> templateComboBox.clear());
|
||||
|
||||
confirmDialog.add(DialogStylingHelper.wrapContent(dialogContent));
|
||||
confirmDialog.getFooter().add(cancelButton, confirmButton);
|
||||
confirmDialog.open();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ public class PickupStationDialog extends Dialog {
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private String phone;
|
||||
private String mail;
|
||||
private String street;
|
||||
private String houseNumber;
|
||||
private String addressAddition;
|
||||
@@ -125,6 +126,14 @@ public class PickupStationDialog extends Dialog {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getMail() {
|
||||
return mail;
|
||||
}
|
||||
|
||||
public void setMail(String mail) {
|
||||
this.mail = mail;
|
||||
}
|
||||
|
||||
public String getStreet() {
|
||||
return street;
|
||||
}
|
||||
@@ -231,6 +240,7 @@ public class PickupStationDialog extends Dialog {
|
||||
private final TextField firstName;
|
||||
private final TextField lastName;
|
||||
private final TextField phone;
|
||||
private final TextField mail;
|
||||
private final TextField street;
|
||||
private final TextField houseNumber;
|
||||
private final TextField addressAddition;
|
||||
@@ -239,6 +249,8 @@ public class PickupStationDialog extends Dialog {
|
||||
private final Checkbox saveAddress;
|
||||
|
||||
private final ComboBox<String> customerComboBox;
|
||||
private final Map<String, Customer> customerLabelMap = new LinkedHashMap<>();
|
||||
private final Map<String, Customer> companyCustomerMap = new LinkedHashMap<>();
|
||||
private DatePicker appointmentDatePicker;
|
||||
private TimePicker appointmentTimePicker;
|
||||
private Checkbox digitalProcessingCheckbox;
|
||||
@@ -278,7 +290,7 @@ public class PickupStationDialog extends Dialog {
|
||||
customerComboBox.setRequiredIndicatorVisible(true);
|
||||
customerComboBox.setWidthFull();
|
||||
|
||||
Map<String, Customer> customerLabelMap = new LinkedHashMap<>();
|
||||
customerLabelMap.clear();
|
||||
for (Customer c : customers) {
|
||||
String label = (c.getCompanyName() != null && !c.getCompanyName().isBlank())
|
||||
? c.getCompanyName() + " | "
|
||||
@@ -330,6 +342,11 @@ public class PickupStationDialog extends Dialog {
|
||||
phone.setPlaceholder(translationHelper.getTranslation("profile.phone"));
|
||||
phone.setWidthFull();
|
||||
|
||||
// E-Mail
|
||||
mail = new TextField(translationHelper.getTranslation("customers.column.email"));
|
||||
mail.setPlaceholder(translationHelper.getTranslation("customers.column.email"));
|
||||
mail.setWidthFull();
|
||||
|
||||
// Street + house number
|
||||
street = new TextField(translationHelper.getTranslation("profile.street"));
|
||||
street.setPlaceholder(translationHelper.getTranslation("profile.street"));
|
||||
@@ -375,22 +392,54 @@ public class PickupStationDialog extends Dialog {
|
||||
|
||||
// Clear error styling on value change for required fields and update tab
|
||||
// indicators
|
||||
firstName.addValueChangeListener(ev -> validateRequiredFields());
|
||||
lastName.addValueChangeListener(ev -> validateRequiredFields());
|
||||
street.addValueChangeListener(ev -> validateRequiredFields());
|
||||
houseNumber.addValueChangeListener(ev -> validateRequiredFields());
|
||||
zip.addValueChangeListener(ev -> validateRequiredFields());
|
||||
city.addValueChangeListener(ev -> validateRequiredFields());
|
||||
firstName.addValueChangeListener(ev -> {
|
||||
validateRequiredFields();
|
||||
updateSaveAddressState();
|
||||
});
|
||||
lastName.addValueChangeListener(ev -> {
|
||||
validateRequiredFields();
|
||||
updateSaveAddressState();
|
||||
});
|
||||
street.addValueChangeListener(ev -> {
|
||||
validateRequiredFields();
|
||||
updateSaveAddressState();
|
||||
});
|
||||
houseNumber.addValueChangeListener(ev -> {
|
||||
validateRequiredFields();
|
||||
updateSaveAddressState();
|
||||
});
|
||||
zip.addValueChangeListener(ev -> {
|
||||
validateRequiredFields();
|
||||
updateSaveAddressState();
|
||||
});
|
||||
city.addValueChangeListener(ev -> {
|
||||
validateRequiredFields();
|
||||
updateSaveAddressState();
|
||||
});
|
||||
salutation.addValueChangeListener(ev -> updateSaveAddressState());
|
||||
phone.addValueChangeListener(ev -> updateSaveAddressState());
|
||||
addressAddition.addValueChangeListener(ev -> updateSaveAddressState());
|
||||
mail.addValueChangeListener(ev -> {
|
||||
validateRequiredFields();
|
||||
updateSaveAddressState();
|
||||
});
|
||||
saveAddress.addValueChangeListener(ev -> {
|
||||
updateMailRequirement();
|
||||
validateRequiredFields();
|
||||
});
|
||||
|
||||
// Customer selection fills address fields
|
||||
customerComboBox.addValueChangeListener(ev -> {
|
||||
String selected = ev.getValue();
|
||||
if (selected == null)
|
||||
if (selected == null) {
|
||||
updateSaveAddressState();
|
||||
return;
|
||||
}
|
||||
Customer c = customerLabelMap.get(selected);
|
||||
if (c == null)
|
||||
if (c == null) {
|
||||
updateSaveAddressState();
|
||||
return;
|
||||
saveAddress.setValue(false);
|
||||
}
|
||||
if (c.getCompanyName() != null)
|
||||
company.setValue(c.getCompanyName());
|
||||
else
|
||||
@@ -412,6 +461,10 @@ public class PickupStationDialog extends Dialog {
|
||||
phone.setValue(c.getTelephone());
|
||||
else
|
||||
phone.clear();
|
||||
if (c.getMail() != null)
|
||||
mail.setValue(c.getMail());
|
||||
else
|
||||
mail.clear();
|
||||
if (c.getStreet() != null)
|
||||
street.setValue(c.getStreet());
|
||||
else
|
||||
@@ -432,10 +485,12 @@ public class PickupStationDialog extends Dialog {
|
||||
city.setValue(c.getCity());
|
||||
else
|
||||
city.clear();
|
||||
|
||||
updateSaveAddressState();
|
||||
});
|
||||
|
||||
formLayout.add(customerComboBox, company, salutation, firstName, lastName, phone, streetLayout, addressAddition,
|
||||
zipCityLayout, saveAddress);
|
||||
formLayout.add(customerComboBox, company, salutation, firstName, lastName, phone, mail, streetLayout,
|
||||
addressAddition, zipCityLayout, saveAddress);
|
||||
|
||||
// TabSheet with address, appointments, and cargo tabs
|
||||
TabSheet tabSheet = new TabSheet();
|
||||
@@ -564,6 +619,11 @@ public class PickupStationDialog extends Dialog {
|
||||
public void setData(PickupData data) {
|
||||
if (data == null)
|
||||
return;
|
||||
if (data.getCustomerSelection() != null) {
|
||||
customerComboBox.setValue(data.getCustomerSelection());
|
||||
} else {
|
||||
customerComboBox.clear();
|
||||
}
|
||||
if (data.getCompany() != null)
|
||||
company.setValue(data.getCompany());
|
||||
if (data.getSalutation() != null)
|
||||
@@ -574,6 +634,10 @@ public class PickupStationDialog extends Dialog {
|
||||
lastName.setValue(data.getLastName());
|
||||
if (data.getPhone() != null)
|
||||
phone.setValue(data.getPhone());
|
||||
if (data.getMail() != null)
|
||||
mail.setValue(data.getMail());
|
||||
else
|
||||
mail.clear();
|
||||
if (data.getStreet() != null)
|
||||
street.setValue(data.getStreet());
|
||||
if (data.getHouseNumber() != null)
|
||||
@@ -584,11 +648,6 @@ public class PickupStationDialog extends Dialog {
|
||||
zip.setValue(data.getZip());
|
||||
if (data.getCity() != null)
|
||||
city.setValue(data.getCity());
|
||||
saveAddress.setValue(data.isSaveAddress());
|
||||
|
||||
if (data.getCustomerSelection() != null) {
|
||||
customerComboBox.setValue(data.getCustomerSelection());
|
||||
}
|
||||
if (data.getAppointmentDate() != null && appointmentDatePicker != null) {
|
||||
appointmentDatePicker.setValue(data.getAppointmentDate());
|
||||
}
|
||||
@@ -608,6 +667,9 @@ public class PickupStationDialog extends Dialog {
|
||||
addCargoRowWithData(item);
|
||||
}
|
||||
}
|
||||
|
||||
saveAddress.setValue(data.isSaveAddress());
|
||||
updateSaveAddressState();
|
||||
}
|
||||
|
||||
private PickupData collectData() {
|
||||
@@ -617,6 +679,7 @@ public class PickupStationDialog extends Dialog {
|
||||
data.setFirstName(firstName.getValue());
|
||||
data.setLastName(lastName.getValue());
|
||||
data.setPhone(phone.getValue());
|
||||
data.setMail(mail.getValue());
|
||||
data.setStreet(street.getValue());
|
||||
data.setHouseNumber(houseNumber.getValue());
|
||||
data.setAddressAddition(addressAddition.getValue());
|
||||
@@ -649,6 +712,7 @@ public class PickupStationDialog extends Dialog {
|
||||
addressValid &= validateTextField(houseNumber);
|
||||
addressValid &= validateTextField(zip);
|
||||
addressValid &= validateTextField(city);
|
||||
addressValid &= validateMailField();
|
||||
if (addressTabError != null) {
|
||||
addressTabError.setVisible(!addressValid);
|
||||
}
|
||||
@@ -685,6 +749,17 @@ public class PickupStationDialog extends Dialog {
|
||||
return !empty;
|
||||
}
|
||||
|
||||
private boolean validateMailField() {
|
||||
String value = mail.getValue();
|
||||
String normalizedValue = value == null ? "" : value.trim();
|
||||
boolean empty = normalizedValue.isEmpty();
|
||||
boolean required = Boolean.TRUE.equals(saveAddress.getValue());
|
||||
boolean invalid = !empty && !normalizedValue.contains("@");
|
||||
boolean hasError = invalid || (required && empty);
|
||||
applyErrorStyling(mail, hasError);
|
||||
return !hasError;
|
||||
}
|
||||
|
||||
private boolean validateCargoItems() {
|
||||
boolean valid = true;
|
||||
if (cargoList == null)
|
||||
@@ -737,16 +812,25 @@ public class PickupStationDialog extends Dialog {
|
||||
List<String> companyNames = customers.stream().map(Customer::getCompanyName)
|
||||
.filter(name -> name != null && !name.trim().isEmpty()).distinct().sorted().toList();
|
||||
|
||||
companyCustomerMap.clear();
|
||||
for (Customer customer : customers) {
|
||||
String companyName = normalizeValue(customer.getCompanyName());
|
||||
if (companyName.isEmpty() || companyCustomerMap.containsKey(companyName)) {
|
||||
continue;
|
||||
}
|
||||
companyCustomerMap.put(companyName, customer);
|
||||
}
|
||||
companyField.setItems(companyNames);
|
||||
|
||||
companyField.addValueChangeListener(event -> {
|
||||
String selectedCompany = event.getValue();
|
||||
if (selectedCompany == null || selectedCompany.trim().isEmpty()) {
|
||||
updateSaveAddressState();
|
||||
return;
|
||||
}
|
||||
|
||||
Optional<Customer> matchingCustomer = customers.stream()
|
||||
.filter(c -> selectedCompany.equals(c.getCompanyName())).findFirst();
|
||||
.filter(c -> sameValue(selectedCompany, c.getCompanyName())).findFirst();
|
||||
|
||||
if (matchingCustomer.isPresent()) {
|
||||
Customer customer = matchingCustomer.get();
|
||||
@@ -761,6 +845,8 @@ public class PickupStationDialog extends Dialog {
|
||||
lastName.setValue(customer.getLastName());
|
||||
if (customer.getTelephone() != null)
|
||||
phone.setValue(customer.getTelephone());
|
||||
if (customer.getMail() != null)
|
||||
mail.setValue(customer.getMail());
|
||||
if (customer.getStreet() != null)
|
||||
street.setValue(customer.getStreet());
|
||||
if (customer.getHouseNumber() != null)
|
||||
@@ -772,9 +858,57 @@ public class PickupStationDialog extends Dialog {
|
||||
if (customer.getCity() != null)
|
||||
city.setValue(customer.getCity());
|
||||
}
|
||||
|
||||
updateSaveAddressState();
|
||||
});
|
||||
|
||||
companyField.addCustomValueSetListener(event -> companyField.setValue(event.getDetail()));
|
||||
companyField.addCustomValueSetListener(event -> {
|
||||
companyField.setValue(event.getDetail());
|
||||
updateSaveAddressState();
|
||||
});
|
||||
}
|
||||
|
||||
private void updateSaveAddressState() {
|
||||
Customer selectedCustomer = customerLabelMap.get(customerComboBox.getValue());
|
||||
Customer selectedCompanyCustomer = companyCustomerMap.get(normalizeValue(company.getValue()));
|
||||
boolean existingCustomerSelected = selectedCustomer != null && matchesCustomer(selectedCustomer);
|
||||
boolean existingCompanySelected = selectedCompanyCustomer != null && matchesCustomer(selectedCompanyCustomer);
|
||||
|
||||
if (existingCustomerSelected || existingCompanySelected) {
|
||||
saveAddress.setValue(false);
|
||||
saveAddress.setEnabled(false);
|
||||
updateMailRequirement();
|
||||
return;
|
||||
}
|
||||
|
||||
saveAddress.setEnabled(true);
|
||||
updateMailRequirement();
|
||||
}
|
||||
|
||||
private void updateMailRequirement() {
|
||||
mail.setRequiredIndicatorVisible(Boolean.TRUE.equals(saveAddress.getValue()));
|
||||
}
|
||||
|
||||
private boolean matchesCustomer(Customer customer) {
|
||||
return sameValue(company.getValue(), customer.getCompanyName())
|
||||
&& sameValue(salutation.getValue(), customer.getTitle())
|
||||
&& sameValue(firstName.getValue(), customer.getFirstname())
|
||||
&& sameValue(lastName.getValue(), customer.getLastName())
|
||||
&& sameValue(phone.getValue(), customer.getTelephone())
|
||||
&& sameValue(mail.getValue(), customer.getMail())
|
||||
&& sameValue(street.getValue(), customer.getStreet())
|
||||
&& sameValue(houseNumber.getValue(), customer.getHouseNumber())
|
||||
&& sameValue(addressAddition.getValue(), customer.getAddressAddition())
|
||||
&& sameValue(zip.getValue(), customer.getZip())
|
||||
&& sameValue(city.getValue(), customer.getCity());
|
||||
}
|
||||
|
||||
private boolean sameValue(String left, String right) {
|
||||
return normalizeValue(left).equals(normalizeValue(right));
|
||||
}
|
||||
|
||||
private String normalizeValue(String value) {
|
||||
return value == null ? "" : value.trim();
|
||||
}
|
||||
|
||||
// ============================================
|
||||
|
||||
@@ -19,6 +19,8 @@ public class AddCustomerService {
|
||||
}
|
||||
|
||||
public void addCustomer(Customer customer) {
|
||||
validateCustomer(customer);
|
||||
|
||||
// Setze den aktuellen Benutzer als Ersteller - jetzt direkt aus der Session
|
||||
de.assecutor.votianlt.model.User currentUser = securityService.getCurrentDatabaseUser();
|
||||
customer.setCreatedBy(currentUser.getId());
|
||||
@@ -26,4 +28,20 @@ public class AddCustomerService {
|
||||
|
||||
addCustomerRepository.save(customer);
|
||||
}
|
||||
|
||||
private void validateCustomer(Customer customer) {
|
||||
if (customer == null) {
|
||||
throw new IllegalArgumentException("Kunde darf nicht leer sein");
|
||||
}
|
||||
|
||||
String mail = customer.getMail() != null ? customer.getMail().trim() : "";
|
||||
if (mail.isEmpty()) {
|
||||
throw new IllegalArgumentException("E-Mail-Adresse ist ein Pflichtfeld");
|
||||
}
|
||||
if (!mail.contains("@")) {
|
||||
throw new IllegalArgumentException("Bitte geben Sie eine gültige E-Mail-Adresse ein");
|
||||
}
|
||||
|
||||
customer.setMail(mail);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,6 +131,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
||||
private TextField pickupFirstName;
|
||||
private TextField pickupLastName;
|
||||
private TextField pickupPhone;
|
||||
private String pickupMail;
|
||||
private TextField pickupStreet;
|
||||
private TextField pickupHouseNumber;
|
||||
private TextField pickupAddressAddition;
|
||||
@@ -143,6 +144,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
||||
private final List<StationTile> deliveryStationTilesList = new ArrayList<>();
|
||||
private final List<DeliveryStation> deliveryStationsState = new ArrayList<>();
|
||||
private final List<Boolean> deliveryStationsSaveAddress = new ArrayList<>();
|
||||
private final List<String> deliveryStationsMailState = new ArrayList<>();
|
||||
private final List<Div> deliveryStationSlotList = new ArrayList<>();
|
||||
private final List<Span> deliveryStationDistanceChips = new ArrayList<>();
|
||||
private Div stationsGridContainer;
|
||||
@@ -720,6 +722,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
||||
// Add empty state for this station
|
||||
deliveryStationsState.add(new DeliveryStation());
|
||||
deliveryStationsSaveAddress.add(true);
|
||||
deliveryStationsMailState.add(null);
|
||||
deliveryStationsValidatedByGoogle.add(false);
|
||||
|
||||
int stationIndex = deliveryStationTilesList.size();
|
||||
@@ -766,6 +769,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
||||
deliveryStationTilesList.remove(removeIdx);
|
||||
deliveryStationsState.remove(removeIdx);
|
||||
deliveryStationsSaveAddress.remove(removeIdx);
|
||||
deliveryStationsMailState.remove(removeIdx);
|
||||
deliveryStationsValidatedByGoogle.remove(removeIdx);
|
||||
deliveryStationTasksState.remove(removeIdx);
|
||||
Div removedSlot = deliveryStationSlotList.remove(removeIdx);
|
||||
@@ -854,6 +858,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
||||
pickupFirstName.setValue(data.getFirstName() != null ? data.getFirstName() : "");
|
||||
pickupLastName.setValue(data.getLastName() != null ? data.getLastName() : "");
|
||||
pickupPhone.setValue(data.getPhone() != null ? data.getPhone() : "");
|
||||
pickupMail = trimToNull(data.getMail());
|
||||
pickupStreet.setValue(data.getStreet() != null ? data.getStreet() : "");
|
||||
pickupHouseNumber.setValue(data.getHouseNumber() != null ? data.getHouseNumber() : "");
|
||||
pickupAddressAddition.setValue(data.getAddressAddition() != null ? data.getAddressAddition() : "");
|
||||
@@ -899,6 +904,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
||||
currentData.setFirstName(pickupFirstName.getValue());
|
||||
currentData.setLastName(pickupLastName.getValue());
|
||||
currentData.setPhone(pickupPhone.getValue());
|
||||
currentData.setMail(pickupMail);
|
||||
currentData.setStreet(pickupStreet.getValue());
|
||||
currentData.setHouseNumber(pickupHouseNumber.getValue());
|
||||
currentData.setAddressAddition(pickupAddressAddition.getValue());
|
||||
@@ -1129,6 +1135,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
||||
station.setCity(data.getCity());
|
||||
station.setTasks(data.getTasks() != null ? new ArrayList<>(data.getTasks()) : new ArrayList<>());
|
||||
deliveryStationsSaveAddress.set(idx, data.isSaveAddress());
|
||||
deliveryStationsMailState.set(idx, trimToNull(data.getMail()));
|
||||
|
||||
// Store tasks for this delivery station
|
||||
deliveryStationTasksState.put(idx, data.getTasks() != null ? data.getTasks() : new ArrayList<>());
|
||||
@@ -1166,6 +1173,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
||||
currentData.setFirstName(station.getFirstName());
|
||||
currentData.setLastName(station.getLastName());
|
||||
currentData.setPhone(station.getPhone());
|
||||
currentData.setMail(actualIndex < deliveryStationsMailState.size() ? deliveryStationsMailState.get(actualIndex) : null);
|
||||
currentData.setStreet(station.getStreet());
|
||||
currentData.setHouseNumber(station.getHouseNumber());
|
||||
currentData.setAddressAddition(station.getAddressAddition());
|
||||
@@ -1417,6 +1425,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
||||
pickupLastName.setValue(customer.getLastName());
|
||||
if (customer.getTelephone() != null)
|
||||
pickupPhone.setValue(customer.getTelephone());
|
||||
pickupMail = trimToNull(customer.getMail());
|
||||
if (customer.getStreet() != null)
|
||||
pickupStreet.setValue(customer.getStreet());
|
||||
if (customer.getHouseNumber() != null)
|
||||
@@ -1443,6 +1452,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
||||
|
||||
// Reactivate save checkbox for custom values
|
||||
savePickupAddress.setValue(true);
|
||||
pickupMail = null;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1813,6 +1823,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
||||
pickupCustomer.setFirstname(pickupFirstName.getValue());
|
||||
pickupCustomer.setLastName(pickupLastName.getValue());
|
||||
pickupCustomer.setTelephone(pickupPhone.getValue());
|
||||
pickupCustomer.setMail(pickupMail);
|
||||
pickupCustomer.setStreet(pickupStreet.getValue());
|
||||
pickupCustomer.setHouseNumber(pickupHouseNumber.getValue());
|
||||
pickupCustomer.setAddressAddition(pickupAddressAddition.getValue());
|
||||
@@ -1830,6 +1841,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
||||
deliveryCustomer.setFirstname(ds.getFirstName());
|
||||
deliveryCustomer.setLastName(ds.getLastName());
|
||||
deliveryCustomer.setTelephone(ds.getPhone());
|
||||
deliveryCustomer.setMail(i < deliveryStationsMailState.size() ? deliveryStationsMailState.get(i) : null);
|
||||
deliveryCustomer.setStreet(ds.getStreet());
|
||||
deliveryCustomer.setHouseNumber(ds.getHouseNumber());
|
||||
deliveryCustomer.setAddressAddition(ds.getAddressAddition());
|
||||
|
||||
@@ -478,22 +478,22 @@ addjob.cargo.gridcart=Gitterwagen
|
||||
addjob.cargo.parcel=Paket
|
||||
addjob.cargo.add=Fracht hinzufügen
|
||||
addjob.tasks.title=Aufgaben
|
||||
addjob.tasks.template.placeholder=Template auswählen
|
||||
addjob.tasks.template.save.tooltip=Als Template speichern
|
||||
addjob.tasks.template.save.title=Template speichern
|
||||
addjob.tasks.template.name=Template-Name
|
||||
addjob.tasks.template.placeholder=Vorlage auswählen
|
||||
addjob.tasks.template.save.tooltip=Als Vorlage speichern
|
||||
addjob.tasks.template.save.title=Vorlage speichern
|
||||
addjob.tasks.template.name=Vorlagen-Name
|
||||
addjob.tasks.template.name.placeholder=Name eingeben
|
||||
addjob.tasks.template.name.required=Name ist erforderlich
|
||||
addjob.tasks.template.saved=Template "{0}" gespeichert
|
||||
addjob.tasks.template.saved=Vorlage "{0}" gespeichert
|
||||
addjob.tasks.template.save.error=Fehler beim Speichern: {0}
|
||||
addjob.tasks.template.dialog.error=Fehler beim Öffnen des Dialogs: {0}
|
||||
addjob.tasks.template.no.tasks=Keine Aufgaben zum Speichern
|
||||
addjob.tasks.template.load.title=Template laden
|
||||
addjob.tasks.template.load.text=Möchten Sie das Template "{0}" laden? Diese Aktion ersetzt alle aktuellen Aufgaben.
|
||||
addjob.tasks.template.load.title=Vorlage laden
|
||||
addjob.tasks.template.load.text=Möchten Sie die Vorlage "{0}" laden? Diese Aktion ersetzt alle aktuellen Aufgaben.
|
||||
addjob.tasks.template.load.confirm=Laden
|
||||
addjob.tasks.template.loaded=Template "{0}" geladen
|
||||
addjob.tasks.template.loaded=Vorlage "{0}" geladen
|
||||
addjob.tasks.template.load.error=Fehler beim Laden: {0}
|
||||
addjob.tasks.template.load.templates.error=Fehler beim Laden der Templates: {0}
|
||||
addjob.tasks.template.load.templates.error=Fehler beim Laden der Vorlagen: {0}
|
||||
addjob.tasks.add=Aufgabe hinzufügen
|
||||
addjob.tasks.tasktype=Aufgabentyp
|
||||
addjob.tasks.tasktype.placeholder=Typ wählen
|
||||
|
||||
Reference in New Issue
Block a user