Erweiterungen
This commit is contained in:
@@ -5,6 +5,7 @@ import org.bson.types.ObjectId;
|
|||||||
import org.springframework.data.annotation.Id;
|
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 org.springframework.data.annotation.Transient;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@@ -71,9 +72,6 @@ public class Job {
|
|||||||
@Field("pickup_city")
|
@Field("pickup_city")
|
||||||
private String pickupCity;
|
private String pickupCity;
|
||||||
|
|
||||||
@Field("save_pickup_address")
|
|
||||||
private boolean savePickupAddress;
|
|
||||||
|
|
||||||
// Lieferadresse
|
// Lieferadresse
|
||||||
@Field("delivery_company")
|
@Field("delivery_company")
|
||||||
private String deliveryCompany;
|
private String deliveryCompany;
|
||||||
@@ -105,9 +103,6 @@ public class Job {
|
|||||||
@Field("delivery_city")
|
@Field("delivery_city")
|
||||||
private String deliveryCity;
|
private String deliveryCity;
|
||||||
|
|
||||||
@Field("save_delivery_address")
|
|
||||||
private boolean saveDeliveryAddress;
|
|
||||||
|
|
||||||
// Digitale Abwicklung per App
|
// Digitale Abwicklung per App
|
||||||
@Field("digital_processing")
|
@Field("digital_processing")
|
||||||
private boolean digitalProcessing;
|
private boolean digitalProcessing;
|
||||||
@@ -126,10 +121,6 @@ public class Job {
|
|||||||
@Field("remark")
|
@Field("remark")
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
// Aufgaben
|
|
||||||
@Field("tasks")
|
|
||||||
private List<String> tasks;
|
|
||||||
|
|
||||||
// Preis (netto)
|
// Preis (netto)
|
||||||
@Field("price")
|
@Field("price")
|
||||||
private BigDecimal price;
|
private BigDecimal price;
|
||||||
|
|||||||
25
src/main/java/de/assecutor/votianlt/model/TaskEntry.java
Normal file
25
src/main/java/de/assecutor/votianlt/model/TaskEntry.java
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
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 = "tasks")
|
||||||
|
public class TaskEntry {
|
||||||
|
@Id
|
||||||
|
private ObjectId id;
|
||||||
|
|
||||||
|
@Field("job_id")
|
||||||
|
private ObjectId jobId;
|
||||||
|
|
||||||
|
@Field("text")
|
||||||
|
private String text;
|
||||||
|
}
|
||||||
|
|
||||||
Binary file not shown.
@@ -3,9 +3,12 @@ package de.assecutor.votianlt.pages.add_job.service;
|
|||||||
import de.assecutor.votianlt.model.CargoItem;
|
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.model.TaskEntry;
|
||||||
import de.assecutor.votianlt.repository.JobRepository;
|
import de.assecutor.votianlt.repository.JobRepository;
|
||||||
|
import de.assecutor.votianlt.repository.TaskRepository;
|
||||||
import de.assecutor.votianlt.security.SecurityService;
|
import de.assecutor.votianlt.security.SecurityService;
|
||||||
|
import de.assecutor.votianlt.repository.CargoItemRepository;
|
||||||
|
import java.util.Objects;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.bson.types.ObjectId;
|
import org.bson.types.ObjectId;
|
||||||
@@ -15,22 +18,22 @@ 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
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class AddJobService {
|
public class AddJobService {
|
||||||
|
|
||||||
private final JobRepository jobRepository;
|
|
||||||
private final CargoItemRepository cargoItemRepository;
|
private final CargoItemRepository cargoItemRepository;
|
||||||
|
private final JobRepository jobRepository;
|
||||||
|
private final TaskRepository taskRepository;
|
||||||
private final SecurityService securityService;
|
private final SecurityService securityService;
|
||||||
/**
|
/**
|
||||||
* Speichert einen neuen Auftrag samt CargoItems (separat in cargo_items)
|
* Speichert einen neuen Auftrag samt CargoItems und Tasks
|
||||||
* @param job der Auftrag
|
* @param job der Auftrag
|
||||||
* @param transientCargo zugehörige, noch nicht gespeicherte CargoItems aus der View
|
* @param transientCargo zugehörige, noch nicht gespeicherte CargoItems aus der View
|
||||||
*/
|
*/
|
||||||
public Job addJobWithCargo(Job job, List<CargoItem> transientCargo) {
|
public Job addJobWithCargo(Job job, List<CargoItem> transientCargo, List<TaskEntry> transientTasks) {
|
||||||
try {
|
try {
|
||||||
// Metadaten setzen
|
// Metadaten setzen
|
||||||
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime now = LocalDateTime.now();
|
||||||
@@ -46,23 +49,42 @@ public class AddJobService {
|
|||||||
|
|
||||||
// Auftrag speichern
|
// Auftrag speichern
|
||||||
Job savedJob = jobRepository.save(job);
|
Job savedJob = jobRepository.save(job);
|
||||||
|
final ObjectId jobId = savedJob.getId();
|
||||||
|
boolean modified = false;
|
||||||
|
|
||||||
// CargoItems separat mit Referenz auf Job speichern, IDs im Job verknüpfen
|
// CargoItems separat mit Referenz auf Job speichern, IDs im Job verknüpfen
|
||||||
if (transientCargo != null && !transientCargo.isEmpty()) {
|
if (transientCargo != null && !transientCargo.isEmpty()) {
|
||||||
final ObjectId jobId = savedJob.getId();
|
List<CargoItem> itemsWithJob = transientCargo.stream()
|
||||||
List<CargoItem> itemsWithJob = transientCargo.stream().map(ci -> {
|
.filter(Objects::nonNull)
|
||||||
CargoItem copy = new CargoItem();
|
.filter(ci -> ci.getDescription() != null && !ci.getDescription().isBlank())
|
||||||
copy.setJobId(jobId);
|
.map(ci -> {
|
||||||
copy.setDescription(ci.getDescription());
|
CargoItem copy = new CargoItem();
|
||||||
copy.setQuantity(ci.getQuantity());
|
copy.setJobId(jobId);
|
||||||
copy.setWeightKg(ci.getWeightKg());
|
copy.setDescription(ci.getDescription());
|
||||||
copy.setLengthMm(ci.getLengthMm());
|
copy.setQuantity(ci.getQuantity());
|
||||||
copy.setWidthMm(ci.getWidthMm());
|
copy.setWeightKg(ci.getWeightKg());
|
||||||
copy.setHeightMm(ci.getHeightMm());
|
copy.setLengthMm(ci.getLengthMm());
|
||||||
return copy;
|
copy.setWidthMm(ci.getWidthMm());
|
||||||
}).collect(java.util.stream.Collectors.toList());
|
copy.setHeightMm(ci.getHeightMm());
|
||||||
List<CargoItem> savedItems = cargoItemRepository.saveAll(itemsWithJob);
|
return copy;
|
||||||
List<ObjectId> cargoIds = savedItems.stream().map(CargoItem::getId).collect(java.util.stream.Collectors.toList());
|
}).toList();
|
||||||
|
var savedItems = cargoItemRepository.saveAll(itemsWithJob);
|
||||||
|
var cargoIds = savedItems.stream().map(CargoItem::getId).toList();
|
||||||
|
modified = true;
|
||||||
|
// Tasks separat speichern und referenzieren
|
||||||
|
if (transientTasks != null && !transientTasks.isEmpty()) {
|
||||||
|
var prepared = transientTasks.stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.filter(te -> te.getText() != null && !te.getText().isBlank())
|
||||||
|
.peek(te -> te.setJobId(jobId))
|
||||||
|
.toList();
|
||||||
|
var savedTasks = taskRepository.saveAll(prepared);
|
||||||
|
var taskIds = savedTasks.stream().map(de.assecutor.votianlt.model.TaskEntry::getId).toList();
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modified) {
|
||||||
savedJob = jobRepository.save(savedJob);
|
savedJob = jobRepository.save(savedJob);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,36 +97,6 @@ public class AddJobService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Speichert einen neuen Auftrag in der MongoDB
|
|
||||||
*/
|
|
||||||
public Job addJob(Job job) {
|
|
||||||
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);
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aktualisiert einen bestehenden Auftrag
|
* Aktualisiert einen bestehenden Auftrag
|
||||||
*/
|
*/
|
||||||
@@ -206,7 +198,7 @@ public class AddJobService {
|
|||||||
/**
|
/**
|
||||||
* Speichert einen Auftrag als Entwurf (für automatisches Speichern)
|
* Speichert einen Auftrag als Entwurf (für automatisches Speichern)
|
||||||
*/
|
*/
|
||||||
public Job saveDraft(Job job) {
|
public void saveDraft(Job job) {
|
||||||
try {
|
try {
|
||||||
// Prüfen ob bereits ein Entwurf für diesen Benutzer existiert
|
// Prüfen ob bereits ein Entwurf für diesen Benutzer existiert
|
||||||
String currentUser = securityService.getCurrentUsername();
|
String currentUser = securityService.getCurrentUsername();
|
||||||
@@ -215,7 +207,7 @@ public class AddJobService {
|
|||||||
Job draftJob;
|
Job draftJob;
|
||||||
if (!existingDrafts.isEmpty()) {
|
if (!existingDrafts.isEmpty()) {
|
||||||
// Bestehenden Entwurf aktualisieren
|
// Bestehenden Entwurf aktualisieren
|
||||||
draftJob = existingDrafts.get(0);
|
draftJob = existingDrafts.getFirst();
|
||||||
updateJobFromForm(draftJob, job);
|
updateJobFromForm(draftJob, job);
|
||||||
draftJob.setUpdatedAt(LocalDateTime.now());
|
draftJob.setUpdatedAt(LocalDateTime.now());
|
||||||
} else {
|
} else {
|
||||||
@@ -236,8 +228,6 @@ public class AddJobService {
|
|||||||
Job savedDraft = jobRepository.save(draftJob);
|
Job savedDraft = jobRepository.save(draftJob);
|
||||||
log.info("Entwurf automatisch gespeichert für Benutzer: {}", currentUser);
|
log.info("Entwurf automatisch gespeichert für Benutzer: {}", currentUser);
|
||||||
|
|
||||||
return savedDraft;
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Fehler beim Speichern des Entwurfs: {}", e.getMessage(), e);
|
log.error("Fehler beim Speichern des Entwurfs: {}", e.getMessage(), e);
|
||||||
throw new RuntimeException("Entwurf konnte nicht gespeichert werden: " + e.getMessage());
|
throw new RuntimeException("Entwurf konnte nicht gespeichert werden: " + e.getMessage());
|
||||||
@@ -261,7 +251,6 @@ public class AddJobService {
|
|||||||
existingJob.setPickupAddressAddition(formJob.getPickupAddressAddition());
|
existingJob.setPickupAddressAddition(formJob.getPickupAddressAddition());
|
||||||
existingJob.setPickupZip(formJob.getPickupZip());
|
existingJob.setPickupZip(formJob.getPickupZip());
|
||||||
existingJob.setPickupCity(formJob.getPickupCity());
|
existingJob.setPickupCity(formJob.getPickupCity());
|
||||||
existingJob.setSavePickupAddress(formJob.isSavePickupAddress());
|
|
||||||
|
|
||||||
// Delivery address
|
// Delivery address
|
||||||
existingJob.setDeliveryCompany(formJob.getDeliveryCompany());
|
existingJob.setDeliveryCompany(formJob.getDeliveryCompany());
|
||||||
@@ -274,7 +263,6 @@ public class AddJobService {
|
|||||||
existingJob.setDeliveryAddressAddition(formJob.getDeliveryAddressAddition());
|
existingJob.setDeliveryAddressAddition(formJob.getDeliveryAddressAddition());
|
||||||
existingJob.setDeliveryZip(formJob.getDeliveryZip());
|
existingJob.setDeliveryZip(formJob.getDeliveryZip());
|
||||||
existingJob.setDeliveryCity(formJob.getDeliveryCity());
|
existingJob.setDeliveryCity(formJob.getDeliveryCity());
|
||||||
existingJob.setSaveDeliveryAddress(formJob.isSaveDeliveryAddress());
|
|
||||||
|
|
||||||
// Digital processing
|
// Digital processing
|
||||||
existingJob.setDigitalProcessing(formJob.isDigitalProcessing());
|
existingJob.setDigitalProcessing(formJob.isDigitalProcessing());
|
||||||
@@ -284,7 +272,6 @@ public class AddJobService {
|
|||||||
existingJob.setPickupDate(formJob.getPickupDate());
|
existingJob.setPickupDate(formJob.getPickupDate());
|
||||||
existingJob.setDeliveryDate(formJob.getDeliveryDate());
|
existingJob.setDeliveryDate(formJob.getDeliveryDate());
|
||||||
existingJob.setRemark(formJob.getRemark());
|
existingJob.setRemark(formJob.getRemark());
|
||||||
existingJob.setTasks(formJob.getTasks());
|
|
||||||
existingJob.setPrice(formJob.getPrice());
|
existingJob.setPrice(formJob.getPrice());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,15 +280,6 @@ public class AddJobService {
|
|||||||
*/
|
*/
|
||||||
public Optional<Job> findCurrentDraft(String username) {
|
public Optional<Job> findCurrentDraft(String username) {
|
||||||
List<Job> drafts = jobRepository.findByCreatedByAndIsDraftTrue(username);
|
List<Job> drafts = jobRepository.findByCreatedByAndIsDraftTrue(username);
|
||||||
return drafts.isEmpty() ? Optional.empty() : Optional.of(drafts.get(0));
|
return drafts.isEmpty() ? Optional.empty() : Optional.of(drafts.getFirst());
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Löscht alle Entwürfe eines Benutzers
|
|
||||||
*/
|
|
||||||
public void deleteUserDrafts(String username) {
|
|
||||||
List<Job> drafts = jobRepository.findByCreatedByAndIsDraftTrue(username);
|
|
||||||
jobRepository.deleteAll(drafts);
|
|
||||||
log.info("Entwürfe für Benutzer {} gelöscht", username);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,11 +33,16 @@ import com.vaadin.flow.router.PageTitle;
|
|||||||
import com.vaadin.flow.router.Route;
|
import com.vaadin.flow.router.Route;
|
||||||
import com.vaadin.flow.theme.lumo.LumoUtility;
|
import com.vaadin.flow.theme.lumo.LumoUtility;
|
||||||
import de.assecutor.votianlt.model.Job;
|
import de.assecutor.votianlt.model.Job;
|
||||||
|
import de.assecutor.votianlt.model.TaskEntry;
|
||||||
import de.assecutor.votianlt.pages.add_job.service.AddJobService;
|
import de.assecutor.votianlt.pages.add_job.service.AddJobService;
|
||||||
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
|
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
|
||||||
|
|
||||||
import jakarta.annotation.security.RolesAllowed;
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import de.assecutor.votianlt.model.CargoItem;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.Objects;
|
||||||
|
import de.assecutor.votianlt.model.TaskEntry;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@Route(value = "add_job", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
|
@Route(value = "add_job", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
|
||||||
@@ -100,11 +105,16 @@ 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;
|
|
||||||
|
|
||||||
|
// Backing list for cargo items to mirror UI rows
|
||||||
|
private final List<CargoItem> cargoItemsState = new ArrayList<>();
|
||||||
// Stage sections for drag and drop
|
// Stage sections for drag and drop
|
||||||
|
// Backing list for tasks to mirror UI rows
|
||||||
|
private final List<TaskEntry> tasksState = new ArrayList<>();
|
||||||
// Dynamic lists and additional controls
|
// Dynamic lists and additional controls
|
||||||
|
// Cargo section UI refs for error highlighting
|
||||||
|
private VerticalLayout cargoAreaContainer;
|
||||||
|
private Span cargoError;
|
||||||
private VerticalLayout cargoList;
|
private VerticalLayout cargoList;
|
||||||
private VerticalLayout tasksList;
|
private VerticalLayout tasksList;
|
||||||
private TextArea remarkArea;
|
private TextArea remarkArea;
|
||||||
@@ -647,13 +657,11 @@ public class AddJobView extends Main {
|
|||||||
binder.bind(pickupSalutation, Job::getPickupSalutation, Job::setPickupSalutation);
|
binder.bind(pickupSalutation, Job::getPickupSalutation, Job::setPickupSalutation);
|
||||||
binder.bind(pickupPhone, Job::getPickupPhone, Job::setPickupPhone);
|
binder.bind(pickupPhone, Job::getPickupPhone, Job::setPickupPhone);
|
||||||
binder.bind(pickupAddressAddition, Job::getPickupAddressAddition, Job::setPickupAddressAddition);
|
binder.bind(pickupAddressAddition, Job::getPickupAddressAddition, Job::setPickupAddressAddition);
|
||||||
binder.bind(savePickupAddress, Job::isSavePickupAddress, Job::setSavePickupAddress);
|
|
||||||
|
|
||||||
binder.bind(deliveryCompany, Job::getDeliveryCompany, Job::setDeliveryCompany);
|
binder.bind(deliveryCompany, Job::getDeliveryCompany, Job::setDeliveryCompany);
|
||||||
binder.bind(deliverySalutation, Job::getDeliverySalutation, Job::setDeliverySalutation);
|
binder.bind(deliverySalutation, Job::getDeliverySalutation, Job::setDeliverySalutation);
|
||||||
binder.bind(deliveryPhone, Job::getDeliveryPhone, Job::setDeliveryPhone);
|
binder.bind(deliveryPhone, Job::getDeliveryPhone, Job::setDeliveryPhone);
|
||||||
binder.bind(deliveryAddressAddition, Job::getDeliveryAddressAddition, Job::setDeliveryAddressAddition);
|
binder.bind(deliveryAddressAddition, Job::getDeliveryAddressAddition, Job::setDeliveryAddressAddition);
|
||||||
binder.bind(saveDeliveryAddress, Job::isSaveDeliveryAddress, Job::setSaveDeliveryAddress);
|
|
||||||
|
|
||||||
binder.bind(digitalProcessing, Job::isDigitalProcessing, Job::setDigitalProcessing);
|
binder.bind(digitalProcessing, Job::isDigitalProcessing, Job::setDigitalProcessing);
|
||||||
binder.bind(appUser, Job::getAppUser, Job::setAppUser);
|
binder.bind(appUser, Job::getAppUser, Job::setAppUser);
|
||||||
@@ -800,54 +808,33 @@ public class AddJobView extends Main {
|
|||||||
job.setPickupDate(pickupDate.getValue());
|
job.setPickupDate(pickupDate.getValue());
|
||||||
job.setDeliveryDate(deliveryDate.getValue());
|
job.setDeliveryDate(deliveryDate.getValue());
|
||||||
if (remarkArea != null) job.setRemark(remarkArea.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 with cargo items
|
// Ensure at least one cargo item is provided (tasks may be empty)
|
||||||
Job savedJob = addJobService.addJobWithCargo(job, jobTransientCargo);
|
// Definition: Ein Cargo-Item gilt nur als gefüllt, wenn eine Beschreibung vorhanden ist
|
||||||
|
List<CargoItem> cargoFilled = cargoItemsState.stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.filter(ci -> ci.getDescription() != null && !ci.getDescription().isBlank())
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (cargoFilled.isEmpty()) {
|
||||||
|
Notification errorNotification = Notification.show(
|
||||||
|
"Bitte fügen Sie mindestens eine Ladungszeile hinzu.");
|
||||||
|
errorNotification.setDuration(5000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// toggle cargo error highlight
|
||||||
|
boolean hasCargo = !cargoFilled.isEmpty();
|
||||||
|
cargoError.setVisible(!hasCargo);
|
||||||
|
if (!hasCargo) {
|
||||||
|
cargoAreaContainer.getStyle().set("border", "1px solid var(--lumo-error-color-50pct)");
|
||||||
|
} else {
|
||||||
|
cargoAreaContainer.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// All validations passed, save the job with cargo items and tasks (tasks may be empty)
|
||||||
|
Job savedJob = addJobService.addJobWithCargo(job, cargoFilled, tasksState);
|
||||||
|
|
||||||
// Erfolgsmeldung anzeigen
|
// Erfolgsmeldung anzeigen
|
||||||
Notification successNotification = Notification.show(
|
Notification successNotification = Notification.show(
|
||||||
@@ -865,6 +852,9 @@ public class AddJobView extends Main {
|
|||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Other errors
|
// Other errors
|
||||||
|
// Reset cargo error
|
||||||
|
if (cargoError != null) cargoError.setVisible(false);
|
||||||
|
if (cargoAreaContainer != null) cargoAreaContainer.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
||||||
Notification errorNotification = Notification.show(
|
Notification errorNotification = Notification.show(
|
||||||
"Fehler beim Erstellen des Auftrags: " + e.getMessage());
|
"Fehler beim Erstellen des Auftrags: " + e.getMessage());
|
||||||
errorNotification.setDuration(5000);
|
errorNotification.setDuration(5000);
|
||||||
@@ -872,11 +862,19 @@ public class AddJobView extends Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
cargoItemsState.clear();
|
||||||
|
cargoList.removeAll();
|
||||||
* Setzt alle Formularfelder zurück
|
* Setzt alle Formularfelder zurück
|
||||||
*/
|
*/
|
||||||
private void clearForm() {
|
private void clearForm() {
|
||||||
|
// Reset cargo state and UI
|
||||||
|
cargoItemsState.clear();
|
||||||
|
if (cargoList != null) cargoList.removeAll();
|
||||||
// Reset binder to clear validation state
|
// Reset binder to clear validation state
|
||||||
binder.readBean(new Job());
|
binder.readBean(new Job());
|
||||||
|
// Reset tasks state and UI
|
||||||
|
tasksState.clear();
|
||||||
|
if (tasksList != null) tasksList.removeAll();
|
||||||
|
|
||||||
// Customer selection
|
// Customer selection
|
||||||
customerSelection.clear();
|
customerSelection.clear();
|
||||||
@@ -937,7 +935,6 @@ public class AddJobView extends Main {
|
|||||||
job.setPickupAddressAddition(pickupAddressAddition.getValue());
|
job.setPickupAddressAddition(pickupAddressAddition.getValue());
|
||||||
job.setPickupZip(pickupZip.getValue());
|
job.setPickupZip(pickupZip.getValue());
|
||||||
job.setPickupCity(pickupCity.getValue());
|
job.setPickupCity(pickupCity.getValue());
|
||||||
job.setSavePickupAddress(savePickupAddress.getValue());
|
|
||||||
|
|
||||||
// Delivery address
|
// Delivery address
|
||||||
job.setDeliveryCompany(deliveryCompany.getValue());
|
job.setDeliveryCompany(deliveryCompany.getValue());
|
||||||
@@ -950,7 +947,6 @@ public class AddJobView extends Main {
|
|||||||
job.setDeliveryAddressAddition(deliveryAddressAddition.getValue());
|
job.setDeliveryAddressAddition(deliveryAddressAddition.getValue());
|
||||||
job.setDeliveryZip(deliveryZip.getValue());
|
job.setDeliveryZip(deliveryZip.getValue());
|
||||||
job.setDeliveryCity(deliveryCity.getValue());
|
job.setDeliveryCity(deliveryCity.getValue());
|
||||||
job.setSaveDeliveryAddress(saveDeliveryAddress.getValue());
|
|
||||||
|
|
||||||
// Digital processing
|
// Digital processing
|
||||||
job.setDigitalProcessing(digitalProcessing.getValue());
|
job.setDigitalProcessing(digitalProcessing.getValue());
|
||||||
@@ -1015,28 +1011,32 @@ public class AddJobView extends Main {
|
|||||||
wrapper.setWidthFull();
|
wrapper.setWidthFull();
|
||||||
wrapper.setSpacing(true);
|
wrapper.setSpacing(true);
|
||||||
|
|
||||||
VerticalLayout cargoArea = new VerticalLayout();
|
cargoAreaContainer = new VerticalLayout();
|
||||||
cargoArea.setWidthFull();
|
cargoAreaContainer.setWidthFull();
|
||||||
cargoArea.setSpacing(true);
|
cargoAreaContainer.setSpacing(true);
|
||||||
cargoArea.getStyle().set("background", "var(--lumo-base-color)");
|
cargoAreaContainer.getStyle().set("background", "var(--lumo-base-color)");
|
||||||
cargoArea.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
cargoAreaContainer.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
||||||
cargoArea.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
|
cargoAreaContainer.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
|
||||||
cargoArea.getStyle().set("padding", "var(--lumo-space-m)");
|
cargoAreaContainer.getStyle().set("padding", "var(--lumo-space-m)");
|
||||||
|
|
||||||
wrapper.add(new H3("Ladung"));
|
H3 cargoTitle = new H3("Ladung");
|
||||||
|
cargoError = new Span("Bitte fügen Sie mindestens eine Ladungszeile hinzu.");
|
||||||
|
cargoError.getStyle().set("color", "var(--lumo-error-text-color)");
|
||||||
|
cargoError.getStyle().set("font-size", "var(--lumo-font-size-s)");
|
||||||
|
cargoError.setVisible(false);
|
||||||
|
|
||||||
|
wrapper.add(cargoTitle);
|
||||||
|
|
||||||
cargoList = new VerticalLayout();
|
cargoList = new VerticalLayout();
|
||||||
cargoList.setPadding(false);
|
cargoList.setPadding(false);
|
||||||
cargoList.setSpacing(true);
|
cargoList.setSpacing(true);
|
||||||
cargoArea.add(cargoList);
|
cargoAreaContainer.add(cargoError, cargoList);
|
||||||
|
|
||||||
java.util.function.BiConsumer<String, java.util.function.Consumer<HorizontalLayout>> addCargoRow = (iconName, afterCreate) -> {
|
java.util.function.BiConsumer<String, java.util.function.Consumer<HorizontalLayout>> addCargoRow = (iconName, afterCreate) -> {
|
||||||
HorizontalLayout row = new HorizontalLayout();
|
HorizontalLayout row = new HorizontalLayout();
|
||||||
row.setWidthFull();
|
row.setWidthFull();
|
||||||
row.setAlignItems(FlexComponent.Alignment.END);
|
row.setAlignItems(FlexComponent.Alignment.END);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ComboBox<String> desc = new ComboBox<>("Beschreibung");
|
ComboBox<String> desc = new ComboBox<>("Beschreibung");
|
||||||
desc.setItems("Europalette", "Einwegpalette", "Düsseldorfer-Palette", "Gitterboxpalette", "Gitterwagen", "Paket");
|
desc.setItems("Europalette", "Einwegpalette", "Düsseldorfer-Palette", "Gitterboxpalette", "Gitterwagen", "Paket");
|
||||||
desc.setAllowCustomValue(true);
|
desc.setAllowCustomValue(true);
|
||||||
@@ -1064,10 +1064,43 @@ public class AddJobView extends Main {
|
|||||||
|
|
||||||
Button remove = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
|
Button remove = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
|
||||||
remove.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
|
remove.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
|
||||||
remove.addClickListener(e -> cargoList.remove(row));
|
remove.addClickListener(e -> {
|
||||||
|
int idx = cargoList.getChildren().toList().indexOf(row);
|
||||||
|
if (idx >= 0 && idx < cargoItemsState.size()) {
|
||||||
|
cargoItemsState.remove(idx);
|
||||||
|
}
|
||||||
|
cargoList.remove(row);
|
||||||
|
});
|
||||||
|
|
||||||
row.add(desc, qty, weight, len, wid, hei, remove);
|
row.add(desc, qty, weight, len, wid, hei, remove);
|
||||||
cargoList.add(row);
|
cargoList.add(row);
|
||||||
|
|
||||||
|
// Ensure backing list is in sync: add new item and bind change listeners
|
||||||
|
CargoItem item = new CargoItem();
|
||||||
|
cargoItemsState.add(item);
|
||||||
|
|
||||||
|
// Pre-fill current input values into backing item
|
||||||
|
item.setDescription(desc.getValue());
|
||||||
|
item.setQuantity(qty.getValue());
|
||||||
|
item.setWeightKg(weight.getValue());
|
||||||
|
item.setLengthMm(len.getValue());
|
||||||
|
// Initialize from current inputs
|
||||||
|
item.setDescription(desc.getValue());
|
||||||
|
item.setQuantity(qty.getValue());
|
||||||
|
item.setWeightKg(weight.getValue());
|
||||||
|
item.setLengthMm(len.getValue());
|
||||||
|
item.setWidthMm(wid.getValue());
|
||||||
|
item.setHeightMm(hei.getValue());
|
||||||
|
item.setWidthMm(wid.getValue());
|
||||||
|
item.setHeightMm(hei.getValue());
|
||||||
|
|
||||||
|
desc.addValueChangeListener(ev -> item.setDescription(ev.getValue()));
|
||||||
|
qty.addValueChangeListener(ev -> item.setQuantity(ev.getValue()));
|
||||||
|
weight.addValueChangeListener(ev -> item.setWeightKg(ev.getValue()));
|
||||||
|
len.addValueChangeListener(ev -> item.setLengthMm(ev.getValue()));
|
||||||
|
wid.addValueChangeListener(ev -> item.setWidthMm(ev.getValue()));
|
||||||
|
hei.addValueChangeListener(ev -> item.setHeightMm(ev.getValue()));
|
||||||
|
|
||||||
if (afterCreate != null) afterCreate.accept(row);
|
if (afterCreate != null) afterCreate.accept(row);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1075,42 +1108,10 @@ public class AddJobView extends Main {
|
|||||||
addCargoRow.accept("paket", r -> {});
|
addCargoRow.accept("paket", r -> {});
|
||||||
addCargoRow.accept("", r -> {});
|
addCargoRow.accept("", r -> {});
|
||||||
|
|
||||||
wrapper.add(cargoArea);
|
wrapper.add(cargoAreaContainer);
|
||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void populateFromJob(Job job) {
|
|
||||||
// Pickup address
|
|
||||||
if (job.getPickupCompany() != null) pickupCompany.setValue(job.getPickupCompany());
|
|
||||||
if (job.getPickupSalutation() != null) pickupSalutation.setValue(job.getPickupSalutation());
|
|
||||||
if (job.getPickupFirstName() != null) pickupFirstName.setValue(job.getPickupFirstName());
|
|
||||||
if (job.getPickupLastName() != null) pickupLastName.setValue(job.getPickupLastName());
|
|
||||||
if (job.getPickupPhone() != null) pickupPhone.setValue(job.getPickupPhone());
|
|
||||||
if (job.getPickupStreet() != null) pickupStreet.setValue(job.getPickupStreet());
|
|
||||||
if (job.getPickupHouseNumber() != null) pickupHouseNumber.setValue(job.getPickupHouseNumber());
|
|
||||||
if (job.getPickupAddressAddition() != null) pickupAddressAddition.setValue(job.getPickupAddressAddition());
|
|
||||||
if (job.getPickupZip() != null) pickupZip.setValue(job.getPickupZip());
|
|
||||||
if (job.getPickupCity() != null) pickupCity.setValue(job.getPickupCity());
|
|
||||||
savePickupAddress.setValue(job.isSavePickupAddress());
|
|
||||||
|
|
||||||
// Delivery address
|
|
||||||
if (job.getDeliveryCompany() != null) deliveryCompany.setValue(job.getDeliveryCompany());
|
|
||||||
if (job.getDeliverySalutation() != null) deliverySalutation.setValue(job.getDeliverySalutation());
|
|
||||||
if (job.getDeliveryFirstName() != null) deliveryFirstName.setValue(job.getDeliveryFirstName());
|
|
||||||
if (job.getDeliveryLastName() != null) deliveryLastName.setValue(job.getDeliveryLastName());
|
|
||||||
if (job.getDeliveryPhone() != null) deliveryPhone.setValue(job.getDeliveryPhone());
|
|
||||||
if (job.getDeliveryStreet() != null) deliveryStreet.setValue(job.getDeliveryStreet());
|
|
||||||
if (job.getDeliveryHouseNumber() != null) deliveryHouseNumber.setValue(job.getDeliveryHouseNumber());
|
|
||||||
if (job.getDeliveryAddressAddition() != null) deliveryAddressAddition.setValue(job.getDeliveryAddressAddition());
|
|
||||||
if (job.getDeliveryZip() != null) deliveryZip.setValue(job.getDeliveryZip());
|
|
||||||
if (job.getDeliveryCity() != null) deliveryCity.setValue(job.getDeliveryCity());
|
|
||||||
saveDeliveryAddress.setValue(job.isSaveDeliveryAddress());
|
|
||||||
|
|
||||||
// Digital processing
|
|
||||||
digitalProcessing.setValue(job.isDigitalProcessing());
|
|
||||||
if (job.getAppUser() != null) appUser.setValue(job.getAppUser());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Component createTasksAndNotesSection() {
|
private Component createTasksAndNotesSection() {
|
||||||
VerticalLayout wrapper = new VerticalLayout();
|
VerticalLayout wrapper = new VerticalLayout();
|
||||||
wrapper.setWidthFull();
|
wrapper.setWidthFull();
|
||||||
@@ -1144,11 +1145,21 @@ public class AddJobView extends Main {
|
|||||||
|
|
||||||
Button remove = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
|
Button remove = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
|
||||||
remove.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
|
remove.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
|
||||||
remove.addClickListener(e -> tasksList.remove(row));
|
remove.addClickListener(e -> {
|
||||||
|
int idx = tasksList.getChildren().toList().indexOf(row);
|
||||||
|
if (idx >= 0 && idx < tasksState.size()) tasksState.remove(idx);
|
||||||
|
tasksList.remove(row);
|
||||||
|
});
|
||||||
|
|
||||||
row.add(taskField, remove);
|
row.add(taskField, remove);
|
||||||
row.setFlexGrow(1, taskField);
|
row.setFlexGrow(1, taskField);
|
||||||
tasksList.add(row);
|
tasksList.add(row);
|
||||||
|
|
||||||
|
// Keep backing tasks list in sync
|
||||||
|
TaskEntry te = new TaskEntry();
|
||||||
|
te.setText(taskField.getValue());
|
||||||
|
tasksState.add(te);
|
||||||
|
taskField.addValueChangeListener(ev -> te.setText(ev.getValue()));
|
||||||
};
|
};
|
||||||
|
|
||||||
// 1 Beispielzeile
|
// 1 Beispielzeile
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package de.assecutor.votianlt.repository;
|
||||||
|
|
||||||
|
import de.assecutor.votianlt.model.TaskEntry;
|
||||||
|
import org.bson.types.ObjectId;
|
||||||
|
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface TaskRepository extends MongoRepository<TaskEntry, ObjectId> {
|
||||||
|
List<TaskEntry> findByJobId(ObjectId jobId);
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user