Binde Auftragshistorie an und korrigiere Kundenanzeige

Ergänzt die lesende Historien-API mit Verlaufsanzeige und ordnet Auftragskunden dem effektiven Zahler zu. Enthält VS-Code-Debugkonfiguration, Dokumentation und Regressionstests.

Validiert mit Frontend-Build, 76 Backend-Tests und 25 Browsertests. Live-Datenbankprüfung wegen fehlender Erreichbarkeit offen.
This commit is contained in:
Sven committed 2026-10-07 20:40:58 +02:00
1 parent ac5ea374b9
commit 75e2bf2bec
24 files changed
+1069 -60

No files matched your search

+12 -5
View File
@@ -4,7 +4,7 @@ Java 21, Spring Boot 3.5.16 und MariaDB JDBC. Dieser Adapter verwendet ausschlie
## Zentrale Datenbankserviceschicht
`database.DatabaseService` ist die einzige Komponente mit Zugriff auf `JdbcTemplate` und den Transaktionsmanager. `LegacyAuthenticationProvider`, `LegacyReadService` und `database.DatabaseDiagnostics` verwenden ausschließlich diesen Service; auch Integrationstests lesen den Bestand über ihn. Der Service aktiviert `setEnforceReadOnly(true)`, sodass jede Lesetransaktion mit `SET TRANSACTION READ ONLY` serverseitig abgesichert wird. Der JDBC-Hinweis allein reicht hierfür beim verwendeten MariaDB-Treiber nicht aus.
`database.DatabaseService` ist die einzige Komponente mit Zugriff auf `JdbcTemplate` und den Transaktionsmanager. `LegacyAuthenticationProvider`, `LegacyReadService`, `LegacyHistoryService` und `database.DatabaseDiagnostics` verwenden ausschließlich diesen Service; auch Integrationstests lesen den Bestand über ihn. Der Service aktiviert `setEnforceReadOnly(true)`, sodass jede Lesetransaktion mit `SET TRANSACTION READ ONLY` serverseitig abgesichert wird. Der JDBC-Hinweis allein reicht hierfür beim verwendeten MariaDB-Treiber nicht aus.
- `queryForList(sql, parameters...)` und `queryForObject(sql, type, parameters...)` führen einzelne parametrisierte SELECT-Abfragen in Lesetransaktionen aus.
- `readTransaction(work)` fasst abhängige Abfragen zusammen; Auftragsliste einschließlich Anzahl und Stationen sowie Auftragsdetails verwenden diese Grenze.
@@ -31,6 +31,7 @@ Verfügbare Konfiguration:
| --- | --- |
| `DB_URL` | JDBC-Adresse der bestehenden DB, z. B. `jdbc:mariadb://DB-HOST:3306/DB-NAME?connectTimeout=2000&socketTimeout=5000` |
| `DB_USER`, `DB_PASSWORD` | Vorhandenes Datenbankkonto; keine Zugangsdaten im Quelltext |
| `HISTORY_DB_SCHEMA` | Bestehendes Protokollschema auf demselben Server, Standard `phoenix_log`; benötigt SELECT-Rechte für `log` und `logoperation` mit demselben Datenbankkonto. Name aus Buchstaben, Ziffern und Unterstrichen, maximal 64 Zeichen |
| `LEGACY_APP_URL` | Optionale HTTP(S)-Basisadresse der vorhandenen PHP-Anwendung |
| `SERVER_ADDRESS` | Standard `127.0.0.1` |
| `PORT` | Standard `8080` |
@@ -78,8 +79,9 @@ Die ursprüngliche Niederlassung `user.hq_id` begrenzt alle Abfragen. Bei Auftr
| `POST /api/login` | CSRF-geschützte Anmeldung mit Formularfeldern `username` und `password`; `200` mit Benutzerinformationen |
| `POST /api/logout` | CSRF-geschützte Abmeldung, Sitzung ungültig machen und Cookie löschen; `204` |
| `GET /api/me` | Bestehendes Konto, Niederlassung, verfügbare Lesebereiche |
| `GET /api/orders` | Aufträge, Filter `q`, `status`, `customerId`, `courierId`, `from`, `to` |
| `GET /api/orders` | Aufträge mit Kundennamen; Filter `q`, `status`, `customerId` (effektiver Zahler), `courierId`, `from`, `to` |
| `GET /api/orders/{id}` | Auftrag derselben Erfassungsniederlassung mit Tourstationen |
| `GET /api/orders/{id}/history` | Vollständige Historie eines aktuellen Auftrags derselben Erfassungsniederlassung; Recht 4; aufsteigend nach Ereigniszeit und Log-ID |
| `GET /api/orders/export.csv` | Gefilterter UTF-8-Export, zusätzlich Recht 5; verweigert einen unvollständigen Export oberhalb der Ergebnisgrenze |
| `GET /api/customers`, `GET /api/couriers` | Stammdaten, optionaler Suchparameter `q` |
| `GET /api/invoices` | 501, Bestands-Abrechnungsprozess noch nicht portiert |
@@ -89,13 +91,16 @@ Listen liefern Arrays. Auftragslisten senden zusätzlich `X-Total-Count` und `X-
## Fachliche Zuordnung und offene Arbeit
- Auftraggeber aus `job.csc_id → costcenter.cs_id`; Rechnungskunde separat als `payerCustomerId` aus `csc_id_payer`.
- Die Kundenanzeige (`customerId`, `customerName`, `costCenter`), Namenssuche und der Kundenfilter folgen `job.csc_id_payer → costcenter → customer → company`. Bei Barzahlung (`jb_payment=2`) mit positiver `csc_id_payer_cash` wird für diese Anzeige der tatsächliche Barzahler verwendet; `payerCustomerId` behält den Kunden aus `csc_id_payer`, also gegebenenfalls den technischen Systembarzahler.
- `job.csc_id` bezeichnet die erfassende Kundenkostenstelle und ist bei zentral erfassten Aufträgen regulär `0`. Dieser Bezug bleibt separat als `ordererCustomerId` erhalten. Er darf nicht als allgemeiner Kundenbezug verwendet werden. Quelle: `jobs/job_edit.php:38–48`, `jobs/job_options.php:125–128`, `:1706–1711` und `admin/jb_list.php:1022–1023`.
- Der Kundenname wird mit jedem sichtbaren Auftrag geliefert; die Anzeige ist damit unabhängig vom Ergebnislimit oder Leserecht der separaten Kundenstammliste. Die Auftragsabfrage bleibt über `job.hq_id` und Recht 4 begrenzt. Nicht zuordenbare Kunden (beispielsweise Kostensplit ohne eindeutigen Zahler) bleiben `null` und werden in der Oberfläche ausdrücklich als nicht zugeordnet angezeigt.
- Kundenpreis aus `jb_totalprice` (Netto einschließlich Zuschlägen), Kurierpreis aus `jb_cr_price`; keine neu erfundene Tarifierung.
- Status 0/1/2/8/9 bleiben zusätzlich als `legacyStatus`/`legacyStatusLabel` erhalten. Die UI fasst 8/9 unter Disposition zusammen.
- `jb_storno` bleibt als `legacyStorno` erhalten. 3/4 sind Gegenbuchungen und werden ausdrücklich als Storno-Gutschriften markiert (`isCreditNote`).
- Tourrichtung 1=Abholung, 2=Zustellung, 3=beides (`BOTH`), 0=positionsabhängige Legacy-Regel.
- Leistungen aus der Bitmaske `jb_service`, wobei `metatype.mt_sort` die Bitposition ist. Fahrzeugtypen werden über `metatype` gelesen.
- Historie liegt im Bestand in einer separaten Log-Datenbank. Der Adapter erfindet keine Audit-Ereignisse; das Feld bleibt aktuell leer.
- Historie wird beim Öffnen von „Verlauf“ separat über `/api/orders/{id}/history` aus `phoenix_log.log` geladen. `logoperation` liefert Vorgangsbezeichnungen, `user` den Bearbeiter. Die Oberfläche zeigt die neuesten Ereignisse zuerst und unterscheidet Laden, eine erfolgreich gelesene leere Historie und einen Zugriffsfehler mit Wiederholungsmöglichkeit. Das bisherige `history`-Feld in Auftragslisten/-details bleibt leer; maßgeblich ist der eigene Historien-Endpunkt.
- Die Historienberechtigung folgt `job.hq_id` und Recht 4; Ereignisse anderer Niederlassungen zum selben sichtbaren Auftrag bleiben erhalten. Fehlende/fremde Aufträge liefern 404, Datenbank-/Schemarechtefehler 503. Die Historie wird weder erfunden noch bei einem Fehler durch ein leeres Ergebnis ersetzt. Legacy-Details werden als Text mit bekannten deutschen Feldnamen angezeigt, unbekannte Angaben bleiben erhalten; gespeicherter HTML-Code wird nicht ausgeführt. Es erfolgen keine Schemaänderungen oder Datenbankschreibzugriffe.
- Abrechnung im Bestand ist keine nachgewiesene einfache Invoice-CRUD-Tabelle. Belege, Gegenbuchungen, Zahlungsstatus, Tarifierung, Auftragsanlage/Änderung und Fahrerkommunikation bleiben bis zur vollständigen Portierung im vorhandenen PHP-System. Optional verlinkt die React-Oberfläche diese Module.
- Archivtabellen und Mehrniederlassungsrechte sind noch nicht eingebunden. Die bestehende Anwendung bleibt für diese Funktionen erforderlich.
@@ -105,10 +110,12 @@ SQL-/Mapping-Quellen im vorhandenen Code: `include/mcglobal.inc.php`, `include/d
`./mvnw test` führt Tests ohne Bestandszugriff aus: bestehende Anmeldung und Rechte, Sitzungs- und CSRF-Verhalten, HQ-Abgrenzung, SQL-Parameterbindung, Nichtverfügbarkeit, Verbot fachlicher Schreibpfade, serverseitige Vorbereitung der Lesetransaktion, Transaktions-/Fehlerverhalten, Schemadiagnose, Legacy-Status/Preis-/Tour-Mapping einschließlich JDBC-Boolean-Flags und CSV-Schutz. Normale Maven-Builds und Unit-Tests laden `.env` nicht. Ein Architekturtest prüft die kompilierten Java-Abhängigkeiten und Python-Launcher auf direkte Datenbankzugriffe außerhalb des Services. Nur dessen eigener Unit-Test darf JDBC mocken. Es wird keine Testdatenbank angelegt und keine Bestandsdatenbank verändert.
Mit `VOTIAN_VERIFY_EXISTING_DB=true` und den konfigurierten `DB_URL`, `DB_USER`, `DB_PASSWORD` wird zusätzlich `LegacyDatabaseReadIntegrationTest` ausgeführt. Er prüft die drei Listen-APIs mit echten SELECT-Abfragen über MockMvc und die numerische JDBC-Abbildung von `TINYINT(1)`. Die Authentifizierung wird nur in diesem Test simuliert; die echte Anmeldung wird damit nicht getestet. Die gewählte Niederlassung muss vorhandene Aufträge, Kunden und Kuriere enthalten. Ohne ausdrückliche Aktivierung bleibt dieser Test deaktiviert.
Mit `VOTIAN_VERIFY_EXISTING_DB=true` und den konfigurierten `DB_URL`, `DB_USER`, `DB_PASSWORD` wird zusätzlich `LegacyDatabaseReadIntegrationTest` ausgeführt. Er prüft die drei Listen-APIs mit echten SELECT-Abfragen über MockMvc, die numerische JDBC-Abbildung von `TINYINT(1)` und den Historien-Endpunkt gegen das konfigurierte Protokollschema. Für die Historie muss einer der neuesten 100 aktuellen Aufträge Protokolleinträge besitzen; geprüft werden Anzahl und Antwortformat ohne Ausgabe der Protokollinhalte. Die Authentifizierung wird nur in diesem Test simuliert; die echte Anmeldung wird damit nicht getestet. Die gewählte Niederlassung muss vorhandene Aufträge, Kunden und Kuriere enthalten. Ohne ausdrückliche Aktivierung bleibt dieser Test deaktiviert.
Für diesen bewusst aktivierten Integrationstest mit der lokalen `.env` im Verzeichnis `backend/` ausführen:
```sh
VOTIAN_VERIFY_EXISTING_DB=true python3 ../scripts/env_config.py ./mvnw test
```
Der Integrationstest vergleicht außerdem die Kundenprojektion der neuesten 100 Aufträge mit unabhängigen Abfragen der Zahler-, Barzahler- und Erfassungskundenbezüge. Auch hierbei enthalten Testausgaben keine Kunden- oder Auftragsinhalte.