Migriert die weiterhin benötigten Zeitaufgaben einschließlich SOAP-Importen, Betriebsstatus und Wiederanlaufkontrolle. Aktiviert 115 native Spring-Tasks im Docker-Image ohne Cron-Daemon oder PHP und dokumentiert die entfallenen HHA-Aufrufe. Ergänzt PHP-/UI-Testwerkzeuge und aktualisiert den Projektstand auf Version 0.2.0.
19 KiB
VoTiAn NG · Stadtbote
React-/TypeScript-Oberfläche und Spring-Boot-Anbindung für die vorhandene PHP-Anwendung. Verbindliche Referenz ist ausschließlich der aktuelle Serverstand per SSH auf root@172.16.0.158, Verzeichnis /data/0/votian-test/html (Projektwurzel /data/0/votian-test). Der alte lokale PHP-Ordner wird nicht mehr als Referenz verwendet. Die neue Anwendung verwendet dieselbe vorhandene MySQL-Datenbank und deren Tabellen. Sie enthält keine eigene Datenbank, keine automatischen Schemaänderungen und keine Demodatensätze.
Stand
Die Altanwendung wurde fachlich und technisch analysiert. Die neue Oberfläche bildet Auftragslisten, Disposition, Touren, Kunden, Kuriere und Auswertung ab. Das Spring-Boot-Backend greift ausschließlich über den zentralen DatabaseService auf das vorhandene Schema zu. Die Anmeldung verwendet vorhandene VoTiAn-Zentralenbenutzer mit ihren Niederlassungs- und Mitarbeiterrechten.
Die Auftragserfassung läuft jetzt nativ in React und Spring Boot: Sofortauftrag, Vorbestellung, Dauerauftrag, Barzahlung, direkte Fahrzeugzuweisung und Nacherfassung. Dazu gehören Kostenstellensuche, mehrere Stationen, PLZ-/Gebietstarife oder Festpreise, Preisvorschau, Rechteprüfung und transaktionales Speichern im Bestand. Hinzu kommen Kostensplit, Kurier-Unteraufträge bei Nacherfassungen, Artikelpositionen mit optionalen Lagerbewegungen und eingebettete Kartenauswahl. Voraussetzung ist das bestehende Erfassungsrecht 7; Lagerbewegungen benötigen zusätzlich Recht 14, Unternehmerverrechnung Recht 38.
Kunden- und Transporteurstamm → Portal-Hauptzugang verwaltet Aktivierung, Anmeldename, Passwort, Sperre und 2FA-Rücksetzung der vorhandenen PHP-Portalzugänge. Kontakt- und Abrechnungsdaten bleiben bei einer Zugangssperre erhalten. Zusätzliche Kundenmitarbeiter, eigene Portalrechte und Kostenstellenfreigaben sind unter „Kundenmitarbeiter / Portalrechte“ pflegbar. Fehlt die PHP-Matrixtabelle, bleibt nur die Matrix gesperrt; zur expliziten Einrichtung liegt backend/sql/portal-costcenter-matrix.sql bereit.
Unter Verwaltung → Mitarbeiter → Lagerrechte sind Hauptlagerzugänge, Unterlagereinschränkungen, Lesemodus und das PHP-Artikelverwaltungsflag pflegbar. Lagerbuchungen prüfen diese Zugänge erneut; bestehende fremde Freigaben und geschützte Konten bleiben geschützt.
Adressvorschläge über Google Places ergänzen Kunden-, Kurier- und Kostenstellenadressen. In der Kundenliste lassen sich Kostenstellen einblenden, nach Namen filtern und direkt bearbeiten. Der Google-Schlüssel bleibt im Backend.
Der geprüfte Menüumfang und noch fehlende Funktionen stehen in docs/MENUE-ABGLEICH.md. Freie Artikelpositionen, Alt-Tastenkürzel, Nachhaltigkeitsdaten und Erfassung in freigegebenen Niederlassungen sind ebenfalls implementiert.
Neu aus dem Menüabgleich: Unter Verwaltung sind Feiertage, CLASSIC-/BWV-Anfahrtszeiten, Gruppen mit Mitgliedschaften und das Impressum angebunden. Die Auswertung berechnet komplette Zeiträume direkt auf dem Bestand, mit Niederlassungen, Vergleichszeiträumen, Gruppierungen, KM/CO₂e und CSV. Der genaue Funktionsumfang und die noch offenen PHP-Funktionen stehen im Menüabgleich.
Die vollständige Ablösung der PHP-Anwendung ist weiterhin offen. Native Auftragsbearbeitung, Fahrzeugzuweisung, Abschluss, Storno/Gegenbuchung, Kunden-/Kurier-/Kostenstellen-/Fahrzeugpflege sowie Abrechnungsübersichten, Rechnungszuordnung, CSV und PDF-Leistungsübersichten sind ergänzt. Rechnungserstellung, Buchhaltungsläufe und die sechs nativen Automatikdienste sind inzwischen implementiert. Die Betriebsumschaltung der Automatikdienste ist noch ausstehend; der Kategorie-9-Einnahmenexport benötigt weiterhin die Originaldefinition. Der aktuelle Stand und die Prüfgrenzen stehen in docs/PRUEFSTATUS.md. Foto-Workflow-Sets mit Stationsvorgaben, automatische Kilometer-/PZM- und Fernverkehrskalkulation sowie Auslandsabholung sind angebunden. Die Routenfunktionen benötigen den bestehenden PZM-Dienst (PZM_URL) und einen Google-Schlüssel (GOOGLE_MAPS_API_KEY); der Fotodienst kommt aus dem Bestandsparameter URL_CS_PHOTO_WORKFLOW, die eingebettete Karte aus URL_GEOCORDS. Die automatische Adressauflösung gewählter Kartenpunkte verwendet ebenfalls den Google-Schlüssel. Umfang, Betriebsabhängigkeiten und Prüfnachweise stehen in docs/AUFTRAGSERFASSUNG.md.
Die vollständige Analyse und Abdeckungsmatrix stehen in docs/ALTSYSTEM-ANALYSE.md.
Start
Benötigt: Java 21, Node.js 22.12 oder neuer, Python 3 sowie der Netzwerk-/VPN-Zugang zur bestehenden Datenbank. Maven wird über den mitgelieferten Wrapper verwendet.
cd /Users/svencarstensen/Documents/Entwicklung/Source/Kunden/Assecutor/votianNG
./dev.sh
Die Oberfläche ist anschließend unter http://localhost:5173 erreichbar. Strg+C beendet die gestarteten Prozesse. Das Backend-Protokoll liegt in work/backend.log.
dev.sh, run.sh und die Datenbankdiagnose laden DB_URL, DB_USER und DB_PASSWORD aus der lokalen .env im Projektstamm. Bereits gesetzte Umgebungsvariablen haben Vorrang. Die Zugangsdaten werden ausschließlich als Prozessumgebung an Java übergeben, ohne sie auszugeben oder in Prozessargumente zu schreiben.
Die vorhandenen lokalen Datenbankwerte wurden nach .env übernommen. Bei einer neuen Einrichtung .env.example nach .env kopieren und die drei Werte eintragen. DB_URL enthält den vom jeweiligen Rechner erreichbaren Host bzw. die VPN-Adresse, Port und Datenbanknamen:
DB_URL=jdbc:mariadb://DB-HOST:3306/DB-NAME?connectTimeout=3000&socketTimeout=5000&allowMultiQueries=false
Diese Zeile gehört in .env. Werte werden wörtlich gelesen; $, #, = und Backslashes im Passwort bleiben erhalten. Die Datei wird nicht als Shellskript ausgeführt und sollte nicht mit source geladen werden. Für die zusätzliche Verwendung mit Docker die Werte ohne umschließende Anführungszeichen schreiben. .env und lokale Varianten sind per .gitignore ausgeschlossen; .env.example enthält ausschließlich Platzhalter.
Nur die Konfiguration ohne Datenbankverbindung prüfen:
python3 scripts/run-backend.py --check-config
Die neue Anwendung liest connection.local.json und die PHP-Konfiguration nicht mehr als Zugangsdatenquelle. Der PHP-Altbestand bleibt eigenständig; seine Konfiguration wird durch diese Umstellung nicht verändert.
Debuggen in VS Code
Den Projektstamm votianNG in VS Code öffnen. Benötigt werden das Extension Pack for Java, Java 21 und Google Chrome. Die Frontend-Abhängigkeiten einmalig mit npm ci --prefix frontend installieren und die lokale .env wie oben beschrieben einrichten.
Unter Ausführen und Debuggen die Konfiguration VoTiAn NG: Backend + Frontend auswählen und mit F5 starten. Das Backend wird vom Java-Debugger gebaut und mit der .env auf 127.0.0.1:8080 gestartet. Vite läuft auf 127.0.0.1:5173 und öffnet automatisch Chrome mit aktiviertem Browser-Debugger. Haltepunkte sind sowohl in Java als auch in React/TypeScript möglich. Die beiden Komponenten lassen sich über die Einzelkonfigurationen auch separat starten.
Die Ports sind im Debugstart festgelegt, passend zum Vite-API-Proxy. Ein bereits laufendes dev.sh vorher beenden. Beim Stoppen einer Komponente des gemeinsamen Starts werden beide Server und die zugehörige Browser-Debugsitzung beendet.
Der Java-Debugger liest .env mit dem üblichen dotenv-Parser: $ bleibt wörtlich, ein unquotiertes # beginnt dagegen einen Kommentar. Bei solchen Werten sind Anführungszeichen erforderlich; für Docker gegebenenfalls eine separate Datei mit unquotierten Werten verwenden.
Gebündelte Anwendung
./build.sh
./run.sh
build.sh baut React, integriert die gebauten Dateien in das Spring-Boot-JAR und führt die Backend-Tests aus. Die gebündelte Oberfläche ist über http://localhost:8080 erreichbar. run.sh übernimmt dieselbe .env wie der Entwicklungsstart. Build und Unit-Tests laden keine lokalen Datenbankzugangsdaten.
Docker-Image veröffentlichen
docker login registry.assecutor.org
./docker_push.sh
# Alternativer Image-Tag:
./docker_push.sh 0.1
Das Skript führt build.sh einschließlich Tests aus und pusht das Java-21-Image mit gebündeltem React-Frontend für linux/amd64 nach registry.assecutor.org/votianng. Ohne Argument verwendet es die Version aus backend/pom.xml; ein expliziter Tag ändert nur den Image-Tag. Das Ziel lässt sich über REGISTRY_IMAGE überschreiben.
Im Container lauscht die Anwendung auf Port 8080 an 0.0.0.0. Die .env wird beim Containerstart übergeben:
docker run --rm --env-file .env -p 127.0.0.1:8080:8080 registry.assecutor.org/votianng:0.1
Die vorhandene PHP-Anwendung und .env sind nicht im Image enthalten. Bei Bedarf LEGACY_APP_URL zusätzlich als Umgebungsvariable übergeben. Beim direkten Start über Maven oder java -jar wird .env über den gemeinsamen Wrapper geladen, beispielsweise aus backend/: python3 ../scripts/env_config.py ./mvnw spring-boot:run.
Anmeldung und Reverse Proxy
VoTiAn verwendet serverseitige Sitzungen mit dem Cookie VOTIANSESSION. Die Systemzugangsdaten werden einmal beim Anmelden übertragen; die Oberfläche speichert sie nicht im Browser und setzt keinen Authorization-Header. Ein zusätzlicher HTTP-Basic-Passwortschutz im Nginx Proxy Manager kann deshalb bestehen bleiben: Dessen Zugangsdaten verwaltet der Browser unabhängig von der VoTiAn-Sitzung. Die Abmeldung in VoTiAn beendet nur die Sitzung der Anwendung.
Für https://votian.app muss der Proxy die Oberfläche und /api/** an denselben VoTiAn-Dienst weiterleiten, Cookies durchlassen und das ursprüngliche HTTPS-Protokoll als X-Forwarded-Proto: https übermitteln. Die Anwendung wertet diese Header mit der nativen Tomcat-Unterstützung aus. Nur der vertrauenswürdige Proxy sollte den Backend-Port erreichen; er muss vom Client übermittelte Forwarded-Header ersetzen. Tomcat vertraut standardmäßig internen Proxyadressen. Bei anderer Netzwerkstruktur die erlaubten Proxyadressen gezielt über SERVER_TOMCAT_REMOTEIP_INTERNAL_PROXIES konfigurieren; die Prüfung nicht durch einen leeren Wert abschalten. Siehe Spring Boot: Reverse Proxy.
Im HTTPS-Produktionsbetrieb zusätzlich SERVER_SERVLET_SESSION_COOKIE_SECURE=true als Container-Umgebungsvariable setzen, zum Beispiel mit docker run --env-file .env -e SERVER_SERVLET_SESSION_COOKIE_SECURE=true .... Damit wird das Sitzungscookie ausschließlich über HTTPS übertragen. Der direkte HTTP-Aufruf per Server-IP ist dann kein Anmeldeweg mehr. Für die lokale Entwicklung über HTTP diese Einstellung weglassen; ohne explizite Vorgabe richtet sich Secure nach dem vom Backend erkannten Request-Protokoll.
Das Sitzungscookie ist HttpOnly und SameSite=Lax. Standardmäßig ist nach 30 Minuten ohne Anfrage an die Sitzung eine erneute Anmeldung erforderlich. Sitzungen liegen im Arbeitsspeicher des Backends und gehen bei einem Neustart verloren; mehrere Instanzen benötigen Sitzungsaffinität oder eine später einzurichtende gemeinsame Sitzungsablage. Benutzerrechte werden bei der Anmeldung geladen und gelten bis zur Abmeldung bzw. zum Sitzungsablauf. Änderungen an Konten oder Rechten beenden vorhandene Sitzungen nicht sofort.
Konfiguration
| Variable | Bedeutung |
|---|---|
DB_URL, DB_USER, DB_PASSWORD |
Datenbankzugang aus .env; Prozessumgebung hat Vorrang |
LEGACY_APP_URL |
Basis-URL der vorhandenen PHP-Anwendung, damit Aktionen die bestehenden Fachabläufe öffnen |
PORT |
Spring-Boot-Port, Standard 8080 |
SERVER_ADDRESS |
Bind-Adresse, Standard 127.0.0.1 |
FORWARD_HEADERS_STRATEGY |
Auswertung der Proxy-Header, Standard native |
SERVER_SERVLET_SESSION_COOKIE_SECURE |
Im HTTPS-Produktionsbetrieb true setzen; lokal über HTTP weglassen |
SESSION_TIMEOUT |
Ablauf bei Inaktivität, Standard 30m |
MAX_RESULTS |
Obergrenze des geladenen Listenausschnitts, Standard 500 |
Die neue Anwendung ist standardmäßig nur lokal erreichbar. Für einen Serverbetrieb sind TLS und die korrekte Weiterleitung über einen Reverse Proxy zu konfigurieren. Es werden keine Standardkonten angelegt. Kunden- und Kurierlogins sind noch keine portierten Zugangswege; vorhandene Zentralenrechte werden nicht pauschal erweitert.
Prüfungen
Die Unit-Tests der PHP-Altanwendung prüfen den aktuellen Serverquellstand in einem separaten Container mit dem Image der Altanwendung. Auf 172.16.0.158 starten sie mit /data/0/votian-test/tests/unit/run-server.sh. Testumfang, Isolation und bekannte Kalenderfehler sind in der verlinkten Anleitung dokumentiert.
Die Oberflächentests der PHP-Altanwendung prüfen die echte Auftragserfassung auf test.sb.assecutor.de mit Playwright und Firefox. Start auf dem Server: /data/0/votian-test/tests/ui/run-server.sh; lokal: npm run test:legacy-ui. Ein tatsächlicher Speichertest wird mit --create-order aktiviert und verwendet ausschließlich die bezeichneten Assecutor-Testkunden.
python3 -m unittest discover -s tests -p 'test_*.py' -v
cd backend && ./mvnw test
Standardtests verwenden Mocks. Optionale Integrationsprüfungen sind ausschließlich für die disposable MariaDB unter 127.0.0.1:33079/crm_qa freigeschaltet (CRM_INTEGRATION_URL); dort erzeugen sie isolierte Testtabellen. Sie führen keine Schreibzugriffe gegen den Bestand aus. Ein erfolgreicher Build ersetzt nicht die noch notwendige Prüfung mit aktivem VPN, dem korrekten Datenbankendpunkt und fachlichen Vergleichsfällen aus dem Altbetrieb.
Die optionale Diagnose python3 scripts/check-database.py startet das gebaute Backend-JAR im Modus --database-check, ohne Webserver. Verbindung und benötigte Tabellenspalten werden ausschließlich über denselben DatabaseService mit SELECT auf Serverversion und information_schema geprüft. Die Diagnose liest keine Auftrags-/Benutzerdatensätze, gibt keine Zugangsdaten aus und beendet danach den Prozess. Ein eigener Python-Datenbanktreiber ist nicht mehr erforderlich.
Datenbank-Serviceschicht
Alle Zugriffe der neuen Anwendung laufen über backend/src/main/java/de/stadtbote/votian/database/DatabaseService.java: Anmeldung, Aufträge und Stationen, Kunden, Kuriere, CSV-Daten sowie Verbindungs- und Schemadiagnose. Nur dieser Service verwendet JDBC und steuert Datenbanktransaktionen. Auftragsanzahl, Liste und Stationen werden in einer gemeinsamen Lesetransaktion gelesen. SQL-Werte werden als Parameter gebunden.
Die Auftragserfassung öffnet mit writeTransaction(...) eine explizite Schreibtransaktion. Nur innerhalb dieser Grenze sind insert, update und lockRows erlaubt. Die freigegebenen Verwaltungsprozesse, Auftragsbearbeitung und Stammdatenpflege verwenden dieselbe Schreibgrenze. Architekturtests prüfen Java-Bytecode und Python-Launcher auf Direktzugriffe außerhalb dieser Schicht. Die vorhandene PHP-Anwendung ist von dieser Umstellung nicht betroffen.
Projektstruktur
votian/ Historischer lokaler PHP-Stand, keine Referenz mehr
frontend/ React, TypeScript, Vite und Stadtbote-Oberfläche
backend/ Spring Boot, JDBC und vorhandene Benutzer-/Rechtestruktur
docs/ Analyse, Fachregeln, Abdeckung und weitere Abnahme
scripts/ Lokaler Start mit vorhandener Konfiguration
tests/ Zusätzliche Konfigurations- und Browserprüfungen
Zeitgesteuerte Angebotsrückrufe
OFFER_REVOCATION_WORKER_ENABLED=false hält den nativen Rückruf-/Sperrbereinigungsdienst im PHP-Parallelbetrieb aus. Zur Umschaltung zuerst die Rückruf- und Sperrbereinigungsblöcke in include/autoranking_main.php außer Betrieb nehmen, dann den Schalter setzen und NG neu starten. Der Angebotslauf kann separat mit dem nachfolgend beschriebenen nativen Dienst abgelöst werden. Die Fachparameter AUTORANKING_REVOCATION_ENABLED, AUTORANKING_REVOKE_JOB_MODE und die automatischen/manuellen Minuten-/Sekundenfristen stammen direkt aus der Dispositionsniederlassung. Keine globale Vererbung für diesen PHP-Pfad. Nur transaktionale Schreibziele werden verarbeitet; ungültige Zuordnungen und Fristen bleiben zur Klärung stehen. Die Fristanzeige unter Kurierauswahl → Vermittlung ändern liest diese Einstellungen auch bei ausgeschaltetem NG-Worker.
Automatische Angebote
AUTO_OFFER_WORKER_ENABLED=false hält den nativen Angebotslauf aus. Die lesende Kandidatenvorschau ist bereits unter Kurierauswahl → Vermittlung ändern verfügbar. Vor Aktivierung zuerst den Angebotsblock von include/autoranking_main.php beenden; anschließend NG mit AUTO_OFFER_WORKER_ENABLED=true neu starten. Rückrufe und Freigabe überholter Kuriersemaphoren müssen weiterhin durch genau einen zuständigen Dienst verarbeitet werden (separater Schalter OFFER_REVOCATION_WORKER_ENABLED). Der bestehende Geräteabruf und Auditverbraucher bleiben erforderlich; der vorhandene keepalive-Eintrag autoranking wird vom aktivierten Angebotslauf fortgeschrieben.
Der Angebotslauf liest die Fachparameter der Dispositionsniederlassung: Aktivierung, Versuchslimit, Rundenanzahl, Vermittlungsmodus/-tiefe und Fahrzeugregeln. Kundenbezogene Favoriten-/Sperrschalter verwenden den jeweiligen PHP-Parameterfallback. Die Schreibziele job, courier, autoranking und genericdatacontainer benötigen InnoDB. Aufträge mit unvollständiger oder mehrdeutiger Konfiguration bleiben zur Klärung stehen. Ausführung ist alle 15 Sekunden nach Abschluss des vorherigen Durchlaufs, mit 40 Aufträgen pro Seite. Es erfolgt keine Schemaumstellung durch den Dienst.
Betriebsstatus
Verwaltung → Betriebsstatus zeigt die sechs nativen Automatikdienste mit Aktivierung, Umfang, letztem abgeschlossenen Lauf und Fehler-/Ergebniszahlen. Die Laufwerte gelten für die gesamte NG-Instanz und beginnen nach einem Neustart neu. Ein Lauf ohne Abschluss seit mehr als drei Minuten wird als überfällig gekennzeichnet; daraus folgt nicht automatisch, dass der Prozess beendet ist. Gemeinsame keepalive-Zeitstempel können von PHP oder NG stammen und sind kein Nachweis der Zuständigkeit.
Auftrags-/Kurierkennzahlen und Geräte-Abschlussmeldungen sind auf die ursprüngliche Niederlassung begrenzt. Erforderlich ist das vorhandene Mitarbeiterverwaltungsrecht (Position 3). Das Laden der Ansicht liest ausschließlich; es startet keine Dienste und versendet keine Nachrichten. Die gesonderte Prüffreigabe eines unterbrochenen Cron-Laufs wird protokolliert und erlaubt dessen nächsten regulären Termin. Fehlende Datenquellen werden einzeln als nicht verfügbar angezeigt.
Zeitgesteuerte Spring-Tasks
Die bestätigte PHP-Crontab wird über native Spring-Tasks abgelöst. Das Docker-Image aktiviert beim Start alle neun Gruppen (115 Tasks) über SPRING_TASK_GROUPS; feste Termine gelten nach Europe/Berlin. Der Container startet ausschließlich Java und benötigt weder Cron-Daemon noch PHP. SPRING_TASK_GROUPS kann auf einzelne Gruppen oder einen leeren Wert gesetzt werden. Beim direkten Java-Start bleibt LEGACY_CRON_JOBS als Fallback erhalten. Der Betriebsstatus zeigt dauerhafte Laufbelege. Die beiden nicht mehr benötigten HHA-Aufrufe entfallen nach Betreiberentscheidung vom 27.10.2026; damit sind alle weiterhin benötigten PHP-Cron-Aufrufe implementiert. Zeitpläne, Konfiguration, Änderungen gegenüber PHP und Wiederanlauf nach einem unterbrochenen Versand stehen in CRON-MIGRATION.md.