Erweiterungen
This commit is contained in:
40
src/main/java/de/assecutor/votianlt/model/CargoItem.java
Normal file
40
src/main/java/de/assecutor/votianlt/model/CargoItem.java
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package de.assecutor.votianlt.model;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.bson.types.ObjectId;
|
||||||
|
import org.springframework.data.annotation.Id;
|
||||||
|
import org.springframework.data.mongodb.core.mapping.Document;
|
||||||
|
import org.springframework.data.mongodb.core.mapping.Field;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Document(collection = "cargo_items")
|
||||||
|
public class CargoItem {
|
||||||
|
@Id
|
||||||
|
private ObjectId id;
|
||||||
|
|
||||||
|
@Field("job_id")
|
||||||
|
private ObjectId jobId;
|
||||||
|
|
||||||
|
@Field("description")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Field("quantity")
|
||||||
|
private Integer quantity;
|
||||||
|
|
||||||
|
@Field("weight_kg")
|
||||||
|
private Double weightKg;
|
||||||
|
|
||||||
|
@Field("length_mm")
|
||||||
|
private Double lengthMm;
|
||||||
|
|
||||||
|
@Field("width_mm")
|
||||||
|
private Double widthMm;
|
||||||
|
|
||||||
|
@Field("height_mm")
|
||||||
|
private Double heightMm;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -6,7 +6,10 @@ import org.springframework.data.annotation.Id;
|
|||||||
import org.springframework.data.mongodb.core.mapping.Document;
|
import org.springframework.data.mongodb.core.mapping.Document;
|
||||||
import org.springframework.data.mongodb.core.mapping.Field;
|
import org.springframework.data.mongodb.core.mapping.Field;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@Document(collection = "jobs")
|
@Document(collection = "jobs")
|
||||||
@@ -112,7 +115,22 @@ public class Job {
|
|||||||
@Field("app_user")
|
@Field("app_user")
|
||||||
private String appUser;
|
private String appUser;
|
||||||
|
|
||||||
|
// Termine
|
||||||
|
@Field("pickup_date")
|
||||||
|
private LocalDate pickupDate;
|
||||||
|
|
||||||
|
@Field("delivery_date")
|
||||||
|
private LocalDate deliveryDate;
|
||||||
|
|
||||||
|
// Bemerkung
|
||||||
|
@Field("remark")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
// Aufgaben
|
||||||
|
@Field("tasks")
|
||||||
|
private List<String> tasks;
|
||||||
|
|
||||||
// Preis (netto)
|
// Preis (netto)
|
||||||
@Field("price")
|
@Field("price")
|
||||||
private String price;
|
private BigDecimal price;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
package de.assecutor.votianlt.pages.add_job.service;
|
package de.assecutor.votianlt.pages.add_job.service;
|
||||||
|
|
||||||
|
import de.assecutor.votianlt.model.CargoItem;
|
||||||
import de.assecutor.votianlt.model.Job;
|
import de.assecutor.votianlt.model.Job;
|
||||||
import de.assecutor.votianlt.model.JobStatus;
|
import de.assecutor.votianlt.model.JobStatus;
|
||||||
|
import de.assecutor.votianlt.repository.CargoItemRepository;
|
||||||
import de.assecutor.votianlt.repository.JobRepository;
|
import de.assecutor.votianlt.repository.JobRepository;
|
||||||
import de.assecutor.votianlt.security.SecurityService;
|
import de.assecutor.votianlt.security.SecurityService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.bson.types.ObjectId;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@@ -19,7 +23,57 @@ import java.util.Optional;
|
|||||||
public class AddJobService {
|
public class AddJobService {
|
||||||
|
|
||||||
private final JobRepository jobRepository;
|
private final JobRepository jobRepository;
|
||||||
|
private final CargoItemRepository cargoItemRepository;
|
||||||
private final SecurityService securityService;
|
private final SecurityService securityService;
|
||||||
|
/**
|
||||||
|
* Speichert einen neuen Auftrag samt CargoItems (separat in cargo_items)
|
||||||
|
* @param job der Auftrag
|
||||||
|
* @param transientCargo zugehörige, noch nicht gespeicherte CargoItems aus der View
|
||||||
|
*/
|
||||||
|
public Job addJobWithCargo(Job job, List<CargoItem> transientCargo) {
|
||||||
|
try {
|
||||||
|
// Metadaten setzen
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
job.setCreatedAt(now);
|
||||||
|
job.setUpdatedAt(now);
|
||||||
|
job.setStatus(JobStatus.CREATED);
|
||||||
|
job.setCreatedBy(securityService.getCurrentUsername());
|
||||||
|
|
||||||
|
// Auftragsnummer generieren, falls nicht vorhanden
|
||||||
|
if (job.getJobNumber() == null || job.getJobNumber().isEmpty()) {
|
||||||
|
job.setJobNumber(generateJobNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auftrag speichern
|
||||||
|
Job savedJob = jobRepository.save(job);
|
||||||
|
|
||||||
|
// CargoItems separat mit Referenz auf Job speichern, IDs im Job verknüpfen
|
||||||
|
if (transientCargo != null && !transientCargo.isEmpty()) {
|
||||||
|
final ObjectId jobId = savedJob.getId();
|
||||||
|
List<CargoItem> itemsWithJob = transientCargo.stream().map(ci -> {
|
||||||
|
CargoItem copy = new CargoItem();
|
||||||
|
copy.setJobId(jobId);
|
||||||
|
copy.setDescription(ci.getDescription());
|
||||||
|
copy.setQuantity(ci.getQuantity());
|
||||||
|
copy.setWeightKg(ci.getWeightKg());
|
||||||
|
copy.setLengthMm(ci.getLengthMm());
|
||||||
|
copy.setWidthMm(ci.getWidthMm());
|
||||||
|
copy.setHeightMm(ci.getHeightMm());
|
||||||
|
return copy;
|
||||||
|
}).collect(java.util.stream.Collectors.toList());
|
||||||
|
List<CargoItem> savedItems = cargoItemRepository.saveAll(itemsWithJob);
|
||||||
|
List<ObjectId> cargoIds = savedItems.stream().map(CargoItem::getId).collect(java.util.stream.Collectors.toList());
|
||||||
|
savedJob = jobRepository.save(savedJob);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Auftrag erfolgreich gespeichert: {}", savedJob.getJobNumber());
|
||||||
|
return savedJob;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Fehler beim Speichern des Auftrags: {}", e.getMessage(), e);
|
||||||
|
throw new RuntimeException("Auftrag konnte nicht gespeichert werden: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Speichert einen neuen Auftrag in der MongoDB
|
* Speichert einen neuen Auftrag in der MongoDB
|
||||||
@@ -40,6 +94,7 @@ public class AddJobService {
|
|||||||
|
|
||||||
// Auftrag speichern
|
// Auftrag speichern
|
||||||
Job savedJob = jobRepository.save(job);
|
Job savedJob = jobRepository.save(job);
|
||||||
|
|
||||||
log.info("Auftrag erfolgreich gespeichert: {}", savedJob.getJobNumber());
|
log.info("Auftrag erfolgreich gespeichert: {}", savedJob.getJobNumber());
|
||||||
|
|
||||||
return savedJob;
|
return savedJob;
|
||||||
@@ -224,6 +279,13 @@ public class AddJobService {
|
|||||||
// Digital processing
|
// Digital processing
|
||||||
existingJob.setDigitalProcessing(formJob.isDigitalProcessing());
|
existingJob.setDigitalProcessing(formJob.isDigitalProcessing());
|
||||||
existingJob.setAppUser(formJob.getAppUser());
|
existingJob.setAppUser(formJob.getAppUser());
|
||||||
|
|
||||||
|
// Termine, Bemerkung, Aufgaben, Preis (Cargo separat persistiert)
|
||||||
|
existingJob.setPickupDate(formJob.getPickupDate());
|
||||||
|
existingJob.setDeliveryDate(formJob.getDeliveryDate());
|
||||||
|
existingJob.setRemark(formJob.getRemark());
|
||||||
|
existingJob.setTasks(formJob.getTasks());
|
||||||
|
existingJob.setPrice(formJob.getPrice());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -93,8 +93,6 @@ public class AddJobView extends Main {
|
|||||||
private DatePicker pickupDate;
|
private DatePicker pickupDate;
|
||||||
private DatePicker deliveryDate;
|
private DatePicker deliveryDate;
|
||||||
|
|
||||||
// TabSheet and Tab references for dynamic label updates
|
|
||||||
private TabSheet tabSheet;
|
|
||||||
private com.vaadin.flow.component.tabs.Tab addressesTab;
|
private com.vaadin.flow.component.tabs.Tab addressesTab;
|
||||||
private com.vaadin.flow.component.tabs.Tab appointmentsTab;
|
private com.vaadin.flow.component.tabs.Tab appointmentsTab;
|
||||||
private com.vaadin.flow.component.tabs.Tab cargoTab;
|
private com.vaadin.flow.component.tabs.Tab cargoTab;
|
||||||
@@ -102,8 +100,14 @@ public class AddJobView extends Main {
|
|||||||
|
|
||||||
// Submit button
|
// Submit button
|
||||||
private Button submitButton;
|
private Button submitButton;
|
||||||
|
// Transient transfer list for cargo items (not part of Job entity anymore)
|
||||||
|
private java.util.List<de.assecutor.votianlt.model.CargoItem> jobTransientCargo;
|
||||||
|
|
||||||
// Stage sections for drag and drop
|
// Stage sections for drag and drop
|
||||||
|
// Dynamic lists and additional controls
|
||||||
|
private VerticalLayout cargoList;
|
||||||
|
private VerticalLayout tasksList;
|
||||||
|
private TextArea remarkArea;
|
||||||
private VerticalLayout pickupSection;
|
private VerticalLayout pickupSection;
|
||||||
private VerticalLayout deliverySection;
|
private VerticalLayout deliverySection;
|
||||||
|
|
||||||
@@ -112,6 +116,7 @@ public class AddJobView extends Main {
|
|||||||
public AddJobView(AddJobService addJobService) {
|
public AddJobView(AddJobService addJobService) {
|
||||||
this.addJobService = addJobService;
|
this.addJobService = addJobService;
|
||||||
initializeComponents();
|
initializeComponents();
|
||||||
|
populateTestData(); // Pre-populate all required fields with test data
|
||||||
setupLayout();
|
setupLayout();
|
||||||
setupValidation();
|
setupValidation();
|
||||||
loadDraftIfExists();
|
loadDraftIfExists();
|
||||||
@@ -131,6 +136,7 @@ public class AddJobView extends Main {
|
|||||||
// Pickup address
|
// Pickup address
|
||||||
pickupCompany = new TextField("Firma");
|
pickupCompany = new TextField("Firma");
|
||||||
pickupCompany.setPlaceholder("z.B. IKEA, McDonald's, DHL...");
|
pickupCompany.setPlaceholder("z.B. IKEA, McDonald's, DHL...");
|
||||||
|
pickupCompany.setValue("Test Abholfirma GmbH");
|
||||||
addGooglePlacesAutocomplete(pickupCompany, 0); // Stage 0 für Pickup
|
addGooglePlacesAutocomplete(pickupCompany, 0); // Stage 0 für Pickup
|
||||||
pickupSalutation = new ComboBox<>("Anrede");
|
pickupSalutation = new ComboBox<>("Anrede");
|
||||||
pickupSalutation.setItems("Herr", "Frau", "Divers");
|
pickupSalutation.setItems("Herr", "Frau", "Divers");
|
||||||
@@ -162,6 +168,7 @@ public class AddJobView extends Main {
|
|||||||
// Delivery address
|
// Delivery address
|
||||||
deliveryCompany = new TextField("Firma");
|
deliveryCompany = new TextField("Firma");
|
||||||
deliveryCompany.setPlaceholder("z.B. EDEKA, Bauhaus, Amazon...");
|
deliveryCompany.setPlaceholder("z.B. EDEKA, Bauhaus, Amazon...");
|
||||||
|
deliveryCompany.setValue("Test Lieferfirma AG");
|
||||||
addGooglePlacesAutocomplete(deliveryCompany, 1); // Stage 1 für Delivery
|
addGooglePlacesAutocomplete(deliveryCompany, 1); // Stage 1 für Delivery
|
||||||
deliverySalutation = new ComboBox<>("Anrede");
|
deliverySalutation = new ComboBox<>("Anrede");
|
||||||
deliverySalutation.setItems("Herr", "Frau", "Divers");
|
deliverySalutation.setItems("Herr", "Frau", "Divers");
|
||||||
@@ -201,6 +208,14 @@ public class AddJobView extends Main {
|
|||||||
price.setPlaceholder("z.B. 150.00");
|
price.setPlaceholder("z.B. 150.00");
|
||||||
price.setRequiredIndicatorVisible(true);
|
price.setRequiredIndicatorVisible(true);
|
||||||
|
|
||||||
|
// Erzwinge Komma als Dezimaltrennzeichen: ersetze Punkt beim Tippen
|
||||||
|
price.addValueChangeListener(e -> {
|
||||||
|
String v = e.getValue();
|
||||||
|
if (v != null && v.contains(".")) {
|
||||||
|
String replaced = v.replace('.', ',');
|
||||||
|
if (!replaced.equals(v)) price.setValue(replaced);
|
||||||
|
}
|
||||||
|
});
|
||||||
// Date picker fields for appointments
|
// Date picker fields for appointments
|
||||||
pickupDate = new DatePicker("Datum");
|
pickupDate = new DatePicker("Datum");
|
||||||
pickupDate.setRequiredIndicatorVisible(true);
|
pickupDate.setRequiredIndicatorVisible(true);
|
||||||
@@ -212,6 +227,33 @@ public class AddJobView extends Main {
|
|||||||
submitButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
submitButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void populateTestData() {
|
||||||
|
// Populate pickup address required fields
|
||||||
|
pickupCompany.setValue("Firma 1");
|
||||||
|
pickupFirstName.setValue("Max");
|
||||||
|
pickupLastName.setValue("Mustermann");
|
||||||
|
pickupStreet.setValue("Musterstraße");
|
||||||
|
pickupHouseNumber.setValue("123");
|
||||||
|
pickupZip.setValue("20095");
|
||||||
|
pickupCity.setValue("Hamburg");
|
||||||
|
|
||||||
|
// Populate delivery address required fields
|
||||||
|
deliveryFirstName.setValue("Anna");
|
||||||
|
deliveryLastName.setValue("Beispiel");
|
||||||
|
deliveryStreet.setValue("Beispielweg");
|
||||||
|
deliveryHouseNumber.setValue("456");
|
||||||
|
deliveryZip.setValue("10115");
|
||||||
|
deliveryCity.setValue("Berlin");
|
||||||
|
|
||||||
|
// Populate price field
|
||||||
|
price.setValue("150.00");
|
||||||
|
|
||||||
|
// Populate date fields with current date + 1 day for pickup, +2 days for delivery
|
||||||
|
java.time.LocalDate today = java.time.LocalDate.now();
|
||||||
|
pickupDate.setValue(today.plusDays(1));
|
||||||
|
deliveryDate.setValue(today.plusDays(2));
|
||||||
|
}
|
||||||
|
|
||||||
private void setupLayout() {
|
private void setupLayout() {
|
||||||
setSizeFull();
|
setSizeFull();
|
||||||
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX,
|
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX,
|
||||||
@@ -221,18 +263,19 @@ public class AddJobView extends Main {
|
|||||||
add(new ViewToolbar("Neuen Auftrag anlegen"));
|
add(new ViewToolbar("Neuen Auftrag anlegen"));
|
||||||
|
|
||||||
// Create TabSheet for organizing the form
|
// Create TabSheet for organizing the form
|
||||||
tabSheet = new TabSheet();
|
// TabSheet and Tab references for dynamic label updates
|
||||||
|
TabSheet tabSheet = new TabSheet();
|
||||||
tabSheet.setSizeFull();
|
tabSheet.setSizeFull();
|
||||||
|
|
||||||
// Tab 1: Customer & Addresses
|
// Tab 1: Customer & Addresses
|
||||||
addressesTab = tabSheet.add("Auftraggeber & Adressen", createCustomerAndAddressesTab());
|
addressesTab = tabSheet.add("Auftraggeber & Adressen", createCustomerAndAddressesTab());
|
||||||
|
|
||||||
// Tab 2: Appointments & Processing
|
// Tab 2: Appointments & Processing
|
||||||
appointmentsTab = tabSheet.add("Termine & Verarbeitung", createAppointmentsAndProcessingTab());
|
appointmentsTab = tabSheet.add("Termine & Verarbeitung", createAppointmentsAndProcessingTab());
|
||||||
|
|
||||||
// Tab 3: Cargo & Tasks
|
// Tab 3: Cargo & Tasks
|
||||||
cargoTab = tabSheet.add("Ladung & Aufgaben", createCargoAndTasksTab());
|
cargoTab = tabSheet.add("Ladung & Aufgaben", createCargoAndTasksTab());
|
||||||
|
|
||||||
// Tab 4: Price & Submit
|
// Tab 4: Price & Submit
|
||||||
priceTab = tabSheet.add("Preis & Abschluss", createPriceAndSubmitTab());
|
priceTab = tabSheet.add("Preis & Abschluss", createPriceAndSubmitTab());
|
||||||
|
|
||||||
@@ -244,7 +287,7 @@ public class AddJobView extends Main {
|
|||||||
buttonLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
buttonLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
||||||
buttonLayout.setPadding(true);
|
buttonLayout.setPadding(true);
|
||||||
buttonLayout.add(submitButton);
|
buttonLayout.add(submitButton);
|
||||||
|
|
||||||
add(buttonLayout);
|
add(buttonLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,7 +393,7 @@ public class AddJobView extends Main {
|
|||||||
|
|
||||||
// Add cargo section
|
// Add cargo section
|
||||||
tabContent.add(createCargoSection());
|
tabContent.add(createCargoSection());
|
||||||
|
|
||||||
// Add tasks and notes section
|
// Add tasks and notes section
|
||||||
tabContent.add(createTasksAndNotesSection());
|
tabContent.add(createTasksAndNotesSection());
|
||||||
|
|
||||||
@@ -521,6 +564,31 @@ public class AddJobView extends Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setupValidation() {
|
private void setupValidation() {
|
||||||
|
// Bind pickup address fields with validation
|
||||||
|
binder.forField(pickupFirstName)
|
||||||
|
.asRequired("")
|
||||||
|
.bind(Job::getPickupFirstName, Job::setPickupFirstName);
|
||||||
|
|
||||||
|
binder.forField(pickupLastName)
|
||||||
|
.asRequired("")
|
||||||
|
.bind(Job::getPickupLastName, Job::setPickupLastName);
|
||||||
|
|
||||||
|
binder.forField(pickupStreet)
|
||||||
|
.asRequired("")
|
||||||
|
.bind(Job::getPickupStreet, Job::setPickupStreet);
|
||||||
|
|
||||||
|
binder.forField(pickupHouseNumber)
|
||||||
|
.asRequired("")
|
||||||
|
.bind(Job::getPickupHouseNumber, Job::setPickupHouseNumber);
|
||||||
|
|
||||||
|
binder.forField(pickupZip)
|
||||||
|
.asRequired("")
|
||||||
|
.bind(Job::getPickupZip, Job::setPickupZip);
|
||||||
|
|
||||||
|
binder.forField(pickupCity)
|
||||||
|
.asRequired("")
|
||||||
|
.bind(Job::getPickupCity, Job::setPickupCity);
|
||||||
|
|
||||||
// Bind delivery address fields with validation
|
// Bind delivery address fields with validation
|
||||||
binder.forField(deliveryFirstName)
|
binder.forField(deliveryFirstName)
|
||||||
.asRequired("")
|
.asRequired("")
|
||||||
@@ -546,15 +614,32 @@ public class AddJobView extends Main {
|
|||||||
.asRequired("")
|
.asRequired("")
|
||||||
.bind(Job::getDeliveryCity, Job::setDeliveryCity);
|
.bind(Job::getDeliveryCity, Job::setDeliveryCity);
|
||||||
|
|
||||||
// Bind price field with validation
|
// Bind price field: Komma-Zahlen in Punkt-Zahlen umsetzen, dann nach BigDecimal konvertieren
|
||||||
binder.forField(price)
|
binder.forField(price)
|
||||||
.asRequired("")
|
.withNullRepresentation("")
|
||||||
|
.asRequired("Preis erforderlich")
|
||||||
|
.withConverter(
|
||||||
|
(String s) -> {
|
||||||
|
if (s == null || s.trim().isEmpty()) return null;
|
||||||
|
String normalized = s.replace(" ", "").replace(".", "").replace(',', '.');
|
||||||
|
try { return new java.math.BigDecimal(normalized); }
|
||||||
|
catch (NumberFormatException ex) { throw new NumberFormatException("Ungültiger Betrag"); }
|
||||||
|
},
|
||||||
|
(java.math.BigDecimal bd) -> bd == null ? "" : bd.toString(),
|
||||||
|
"Ungültiger Betrag"
|
||||||
|
)
|
||||||
|
.withValidator(value -> value != null && value.compareTo(java.math.BigDecimal.ZERO) > 0,
|
||||||
|
"Der Preis muss größer als 0 sein")
|
||||||
.bind(Job::getPrice, Job::setPrice);
|
.bind(Job::getPrice, Job::setPrice);
|
||||||
|
|
||||||
// Bind date picker fields with validation (we'll need to add these to Job model later)
|
// Bind date picker fields with validation
|
||||||
// For now, we'll just set up the validation without binding to the model
|
binder.forField(pickupDate)
|
||||||
binder.forField(pickupDate).asRequired("");
|
.asRequired("")
|
||||||
binder.forField(deliveryDate).asRequired("");
|
.bind(Job::getPickupDate, Job::setPickupDate);
|
||||||
|
|
||||||
|
binder.forField(deliveryDate)
|
||||||
|
.asRequired("")
|
||||||
|
.bind(Job::getDeliveryDate, Job::setDeliveryDate);
|
||||||
|
|
||||||
// Bind optional fields without validation
|
// Bind optional fields without validation
|
||||||
binder.bind(customerSelection, Job::getCustomerSelection, Job::setCustomerSelection);
|
binder.bind(customerSelection, Job::getCustomerSelection, Job::setCustomerSelection);
|
||||||
@@ -578,7 +663,7 @@ public class AddJobView extends Main {
|
|||||||
|
|
||||||
// Trigger initial validation when view is displayed
|
// Trigger initial validation when view is displayed
|
||||||
triggerValidation();
|
triggerValidation();
|
||||||
|
|
||||||
// Update tab labels with initial validation state
|
// Update tab labels with initial validation state
|
||||||
updateTabLabels();
|
updateTabLabels();
|
||||||
}
|
}
|
||||||
@@ -685,12 +770,12 @@ public class AddJobView extends Main {
|
|||||||
boolean pickupErrors = isFieldEmpty(pickupFirstName) || isFieldEmpty(pickupLastName) ||
|
boolean pickupErrors = isFieldEmpty(pickupFirstName) || isFieldEmpty(pickupLastName) ||
|
||||||
isFieldEmpty(pickupStreet) || isFieldEmpty(pickupHouseNumber) ||
|
isFieldEmpty(pickupStreet) || isFieldEmpty(pickupHouseNumber) ||
|
||||||
isFieldEmpty(pickupZip) || isFieldEmpty(pickupCity);
|
isFieldEmpty(pickupZip) || isFieldEmpty(pickupCity);
|
||||||
|
|
||||||
// Check delivery address fields
|
// Check delivery address fields
|
||||||
boolean deliveryErrors = isFieldEmpty(deliveryFirstName) || isFieldEmpty(deliveryLastName) ||
|
boolean deliveryErrors = isFieldEmpty(deliveryFirstName) || isFieldEmpty(deliveryLastName) ||
|
||||||
isFieldEmpty(deliveryStreet) || isFieldEmpty(deliveryHouseNumber) ||
|
isFieldEmpty(deliveryStreet) || isFieldEmpty(deliveryHouseNumber) ||
|
||||||
isFieldEmpty(deliveryZip) || isFieldEmpty(deliveryCity);
|
isFieldEmpty(deliveryZip) || isFieldEmpty(deliveryCity);
|
||||||
|
|
||||||
return pickupErrors || deliveryErrors;
|
return pickupErrors || deliveryErrors;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -711,10 +796,58 @@ public class AddJobView extends Main {
|
|||||||
try {
|
try {
|
||||||
Job job = new Job();
|
Job job = new Job();
|
||||||
|
|
||||||
|
// Zusätzliche Felder, die nicht über den Binder gebunden sind, manuell setzen
|
||||||
|
job.setPickupDate(pickupDate.getValue());
|
||||||
|
job.setDeliveryDate(deliveryDate.getValue());
|
||||||
|
if (remarkArea != null) job.setRemark(remarkArea.getValue());
|
||||||
|
if (tasksList != null) {
|
||||||
|
java.util.List<String> tasks = new java.util.ArrayList<>();
|
||||||
|
tasksList.getChildren().forEach(comp -> {
|
||||||
|
if (comp instanceof com.vaadin.flow.component.orderedlayout.HorizontalLayout row) {
|
||||||
|
row.getChildren().filter(c -> c instanceof TextField).forEach(tf -> {
|
||||||
|
String v = ((TextField) tf).getValue();
|
||||||
|
if (v != null && !v.trim().isEmpty()) tasks.add(v.trim());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
job.setTasks(tasks);
|
||||||
|
}
|
||||||
|
if (cargoList != null) {
|
||||||
|
java.util.List<de.assecutor.votianlt.model.CargoItem> items = new java.util.ArrayList<>();
|
||||||
|
cargoList.getChildren().forEach(comp -> {
|
||||||
|
if (comp instanceof com.vaadin.flow.component.orderedlayout.HorizontalLayout row) {
|
||||||
|
String desc = null; Integer qty = null; Double weight = null, len = null, wid = null, hei = null;
|
||||||
|
for (com.vaadin.flow.component.Component c : row.getChildren().toList()) {
|
||||||
|
if (c instanceof TextField tf && tf.getLabel() != null && tf.getLabel().contains("Beschreibung")) desc = tf.getValue();
|
||||||
|
if (c instanceof IntegerField ifld) qty = ifld.getValue();
|
||||||
|
if (c instanceof NumberField nf) {
|
||||||
|
String label = nf.getLabel();
|
||||||
|
if (label != null) {
|
||||||
|
switch (label) {
|
||||||
|
case "Gewicht" -> weight = nf.getValue();
|
||||||
|
case "Länge" -> len = nf.getValue();
|
||||||
|
case "Breite" -> wid = nf.getValue();
|
||||||
|
case "Höhe" -> hei = nf.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (desc != null || qty != null || weight != null || len != null || wid != null || hei != null) {
|
||||||
|
de.assecutor.votianlt.model.CargoItem ci = new de.assecutor.votianlt.model.CargoItem();
|
||||||
|
ci.setDescription(desc); ci.setQuantity(qty); ci.setWeightKg(weight);
|
||||||
|
ci.setLengthMm(len); ci.setWidthMm(wid); ci.setHeightMm(hei);
|
||||||
|
items.add(ci);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// temporär im Job-Objekt als Transfertyp beilegen (über transient Helper)
|
||||||
|
jobTransientCargo = items;
|
||||||
|
}
|
||||||
|
|
||||||
// Validate all required fields using the binder
|
// Validate all required fields using the binder
|
||||||
if (binder.writeBeanIfValid(job)) {
|
if (binder.writeBeanIfValid(job)) {
|
||||||
// All validations passed, save the job
|
// All validations passed, save the job with cargo items
|
||||||
Job savedJob = addJobService.addJob(job);
|
Job savedJob = addJobService.addJobWithCargo(job, jobTransientCargo);
|
||||||
|
|
||||||
// Erfolgsmeldung anzeigen
|
// Erfolgsmeldung anzeigen
|
||||||
Notification successNotification = Notification.show(
|
Notification successNotification = Notification.show(
|
||||||
@@ -892,7 +1025,7 @@ public class AddJobView extends Main {
|
|||||||
|
|
||||||
wrapper.add(new H3("Ladung"));
|
wrapper.add(new H3("Ladung"));
|
||||||
|
|
||||||
VerticalLayout cargoList = new VerticalLayout();
|
cargoList = new VerticalLayout();
|
||||||
cargoList.setPadding(false);
|
cargoList.setPadding(false);
|
||||||
cargoList.setSpacing(true);
|
cargoList.setSpacing(true);
|
||||||
cargoArea.add(cargoList);
|
cargoArea.add(cargoList);
|
||||||
@@ -996,7 +1129,7 @@ public class AddJobView extends Main {
|
|||||||
content.add(tasksTitle);
|
content.add(tasksTitle);
|
||||||
|
|
||||||
// Dynamische Aufgabenliste
|
// Dynamische Aufgabenliste
|
||||||
VerticalLayout tasksList = new VerticalLayout();
|
tasksList = new VerticalLayout();
|
||||||
tasksList.setPadding(false);
|
tasksList.setPadding(false);
|
||||||
tasksList.setSpacing(true);
|
tasksList.setSpacing(true);
|
||||||
|
|
||||||
@@ -1030,11 +1163,11 @@ public class AddJobView extends Main {
|
|||||||
// Bemerkung
|
// Bemerkung
|
||||||
H3 remarksTitle = new H3("Bemerkung");
|
H3 remarksTitle = new H3("Bemerkung");
|
||||||
remarksTitle.getStyle().set("margin", "0");
|
remarksTitle.getStyle().set("margin", "0");
|
||||||
TextArea remark = new TextArea();
|
remarkArea = new TextArea();
|
||||||
remark.setPlaceholder("z.B. rückwärtigen Liefereingang benutzen o. ä.");
|
remarkArea.setPlaceholder("z.B. rückwärtigen Liefereingang benutzen o. ä.");
|
||||||
remark.setWidthFull();
|
remarkArea.setWidthFull();
|
||||||
remark.setMinHeight("180px");
|
remarkArea.setMinHeight("180px");
|
||||||
content.add(remarksTitle, remark);
|
content.add(remarksTitle, remarkArea);
|
||||||
|
|
||||||
wrapper.add(content);
|
wrapper.add(content);
|
||||||
return wrapper;
|
return wrapper;
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package de.assecutor.votianlt.repository;
|
||||||
|
|
||||||
|
import de.assecutor.votianlt.model.CargoItem;
|
||||||
|
import org.bson.types.ObjectId;
|
||||||
|
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface CargoItemRepository extends MongoRepository<CargoItem, ObjectId> {
|
||||||
|
List<CargoItem> findByJobId(ObjectId jobId);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -47,8 +47,8 @@ public class SecurityConfig extends VaadinWebSecurity {
|
|||||||
// Dies fügt automatisch .anyRequest().authenticated() hinzu
|
// Dies fügt automatisch .anyRequest().authenticated() hinzu
|
||||||
super.configure(http);
|
super.configure(http);
|
||||||
|
|
||||||
// Setze die Login-View
|
// Setze die Login-View (per Pfad, um Router-Resolution-Probleme zu vermeiden)
|
||||||
setLoginView(http, LoginView.class);
|
setLoginView(http, "/login");
|
||||||
|
|
||||||
// Logout-Konfiguration
|
// Logout-Konfiguration
|
||||||
http.logout(logout -> logout
|
http.logout(logout -> logout
|
||||||
|
|||||||
Reference in New Issue
Block a user