Erweiterungen

This commit is contained in:
2025-08-12 21:43:51 +02:00
parent a00e9a78a4
commit aa28a7044c
5 changed files with 898 additions and 61 deletions

View File

@@ -2,41 +2,113 @@ package de.assecutor.votianlt.model;
import lombok.Data;
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;
import java.time.LocalDateTime;
@Data
@Document(collection = "jobs")
public class Job {
@Id
private ObjectId id;
// Metadaten
@Field("job_number")
private String jobNumber; // Eindeutige Auftragsnummer
@Field("status")
private JobStatus status = JobStatus.CREATED; // Status des Auftrags
@Field("created_at")
private LocalDateTime createdAt;
@Field("updated_at")
private LocalDateTime updatedAt;
@Field("created_by")
private String createdBy; // Benutzer, der den Auftrag erstellt hat
@Field("is_draft")
private boolean isDraft = false; // Kennzeichnet Entwürfe
// Auftraggeber/Rechnungsempfänger
@Field("customer_selection")
private String customerSelection; // Kunde01 | KOTVor K01Nach
// Abholadresse
@Field("pickup_company")
private String pickupCompany;
@Field("pickup_salutation")
private String pickupSalutation;
@Field("pickup_first_name")
private String pickupFirstName;
@Field("pickup_last_name")
private String pickupLastName;
@Field("pickup_phone")
private String pickupPhone;
@Field("pickup_street")
private String pickupStreet;
@Field("pickup_house_number")
private String pickupHouseNumber;
@Field("pickup_address_addition")
private String pickupAddressAddition;
@Field("pickup_zip")
private String pickupZip;
@Field("pickup_city")
private String pickupCity;
@Field("save_pickup_address")
private boolean savePickupAddress;
// Lieferadresse
@Field("delivery_company")
private String deliveryCompany;
@Field("delivery_salutation")
private String deliverySalutation;
@Field("delivery_first_name")
private String deliveryFirstName;
@Field("delivery_last_name")
private String deliveryLastName;
@Field("delivery_phone")
private String deliveryPhone;
@Field("delivery_street")
private String deliveryStreet;
@Field("delivery_house_number")
private String deliveryHouseNumber;
@Field("delivery_address_addition")
private String deliveryAddressAddition;
@Field("delivery_zip")
private String deliveryZip;
@Field("delivery_city")
private String deliveryCity;
@Field("save_delivery_address")
private boolean saveDeliveryAddress;
// Digitale Abwicklung per App
@Field("digital_processing")
private boolean digitalProcessing;
@Field("app_user")
private String appUser;
}

View File

@@ -0,0 +1,30 @@
package de.assecutor.votianlt.model;
/**
* Status-Enum für Aufträge
*/
public enum JobStatus {
CREATED("Erstellt"),
IN_PROGRESS("In Bearbeitung"),
PICKUP_SCHEDULED("Abholung geplant"),
PICKED_UP("Abgeholt"),
IN_TRANSIT("Unterwegs"),
DELIVERED("Zugestellt"),
COMPLETED("Abgeschlossen"),
CANCELLED("Storniert");
private final String displayName;
JobStatus(String displayName) {
this.displayName = displayName;
}
public String getDisplayName() {
return displayName;
}
@Override
public String toString() {
return displayName;
}
}

View File

@@ -1,13 +1,245 @@
package de.assecutor.votianlt.pages.add_job.service;
import de.assecutor.votianlt.model.Job;
import de.assecutor.votianlt.model.JobStatus;
import de.assecutor.votianlt.repository.JobRepository;
import de.assecutor.votianlt.security.SecurityService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Optional;
@Service
@RequiredArgsConstructor
@Slf4j
public class AddJobService {
public void addJob(Job job) {
// TODO: Implement job persistence logic
System.out.println("Job would be saved: " + job.toString());
private final JobRepository jobRepository;
private final SecurityService securityService;
/**
* 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
*/
public Job updateJob(Job job) {
try {
job.setUpdatedAt(LocalDateTime.now());
Job updatedJob = jobRepository.save(job);
log.info("Auftrag erfolgreich aktualisiert: {}", updatedJob.getJobNumber());
return updatedJob;
} catch (Exception e) {
log.error("Fehler beim Aktualisieren des Auftrags: {}", e.getMessage(), e);
throw new RuntimeException("Auftrag konnte nicht aktualisiert werden: " + e.getMessage());
}
}
/**
* Findet einen Auftrag anhand der ID
*/
public Optional<Job> findById(String id) {
try {
return jobRepository.findById(new org.bson.types.ObjectId(id));
} catch (Exception e) {
log.error("Fehler beim Suchen des Auftrags mit ID {}: {}", id, e.getMessage());
return Optional.empty();
}
}
/**
* Findet einen Auftrag anhand der Auftragsnummer
*/
public Optional<Job> findByJobNumber(String jobNumber) {
return jobRepository.findByJobNumber(jobNumber);
}
/**
* Findet alle Aufträge eines Benutzers
*/
public List<Job> findJobsByUser(String username) {
return jobRepository.findByCreatedBy(username);
}
/**
* Findet alle Aufträge mit einem bestimmten Status
*/
public List<Job> findJobsByStatus(JobStatus status) {
return jobRepository.findByStatus(status);
}
/**
* Löscht einen Auftrag
*/
public void deleteJob(String id) {
try {
jobRepository.deleteById(new org.bson.types.ObjectId(id));
log.info("Auftrag mit ID {} erfolgreich gelöscht", id);
} catch (Exception e) {
log.error("Fehler beim Löschen des Auftrags mit ID {}: {}", id, e.getMessage());
throw new RuntimeException("Auftrag konnte nicht gelöscht werden: " + e.getMessage());
}
}
/**
* Generiert eine eindeutige Auftragsnummer
*/
private String generateJobNumber() {
String prefix = "JOB";
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
// Zähle Aufträge des aktuellen Tages
String todayPrefix = prefix + timestamp;
long todayCount = jobRepository.findAll().stream()
.filter(job -> job.getJobNumber() != null && job.getJobNumber().startsWith(todayPrefix))
.count();
// Generiere neue Nummer
String jobNumber;
do {
todayCount++;
jobNumber = String.format("%s%s%03d", prefix, timestamp, todayCount);
} while (jobRepository.existsByJobNumber(jobNumber));
return jobNumber;
}
/**
* Zählt alle Aufträge
*/
public long countAllJobs() {
return jobRepository.count();
}
/**
* Zählt Aufträge nach Status
*/
public long countJobsByStatus(JobStatus status) {
return jobRepository.countByStatus(status);
}
/**
* Speichert einen Auftrag als Entwurf (für automatisches Speichern)
*/
public Job saveDraft(Job job) {
try {
// Prüfen ob bereits ein Entwurf für diesen Benutzer existiert
String currentUser = securityService.getCurrentUsername();
List<Job> existingDrafts = jobRepository.findByCreatedByAndIsDraftTrue(currentUser);
Job draftJob;
if (!existingDrafts.isEmpty()) {
// Bestehenden Entwurf aktualisieren
draftJob = existingDrafts.get(0);
updateJobFromForm(draftJob, job);
draftJob.setUpdatedAt(LocalDateTime.now());
} else {
// Neuen Entwurf erstellen
draftJob = job;
LocalDateTime now = LocalDateTime.now();
draftJob.setCreatedAt(now);
draftJob.setUpdatedAt(now);
draftJob.setStatus(JobStatus.CREATED);
draftJob.setCreatedBy(currentUser);
draftJob.setDraft(true);
// Spezielle Entwurfs-Auftragsnummer
draftJob.setJobNumber("DRAFT_" + currentUser.replace("@", "_") + "_" +
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")));
}
Job savedDraft = jobRepository.save(draftJob);
log.info("Entwurf automatisch gespeichert für Benutzer: {}", currentUser);
return savedDraft;
} catch (Exception e) {
log.error("Fehler beim Speichern des Entwurfs: {}", e.getMessage(), e);
throw new RuntimeException("Entwurf konnte nicht gespeichert werden: " + e.getMessage());
}
}
/**
* Aktualisiert einen bestehenden Job mit neuen Formulardaten
*/
private void updateJobFromForm(Job existingJob, Job formJob) {
existingJob.setCustomerSelection(formJob.getCustomerSelection());
// Pickup address
existingJob.setPickupCompany(formJob.getPickupCompany());
existingJob.setPickupSalutation(formJob.getPickupSalutation());
existingJob.setPickupFirstName(formJob.getPickupFirstName());
existingJob.setPickupLastName(formJob.getPickupLastName());
existingJob.setPickupPhone(formJob.getPickupPhone());
existingJob.setPickupStreet(formJob.getPickupStreet());
existingJob.setPickupHouseNumber(formJob.getPickupHouseNumber());
existingJob.setPickupAddressAddition(formJob.getPickupAddressAddition());
existingJob.setPickupZip(formJob.getPickupZip());
existingJob.setPickupCity(formJob.getPickupCity());
existingJob.setSavePickupAddress(formJob.isSavePickupAddress());
// Delivery address
existingJob.setDeliveryCompany(formJob.getDeliveryCompany());
existingJob.setDeliverySalutation(formJob.getDeliverySalutation());
existingJob.setDeliveryFirstName(formJob.getDeliveryFirstName());
existingJob.setDeliveryLastName(formJob.getDeliveryLastName());
existingJob.setDeliveryPhone(formJob.getDeliveryPhone());
existingJob.setDeliveryStreet(formJob.getDeliveryStreet());
existingJob.setDeliveryHouseNumber(formJob.getDeliveryHouseNumber());
existingJob.setDeliveryAddressAddition(formJob.getDeliveryAddressAddition());
existingJob.setDeliveryZip(formJob.getDeliveryZip());
existingJob.setDeliveryCity(formJob.getDeliveryCity());
existingJob.setSaveDeliveryAddress(formJob.isSaveDeliveryAddress());
// Digital processing
existingJob.setDigitalProcessing(formJob.isDigitalProcessing());
existingJob.setAppUser(formJob.getAppUser());
}
/**
* Findet den aktuellen Entwurf eines Benutzers
*/
public Optional<Job> findCurrentDraft(String username) {
List<Job> drafts = jobRepository.findByCreatedByAndIsDraftTrue(username);
return drafts.isEmpty() ? Optional.empty() : Optional.of(drafts.get(0));
}
/**
* 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);
}
}

View File

@@ -4,16 +4,20 @@ import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.checkbox.Checkbox;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.ClientCallable;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.data.binder.ValidationException;
import com.vaadin.flow.router.Menu;
@@ -25,11 +29,14 @@ import de.assecutor.votianlt.pages.add_job.service.AddJobService;
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
import jakarta.annotation.security.RolesAllowed;
import lombok.extern.slf4j.Slf4j;
import java.util.Optional;
@Route(value = "add_job", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
@PageTitle("Neuen Auftrag anlegen")
@Menu(order = 0, icon = "vaadin:clipboard-check", title = "Neuen Auftrag anlegen")
@RolesAllowed("USER")
@Slf4j
public class AddJobView extends Main {
private final AddJobService addJobService;
@@ -76,83 +83,94 @@ public class AddJobView extends Main {
private final Binder<Job> binder = new Binder<>(Job.class);
public AddJobView(AddJobService addJobService) {
this.addJobService = addJobService;
initializeComponents();
setupLayout();
setupValidation();
loadDraftIfExists();
}
private void initializeComponents() {
// Customer selection
customerSelection = new ComboBox<>("Auftraggeber/Rechnungsempfänger");
customerSelection.setItems("Kunde01 | KOTVor K01Nach");
customerSelection.setValue("Kunde01 | KOTVor K01Nach");
customerSelection.setPlaceholder("Wählen Sie einen Auftraggeber aus...");
customerSelection.setWidthFull();
preloadAddressButton = new Button("Vorbelegte Adressfelder leeren");
preloadAddressButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
preloadAddressButton.setIcon(new Icon(VaadinIcon.QUESTION_CIRCLE));
// Required fields notice
requiredFieldsNotice = new Span("Die mit (*) gekennzeichneten Felder sind Pflichtfelder.");
requiredFieldsNotice.getStyle().set("color", "red");
requiredFieldsNotice.getStyle().set("font-size", "14px");
preloadAddressButton.addClickListener(event -> clearAllFields());
// Pickup address
pickupCompany = new TextField("Firma");
pickupCompany.setValue("Kunde01");
pickupCompany.setPlaceholder("z.B. IKEA, McDonald's, DHL...");
addGooglePlacesAutocomplete(pickupCompany, 0); // Stage 0 für Pickup
pickupSalutation = new ComboBox<>("Anrede");
pickupSalutation.setItems("Herr", "Frau", "Divers");
pickupFirstName = new TextField("Vorname*");
pickupFirstName.setValue("K01Vor");
pickupSalutation.setPlaceholder("Anrede wählen...");
pickupFirstName = new TextField("Vorname");
pickupFirstName.setPlaceholder("Max");
pickupFirstName.setRequiredIndicatorVisible(true);
pickupLastName = new TextField("Nachname*");
pickupLastName.setValue("K01Nach");
pickupLastName = new TextField("Nachname");
pickupLastName.setPlaceholder("Mustermann");
pickupLastName.setRequiredIndicatorVisible(true);
pickupPhone = new TextField("Telefonnummer");
pickupPhone.setValue("01");
pickupStreet = new TextField("Straße*");
pickupStreet.setValue("Ottensener Str.");
pickupPhone.setPlaceholder("+49 123 456789");
pickupStreet = new TextField("Straße");
pickupStreet.setPlaceholder("Musterstraße");
pickupStreet.setRequiredIndicatorVisible(true);
pickupHouseNumber = new TextField("Hausnr*");
pickupHouseNumber.setValue("8");
pickupHouseNumber = new TextField("Hausnummer");
pickupHouseNumber.setPlaceholder("123");
pickupHouseNumber.setRequiredIndicatorVisible(true);
pickupAddressAddition = new TextField("Adresszusatz");
pickupZip = new TextField("Postleitzahl*");
pickupZip.setValue("22525");
pickupAddressAddition.setPlaceholder("2. OG, Hinterhaus...");
pickupZip = new TextField("Postleitzahl");
pickupZip.setPlaceholder("12345");
pickupZip.setRequiredIndicatorVisible(true);
pickupCity = new TextField("Ort*");
pickupCity.setValue("Hamburg");
pickupCity = new TextField("Ort");
pickupCity.setPlaceholder("Hamburg");
pickupCity.setRequiredIndicatorVisible(true);
savePickupAddress = new Checkbox("Die Adresse für zukünftige Aufträge speichern.");
// Delivery address
deliveryCompany = new TextField("Firma");
deliveryCompany.setPlaceholder("z.B. EDEKA, Bauhaus, Amazon...");
addGooglePlacesAutocomplete(deliveryCompany, 1); // Stage 1 für Delivery
deliverySalutation = new ComboBox<>("Anrede");
deliverySalutation.setItems("Herr", "Frau", "Divers");
deliveryFirstName = new TextField("Vorname*");
deliverySalutation.setPlaceholder("Anrede wählen...");
deliveryFirstName = new TextField("Vorname");
deliveryFirstName.setPlaceholder("Anna");
deliveryFirstName.setRequiredIndicatorVisible(true);
deliveryLastName = new TextField("Nachname*");
deliveryLastName = new TextField("Nachname");
deliveryLastName.setPlaceholder("Beispiel");
deliveryLastName.setRequiredIndicatorVisible(true);
deliveryPhone = new TextField("Telefonnummer");
deliveryStreet = new TextField("Straße*");
deliveryPhone.setPlaceholder("+49 987 654321");
deliveryStreet = new TextField("Straße");
deliveryStreet.setPlaceholder("Beispielweg");
deliveryStreet.setRequiredIndicatorVisible(true);
deliveryHouseNumber = new TextField("Hausnr*");
deliveryHouseNumber = new TextField("Hausnr");
deliveryHouseNumber.setPlaceholder("456");
deliveryHouseNumber.setRequiredIndicatorVisible(true);
deliveryAddressAddition = new TextField("Adresszusatz");
deliveryZip = new TextField("Postleitzahl*");
deliveryAddressAddition.setPlaceholder("Erdgeschoss, links...");
deliveryZip = new TextField("Postleitzahl");
deliveryZip.setPlaceholder("54321");
deliveryZip.setRequiredIndicatorVisible(true);
deliveryCity = new TextField("Ort*");
deliveryCity = new TextField("Ort");
deliveryCity.setPlaceholder("Berlin");
deliveryCity.setRequiredIndicatorVisible(true);
saveDeliveryAddress = new Checkbox("Die Adresse für zukünftige Aufträge speichern.");
saveDeliveryAddress.setValue(true);
// Digital processing
digitalProcessing = new Checkbox("Digitale Abwicklung per App");
digitalProcessing.setValue(true);
appUser = new ComboBox<>("App-Nutzer");
appUser.setItems("App-Nutzer");
appUser.setPlaceholder("App-Nutzer auswählen...");
// Submit button
submitButton = new Button("Auftrag anlegen", event -> submit());
@@ -176,8 +194,7 @@ public class AddJobView extends Main {
preloadAddressButton.setWidth("30%");
add(customerLayout);
add(requiredFieldsNotice);
// Main content layout with two equal columns (50% each)
HorizontalLayout mainLayout = new HorizontalLayout();
mainLayout.setWidthFull();
@@ -185,24 +202,14 @@ public class AddJobView extends Main {
mainLayout.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.START);
// Left column (50%) - Pickup address section
VerticalLayout leftColumn = createPickupSection();
leftColumn.setWidth("50%");
VerticalLayout pickupSection = createPickupSection();
pickupSection.setWidth("50%");
// Right column (50%) - Delivery address section
VerticalLayout rightColumn = createDeliverySection();
rightColumn.setWidth("50%");
VerticalLayout deliverySection = createDeliverySection();
deliverySection.setWidth("50%");
// Add copy button to the right column at the top
Button copyButton = new Button("Abholadresse kopieren");
copyButton.setIcon(new Icon(VaadinIcon.ARROW_RIGHT));
copyButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
copyButton.addClickListener(e -> copyPickupToDelivery());
copyButton.getStyle().set("margin-bottom", "var(--lumo-space-m)");
// Insert copy button at the beginning of right column
rightColumn.addComponentAsFirst(copyButton);
mainLayout.add(leftColumn, rightColumn);
mainLayout.add(pickupSection, deliverySection);
add(mainLayout);
@@ -222,17 +229,35 @@ public class AddJobView extends Main {
private VerticalLayout createPickupSection() {
VerticalLayout section = new VerticalLayout();
section.setSpacing(true);
section.setPadding(false);
section.setPadding(true);
section.setWidthFull();
// Hellgrauer Rahmen hinzufügen
section.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
section.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
section.getStyle().set("background-color", "var(--lumo-base-color)");
H3 title = new H3("Abholadresse");
title.getStyle().set("margin", "0");
title.getStyle().set("display", "flex");
title.getStyle().set("align-items", "center");
Icon helpIcon = new Icon(VaadinIcon.QUESTION_CIRCLE);
helpIcon.getStyle().set("margin-left", "8px");
title.add(helpIcon);
section.add(title);
HorizontalLayout titleLayout = new HorizontalLayout();
titleLayout.setWidthFull();
titleLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.START);
titleLayout.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER);
titleLayout.add(title);
// Alle einzelnen Controls auf volle Breite setzen
pickupCompany.setWidthFull();
pickupSalutation.setWidthFull();
pickupFirstName.setWidthFull();
pickupLastName.setWidthFull();
pickupPhone.setWidthFull();
pickupAddressAddition.setWidthFull();
savePickupAddress.setWidthFull();
section.add(titleLayout);
section.add(pickupCompany);
section.add(pickupSalutation);
section.add(pickupFirstName);
@@ -241,6 +266,7 @@ public class AddJobView extends Main {
HorizontalLayout streetLayout = new HorizontalLayout();
streetLayout.setWidthFull();
streetLayout.setSpacing(true);
streetLayout.add(pickupStreet, pickupHouseNumber);
pickupStreet.setWidth("70%");
pickupHouseNumber.setWidth("30%");
@@ -250,6 +276,7 @@ public class AddJobView extends Main {
HorizontalLayout zipCityLayout = new HorizontalLayout();
zipCityLayout.setWidthFull();
zipCityLayout.setSpacing(true);
zipCityLayout.add(pickupZip, pickupCity);
pickupZip.setWidth("30%");
pickupCity.setWidth("70%");
@@ -263,12 +290,35 @@ public class AddJobView extends Main {
private VerticalLayout createDeliverySection() {
VerticalLayout section = new VerticalLayout();
section.setSpacing(true);
section.setPadding(false);
section.setPadding(true);
section.setWidthFull();
// Hellgrauer Rahmen hinzufügen
section.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
section.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
section.getStyle().set("background-color", "var(--lumo-base-color)");
H3 title = new H3("Lieferadresse");
title.getStyle().set("margin", "0");
section.add(title);
HorizontalLayout titleLayout = new HorizontalLayout();
titleLayout.setWidthFull();
titleLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.START);
titleLayout.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER);
titleLayout.add(title);
// Alle einzelnen Controls auf volle Breite setzen
deliveryCompany.setWidthFull();
deliverySalutation.setWidthFull();
deliveryFirstName.setWidthFull();
deliveryLastName.setWidthFull();
deliveryPhone.setWidthFull();
deliveryAddressAddition.setWidthFull();
saveDeliveryAddress.setWidthFull();
section.add(titleLayout);
section.add(deliveryCompany);
section.add(deliverySalutation);
section.add(deliveryFirstName);
@@ -277,6 +327,7 @@ public class AddJobView extends Main {
HorizontalLayout streetLayout = new HorizontalLayout();
streetLayout.setWidthFull();
streetLayout.setSpacing(true);
streetLayout.add(deliveryStreet, deliveryHouseNumber);
deliveryStreet.setWidth("70%");
deliveryHouseNumber.setWidth("30%");
@@ -286,6 +337,7 @@ public class AddJobView extends Main {
HorizontalLayout zipCityLayout = new HorizontalLayout();
zipCityLayout.setWidthFull();
zipCityLayout.setSpacing(true);
zipCityLayout.add(deliveryZip, deliveryCity);
deliveryZip.setWidth("30%");
deliveryCity.setWidth("70%");
@@ -296,6 +348,10 @@ public class AddJobView extends Main {
return section;
}
private void copyPickupToDelivery() {
deliveryCompany.setValue(pickupCompany.getValue());
deliverySalutation.setValue(pickupSalutation.getValue());
@@ -347,7 +403,367 @@ public class AddJobView extends Main {
job.setAppUser(appUser.getValue());
job.setCustomerSelection(customerSelection.getValue());
addJobService.addJob(job);
System.out.println("Job created successfully");
try {
Job savedJob = addJobService.addJob(job);
// Erfolgsmeldung anzeigen
Notification successNotification = Notification.show(
"Auftrag erfolgreich erstellt! Auftragsnummer: " + savedJob.getJobNumber());
successNotification.setDuration(5000);
// Formular zurücksetzen
clearForm();
} catch (Exception e) {
// Fehlermeldung anzeigen
Notification errorNotification = Notification.show(
"Fehler beim Erstellen des Auftrags: " + e.getMessage());
errorNotification.setDuration(5000);
}
}
/**
* Setzt alle Formularfelder zurück
*/
private void clearForm() {
// Customer selection
customerSelection.clear();
// Pickup address
pickupCompany.clear();
pickupSalutation.clear();
pickupFirstName.clear();
pickupLastName.clear();
pickupPhone.clear();
pickupStreet.clear();
pickupHouseNumber.clear();
pickupAddressAddition.clear();
pickupZip.clear();
pickupCity.clear();
savePickupAddress.setValue(false);
// Delivery address
deliveryCompany.clear();
deliverySalutation.clear();
deliveryFirstName.clear();
deliveryLastName.clear();
deliveryPhone.clear();
deliveryStreet.clear();
deliveryHouseNumber.clear();
deliveryAddressAddition.clear();
deliveryZip.clear();
deliveryCity.clear();
saveDeliveryAddress.setValue(false);
// Digital processing
digitalProcessing.setValue(false);
appUser.clear();
}
/**
* Persistiert die aktuellen Formulardaten als Entwurf
*/
private void persistCurrentData() {
// Prüfen ob mindestens ein Feld ausgefüllt ist
if (isFormEmpty()) {
return; // Keine Daten zum Speichern
}
Job job = new Job();
// Alle aktuellen Werte aus dem Formular lesen
job.setCustomerSelection(customerSelection.getValue());
// Pickup address
job.setPickupCompany(pickupCompany.getValue());
job.setPickupSalutation(pickupSalutation.getValue());
job.setPickupFirstName(pickupFirstName.getValue());
job.setPickupLastName(pickupLastName.getValue());
job.setPickupPhone(pickupPhone.getValue());
job.setPickupStreet(pickupStreet.getValue());
job.setPickupHouseNumber(pickupHouseNumber.getValue());
job.setPickupAddressAddition(pickupAddressAddition.getValue());
job.setPickupZip(pickupZip.getValue());
job.setPickupCity(pickupCity.getValue());
job.setSavePickupAddress(savePickupAddress.getValue());
// Delivery address
job.setDeliveryCompany(deliveryCompany.getValue());
job.setDeliverySalutation(deliverySalutation.getValue());
job.setDeliveryFirstName(deliveryFirstName.getValue());
job.setDeliveryLastName(deliveryLastName.getValue());
job.setDeliveryPhone(deliveryPhone.getValue());
job.setDeliveryStreet(deliveryStreet.getValue());
job.setDeliveryHouseNumber(deliveryHouseNumber.getValue());
job.setDeliveryAddressAddition(deliveryAddressAddition.getValue());
job.setDeliveryZip(deliveryZip.getValue());
job.setDeliveryCity(deliveryCity.getValue());
job.setSaveDeliveryAddress(saveDeliveryAddress.getValue());
// Digital processing
job.setDigitalProcessing(digitalProcessing.getValue());
job.setAppUser(appUser.getValue());
// Als Entwurf speichern
addJobService.saveDraft(job);
}
/**
* Prüft ob das Formular komplett leer ist
*/
private boolean isFormEmpty() {
return (customerSelection.getValue() == null || customerSelection.getValue().trim().isEmpty()) &&
(pickupCompany.getValue() == null || pickupCompany.getValue().trim().isEmpty()) &&
(pickupFirstName.getValue() == null || pickupFirstName.getValue().trim().isEmpty()) &&
(pickupLastName.getValue() == null || pickupLastName.getValue().trim().isEmpty()) &&
(pickupStreet.getValue() == null || pickupStreet.getValue().trim().isEmpty()) &&
(pickupCity.getValue() == null || pickupCity.getValue().trim().isEmpty()) &&
(deliveryCompany.getValue() == null || deliveryCompany.getValue().trim().isEmpty()) &&
(deliveryFirstName.getValue() == null || deliveryFirstName.getValue().trim().isEmpty()) &&
(deliveryLastName.getValue() == null || deliveryLastName.getValue().trim().isEmpty()) &&
(deliveryStreet.getValue() == null || deliveryStreet.getValue().trim().isEmpty()) &&
(deliveryCity.getValue() == null || deliveryCity.getValue().trim().isEmpty());
}
/**
* Lädt einen bestehenden Entwurf, falls vorhanden
*/
private void loadDraftIfExists() {
try {
String currentUser = getCurrentUsername();
if (currentUser != null) {
Optional<Job> draftOpt = addJobService.findCurrentDraft(currentUser);
if (draftOpt.isPresent()) {
Job draft = draftOpt.get();
loadJobIntoForm(draft);
// Benutzer informieren
Notification notification = Notification.show(
"Entwurf wiederhergestellt. Sie können Ihre Arbeit fortsetzen.");
notification.setDuration(4000);
}
}
} catch (Exception e) {
// Fehler beim Laden des Entwurfs sollten nicht die Anwendung blockieren
System.err.println("Fehler beim Laden des Entwurfs: " + e.getMessage());
}
}
/**
* Lädt Job-Daten in das Formular
*/
private void loadJobIntoForm(Job job) {
if (job.getCustomerSelection() != null) {
customerSelection.setValue(job.getCustomerSelection());
}
// 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());
}
/**
* Hilfsmethode zum Abrufen des aktuellen Benutzernamens
*/
private String getCurrentUsername() {
try {
// Hier würden Sie normalerweise den SecurityService verwenden
// Für diese Demo nehmen wir einen festen Wert
return "test@votianlt.de"; // TODO: SecurityService integrieren
} catch (Exception e) {
return null;
}
}
/**
* Leert alle Felder im Formular
*/
private void clearAllFields() {
// Customer selection
customerSelection.clear();
// Pickup address
pickupCompany.clear();
pickupSalutation.clear();
pickupFirstName.clear();
pickupLastName.clear();
pickupPhone.clear();
pickupStreet.clear();
pickupHouseNumber.clear();
pickupAddressAddition.clear();
pickupZip.clear();
pickupCity.clear();
savePickupAddress.setValue(false);
// Delivery address
deliveryCompany.clear();
deliverySalutation.clear();
deliveryFirstName.clear();
deliveryLastName.clear();
deliveryPhone.clear();
deliveryStreet.clear();
deliveryHouseNumber.clear();
deliveryAddressAddition.clear();
deliveryZip.clear();
deliveryCity.clear();
saveDeliveryAddress.setValue(false);
// Digital processing
digitalProcessing.setValue(false);
appUser.clear();
// Benutzer-Feedback
Notification.show("Alle Felder wurden geleert", 2000, Notification.Position.BOTTOM_CENTER);
}
/**
* Fügt Google Places Autocomplete zu einem TextField hinzu
*/
private void addGooglePlacesAutocomplete(TextField textField, int stageIndex) {
// Initialisierung als Runnable kapseln
Runnable initAutocomplete = () -> {
// Google Places API Script laden (falls noch nicht geladen)
UI.getCurrent().getPage().addJavaScript("https://maps.googleapis.com/maps/api/js?key=AIzaSyDnbitL06iLp3elmj-WtPudCykX9xvXcVE&libraries=places");
// JavaScript für Google Places Autocomplete - nutzt direkt das Input-Element
String script = """
setTimeout(function() {
var host = $0; // Vaadin TextField element
var input = host && host.inputElement ? host.inputElement : (host && host.shadowRoot ? host.shadowRoot.querySelector('input') : null);
if (input && window.google && window.google.maps && window.google.maps.places) {
var autocomplete = new google.maps.places.Autocomplete(input, {
types: ['establishment'],
componentRestrictions: {country: 'de'}
});
autocomplete.addListener('place_changed', function() {
var place = autocomplete.getPlace();
if (place && place.address_components) {
var streetNumber = '';
var route = '';
var locality = '';
var postalCode = '';
var country = '';
var companyName = place.name || '';
for (var i = 0; i < place.address_components.length; i++) {
var component = place.address_components[i];
var types = component.types || [];
if (types.indexOf('street_number') !== -1) {
streetNumber = component.long_name;
} else if (types.indexOf('route') !== -1) {
route = component.long_name;
} else if (types.indexOf('locality') !== -1) {
locality = component.long_name;
} else if (types.indexOf('postal_code') !== -1) {
postalCode = component.long_name;
} else if (types.indexOf('country') !== -1) {
country = component.long_name;
}
}
// Setze den Firmennamen in das Feld (sichtbar und Vaadin-Wert)
input.value = companyName || '';
host.value = companyName || '';
// Sende Daten an Server mit Stage-Index
$1.$server.handleGooglePlaceSelected(%d, companyName, route, streetNumber, postalCode, locality, country);
}
});
} else {
setTimeout(arguments.callee, 500);
}
}, 500);
""".formatted(stageIndex);
// executeJs mit Referenzen: $0 => input element (shadowRoot), $1 => Server (this)
textField.getElement().executeJs(script, textField.getElement(), getElement());
log.debug("Google Places Autocomplete initialisiert (Stage {})", stageIndex);
};
// Wenn UI bereits existiert, direkt initialisieren; sonst beim Attach initialisieren
textField.getUI().ifPresentOrElse(
ui -> ui.access((com.vaadin.flow.server.Command) initAutocomplete::run),
() -> textField.addAttachListener(event -> event.getUI().access((com.vaadin.flow.server.Command) initAutocomplete::run))
);
}
/**
* Handler für Google Places Auswahl - wird vom JavaScript aufgerufen
*/
@ClientCallable
public void handleGooglePlaceSelected(int stageIndex, String companyName, String route, String streetNumber, String postalCode, String locality, String country) {
log.debug("Google Place ausgewählt - Stage: {}, Company: {}, Route: {}, StreetNumber: {}, PostalCode: {}, Locality: {}, Country: {}",
stageIndex, companyName, route, streetNumber, postalCode, locality, country);
if (stageIndex == 0) {
// Pickup address
// Firmenname wird bereits durch das TextField selbst gesetzt
if (route != null && !route.isEmpty()) {
pickupStreet.setValue(route);
}
if (streetNumber != null && !streetNumber.isEmpty()) {
pickupHouseNumber.setValue(streetNumber);
}
if (postalCode != null && !postalCode.isEmpty()) {
pickupZip.setValue(postalCode);
}
if (locality != null && !locality.isEmpty()) {
pickupCity.setValue(locality);
}
// Notification für Benutzer
Notification.show("Abholadresse automatisch ausgefüllt: " + (companyName != null ? companyName : ""),
3000, Notification.Position.BOTTOM_CENTER);
} else if (stageIndex == 1) {
// Delivery address
// Firmenname wird bereits durch das TextField selbst gesetzt
if (route != null && !route.isEmpty()) {
deliveryStreet.setValue(route);
}
if (streetNumber != null && !streetNumber.isEmpty()) {
deliveryHouseNumber.setValue(streetNumber);
}
if (postalCode != null && !postalCode.isEmpty()) {
deliveryZip.setValue(postalCode);
}
if (locality != null && !locality.isEmpty()) {
deliveryCity.setValue(locality);
}
// Notification für Benutzer
Notification.show("Lieferadresse automatisch ausgefüllt: " + (companyName != null ? companyName : ""),
3000, Notification.Position.BOTTOM_CENTER);
}
}
}

View File

@@ -0,0 +1,87 @@
package de.assecutor.votianlt.repository;
import de.assecutor.votianlt.model.Job;
import de.assecutor.votianlt.model.JobStatus;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@Repository
public interface JobRepository extends MongoRepository<Job, ObjectId> {
/**
* Findet einen Auftrag anhand der Auftragsnummer
*/
Optional<Job> findByJobNumber(String jobNumber);
/**
* Findet alle Aufträge eines bestimmten Status
*/
List<Job> findByStatus(JobStatus status);
/**
* Findet alle Aufträge, die von einem bestimmten Benutzer erstellt wurden
*/
List<Job> findByCreatedBy(String createdBy);
/**
* Findet alle Aufträge in einem bestimmten Zeitraum
*/
List<Job> findByCreatedAtBetween(LocalDateTime start, LocalDateTime end);
/**
* Findet alle Aufträge mit einer bestimmten Abholstadt
*/
List<Job> findByPickupCity(String pickupCity);
/**
* Findet alle Aufträge mit einer bestimmten Lieferstadt
*/
List<Job> findByDeliveryCity(String deliveryCity);
/**
* Findet alle Aufträge mit digitaler Abwicklung
*/
List<Job> findByDigitalProcessingTrue();
/**
* Zählt Aufträge nach Status
*/
long countByStatus(JobStatus status);
/**
* Findet die neuesten Aufträge (sortiert nach Erstellungsdatum)
*/
@Query(value = "{}", sort = "{ 'created_at' : -1 }")
List<Job> findLatestJobs();
/**
* Findet Aufträge anhand einer Kundenauswahl
*/
List<Job> findByCustomerSelection(String customerSelection);
/**
* Prüft, ob eine Auftragsnummer bereits existiert
*/
boolean existsByJobNumber(String jobNumber);
/**
* Findet alle Entwürfe eines Benutzers
*/
List<Job> findByCreatedByAndIsDraftTrue(String createdBy);
/**
* Findet alle finalen Aufträge (keine Entwürfe)
*/
List<Job> findByIsDraftFalse();
/**
* Zählt alle Entwürfe
*/
long countByIsDraftTrue();
}