# Entwicklungskonzept DHL Modul Stand: 27.05.2026 ## Ziel Diese Datei ist die aktuelle Arbeitsgrundlage fuer die Weiterentwicklung des DHL Moduls. Die bisherigen Markdown-Dateien in diesem Ordner dokumentieren abgeschlossene oder ueberholte Zwischenstaende, insbesondere den frueheren SDK-Ansatz, Paketinstallation, SSL/cURL-Fixes und einzelne abgeschlossene Entwicklungsschritte. Aktuelle Anforderungen kommen aus `docs/dhl/Anpassung DHL Modul.md`: - Internationaler Versand ausserhalb Deutschlands, insbesondere Oesterreich und Spanien. - Freies Feld fuer Sendungsreferenz oder interne Hinweise wie "Nachlieferung". - Adressvalidierung vor Labelerstellung, damit fehlerhafte Labels und kostenpflichtige Stornos vermieden werden. - Storno im DHL Cockpit pruefen und stabilisieren. - Gewicht von Kompensationsprodukten in das DHL-Paketgewicht einrechnen. - Tracking-Mails auf Rhythmus, Ausloeser und Mehrfachversand pruefen. - Tracking-Codes in Admin, User-Portal und User-N-Portal sichtbar machen. - Tracking-Mail-Versand nur bei Statusaenderung. - DHL-Umstellung von `V62WP` Warenpost auf `V62KP` DHL Kleinpaket bis spaetestens 31.05.2026. ## Aktueller technischer Stand Das produktive Modul basiert auf dem Paket `packages/acme-laravel-dhl` und verwendet die DHL REST API ueber `Acme\Dhl\Support\DhlClient`. Die zentrale Tabelle ist `dhl_package_shipments`. Wichtige produktive Einstiegspunkte: - `app/Http/Controllers/DhlShipmentController.php` - `app/Services/DhlShipmentService.php` - `app/Services/DhlModalService.php` - `app/Services/DhlDataHelper.php` - `app/Services/DhlTrackingService.php` - `packages/acme-laravel-dhl/src/Services/ShippingService.php` - `packages/acme-laravel-dhl/src/Services/ReturnsService.php` - `packages/acme-laravel-dhl/src/Models/DhlShipment.php` - `config/dhl.php` Historische Dokumente erwaehnen teilweise alte Strukturen wie `App\Models\DhlShipment`, `dhl_shipments` oder einen konsolidierten `DhlApiService`. Diese Angaben sind nicht mehr massgeblich, ausser sie werden explizit in aktuellem Code noch referenziert. Aktuell gibt es genau dort noch Altlasten, die bereinigt werden muessen. ## Offensichtliche Befunde ### 1. Produktkuerzel `V62WP` ist veraltet `V62WP` ist weiterhin in Konfiguration, Admin-Settings, Validierung, Produktauswahl und Sprachdateien vorhanden. DHL verlangt die Umstellung auf `V62KP`. Betroffene Stellen: - `config/dhl.php` - `app/Http/Controllers/SettingController.php` - `app/Services/DhlModalService.php` - `packages/acme-laravel-dhl/src/Services/ShippingService.php` - `resources/lang/*/dhl.php` Risiko: Kleinpaket/Warenpost-Labels koennen nach der DHL-Frist abgelehnt werden. ### 2. Async Tracking verwendet veraltetes Model `app/Jobs/TrackShipmentJob.php` importiert `App\Models\DhlShipment`, dieses Model existiert im aktuellen System nicht mehr. Die produktive DHL-Integration verwendet `Acme\Dhl\Models\DhlShipment`. Risiko: Asynchrones Tracking bricht zur Laufzeit, sobald Queue-Tracking genutzt wird. ### 3. Statuswerte fuer Storno sind inkonsistent Im Paket wird bei erfolgreichem Storno `canceled` gesetzt. Andere Stellen, Uebersetzungen und historische Dokumente verwenden `cancelled`. Risiko: Filter, Badges, Terminal-Status, Uebersetzungen und Storno-Buttons verhalten sich uneinheitlich. ### 4. Storno ist technisch vorhanden, aber nicht robust genug Storno laeuft ueber `DELETE /parcel/de/shipping/v2/orders/{shipmentNumber}`. Der Code prueft `canCancel()`, speichert aber Fehler nur begrenzt fachlich verwertbar. Produktspezifische Einschraenkungen wie Warenpost/Kleinpaket sind nicht sauber modelliert. Risiko: Anwender sehen generische Fehler und koennen nicht erkennen, ob ein Storno produktbedingt, statusbedingt, API-bedingt oder wegen Sandbox/Production-Mismatch scheitert. ### 5. Internationaler Versand ist nur teilweise vorbereitet `V53PAK` ist als internationales Produkt vorhanden, und einige Laender werden in 3-stellige ISO-Codes konvertiert. Dennoch gibt es keinen zentralen Produktentscheid je Zielland, keine harte Validierung nicht unterstuetzter Laender und einen gefaehrlichen Fallback auf `DEU`. Risiko: Sendungen nach Oesterreich, Spanien oder weitere Laender koennen falsche Produktcodes, falsche Abrechnungsnummern oder falsche Laendercodes erhalten. ### 6. Adressvalidierung ist nur formal Aktuell prueft das Modul Pflichtfelder, Hausnummern und einfache PLZ-Regeln. Eine echte Pruefung, ob Strasse, PLZ und Ort postalisch existieren, findet nicht statt. Empfohlene Loesung: DHL/Post & DHL `DATAFACTORY AUTOCOMPLETE 2.0` fuer DE/AT/CH pruefen und integrieren. Alternativen fuer breiteren Laenderumfang: Loqate, Google Address Validation oder HERE. Wichtig ist eine harte Sperre bei nicht versandfaehigen Adressen vor Labelerstellung. ### 7. Referenzfeld ist API-seitig vorhanden, aber nicht im Cockpit nutzbar `ShippingService` kann `reference` nach `refNo` mappen. `DhlDataHelper` setzt aktuell automatisch `Order-{id}`. Risiko: Admins koennen Hinweise wie "Nachlieferung" nicht strukturiert am Label/API-Auftrag hinterlegen. ### 8. Gewicht von Kompensationsprodukten fehlt Kompensationsprodukte werden im Warenkorb mit Gewicht `0` abgelegt, damit die Kompensationslogik nicht beeinflusst wird. Das DHL-Gewicht kommt aus `ShoppingOrder->weight` und enthaelt dieses Produktgewicht dadurch nicht. Risiko: DHL-Label wird mit zu geringem Paketgewicht erstellt. ### 9. Tracking-Mail-Logik ist grundsaetzlich brauchbar, muss aber abgesichert werden Der Scheduler ruft stuendlich `dhl:update-tracking --days=30 --send-emails` auf. Statusabhaengige Intervalle verhindern zu viele API-Calls. Automatische Mails werden nur gesendet, wenn eine Sendung neu auf `in_transit` wechselt und noch keine Mail markiert wurde. Risiko: Statusspruenge direkt auf `out_for_delivery` oder besondere DHL-Statuscodes koennen ohne Mail bleiben. Mehrere Sendungen einer Bestellung werden teils zusammengefasst, aber die fachliche Regel muss final bestaetigt werden. ## Entwicklungskonzept ### Phase 1: Pflichtkorrekturen vor DHL-Frist 1. `V62WP` vollstaendig auf `V62KP` migrieren. 2. Neue Konfigurationskeys fuer `DHL_ACCOUNT_NUMBER_V62KP`, Admin-Setting `dhl_account_v62kp`, Dimensionen und Uebersetzungen einfuehren. 3. `ShippingService` Validierung um `V62KP` erweitern und `V62WP` entfernen oder nur noch als Legacy-Mapping fuer Altdaten anzeigen. 4. Bestehende Sendungen mit `V62WP` historisch lesbar lassen, aber neue Labelerstellung blockieren. 5. Tests fuer Produktcode-Auswahl, Validierung und Payload-Erstellung schreiben. ### Phase 2: Stabilisierung von Tracking und Storno 1. `TrackShipmentJob` auf aktuelles Model und aktuellen `DhlTrackingService` umstellen. 2. Statuswerte vereinheitlichen. Empfehlung: intern `canceled` verwenden, Uebersetzung auf Deutsch "Storniert". 3. `TERMINAL_STATUSES`, Badges, Filter und Sprachdateien entsprechend angleichen. 4. Storno-Fehler strukturiert speichern: HTTP-Status, DHL-Fehlercode, DHL-Detailtext, Zeitpunkt. 5. Admin-Feedback verbessern: nicht stornierbar wegen Status, Produkt, API-Antwort oder nicht auffindbarer DHL-Sendung. 6. Tests fuer erfolgreiche Stornierung, bereits stornierte Sendung, nicht stornierbare Sendung und API-Fehler. ### Phase 3: Internationalisierung Versand 1. Zentralen Service fuer Produkt-/Billing-Entscheidung einfuehren, z. B. `DhlProductResolver`. 2. Zielland, Produktcode, Abrechnungsnummer und erlaubte Services dort validieren. 3. Regeln initial: - `DE`: `V01PAK` oder `V62KP` - `AT`, `ES` und weitere aktivierte Laender: `V53PAK` 4. Fallback auf `DEU` entfernen. Unbekannte Laender muessen mit klarer Fehlermeldung abbrechen. 5. Cockpit-Formular: Produkt anhand Zielland vorschlagen, aber Admin-Korrektur erlauben. ### Phase 4: Adressvalidierung vor Labelerstellung 1. Neuen serverseitigen Validierungsendpunkt fuer DHL-Adressen schaffen. 2. Basisvalidierung behalten: Pflichtfelder, Land, PLZ-Format, Hausnummer, Packstation/Postnummer. 3. DHL DATAFACTORY AUTOCOMPLETE 2.0 fuer DE/AT/CH evaluieren. 4. Falls DHL fuer alle benoetigten Laender nicht ausreicht, externen Provider evaluieren. 5. UI-Status einfuehren: - gueltig: Labelerstellung erlaubt - Warnung: Admin kann bewusst fortfahren - Fehler: Labelerstellung gesperrt 6. Validierungsergebnis optional am Shipment/Order protokollieren. ### Phase 5: Referenzfeld und Admin-UX 1. Neues Formularfeld `reference` oder `shipment_reference` im DHL Cockpit. 2. Wert an `DhlDataHelper::prepareOrderData()` uebergeben. 3. `ShippingService` nutzt vorhandenes Mapping nach `refNo`. 4. Referenz im Shipment speichern, damit spaeter nachvollziehbar ist, warum eine Sendung erstellt wurde. 5. Laengenlimit der DHL API beachten, aktuell maximal 35 Zeichen. ### Phase 6: DHL-Gewicht korrekt berechnen 1. Separaten Gewichtsdienst fuer DHL einfuehren, z. B. `DhlShipmentWeightCalculator`. 2. Basis: `ShoppingOrder->weight`. 3. Fuer `shopping_order_items` mit `comp > 0` das Produktgewicht aus `Product->weight` nachladen und addieren. 4. Nur DHL-Gewicht anpassen, nicht die bestehende Warenkorb-/Versandkostenlogik. 5. Rundung und DHL-Grenzwerte je Produkt testen. ### Phase 7: Tracking-Codes und Mails fachlich finalisieren 1. Bestehende Admin-Anzeige pruefen und bei Bedarf vereinheitlichen. 2. Tracking-Code-Anzeige in User-Portal und User-N-Portal ergaenzen. 3. Mail-Regel final definieren: - automatisch nur einmal pro Sendung - nur bei relevanter Statusaenderung - mehrere Sendungen einer Bestellung sinnvoll zusammenfassen 4. Statusspruenge wie `created` direkt nach `out_for_delivery` abdecken. 5. Command `dhl:update-tracking` Tests fuer Mailausloeser und Nicht-Ausloeser ergaenzen. ### Phase 8: DHL-seitige Adressvalidierung ueber mustEncode / printOnlyIfCodable Status: umgesetzt fuer deutsche Empfaengeradressen. Ziel: - Die bestehende formale Vorabpruefung bleibt erhalten. - Bei der finalen DHL-Labelerstellung soll DHL selbst pruefen, ob die Adresse leitcodierbar bzw. versandfaehig ist. - Dafuer soll der DHL-Query-Parameter `mustEncode=true` genutzt werden. - `printOnlyIfCodable` ist laut DHL-Spezifikation der Legacy-Name dieser Funktion. - Wenn DHL die Adresse ablehnt, wird kein Etikett erstellt. - Die DHL-Fehlermeldung wird in das bestehende Modal zurueckgespiegelt, damit der Admin die Lieferadresse direkt korrigieren kann. - Laut DHL-Beschreibung ist diese Pruefung nur fuer deutsche Empfaengeradressen relevant. Geplanter Ablauf: 1. Admin oeffnet das DHL-Erstellungsmodal. 2. Die bestehende Vorabpruefung prueft weiterhin: - Pflichtfelder - PLZ-Format - Produkt-/Zielland-Kombination - Packstation-/Paketbox-Regeln - einfache Plausibilitaet 3. Beim Klick auf `Sendung jetzt erstellen` wird die Sendung an DHL uebergeben. 4. Der DHL-Request enthaelt `mustEncode=true`. 5. DHL erstellt das Label nur, wenn die Adresse codeable/leitcodierbar ist. 6. Wenn DHL die Adresse ablehnt: - es wird kein Shipment als erfolgreich erstellt markiert - die DHL-Fehlermeldung wird normalisiert - das Modal bleibt geoeffnet - die Fehlermeldung erscheint unten in der Vorabpruefung - der Admin kann die Adresse korrigieren und erneut pruefen/erstellen Geplante technische Integrationspunkte: - `packages/acme-laravel-dhl/src/Services/ShippingService.php` - Create-Shipment-Request um den Query-Parameter `mustEncode=true` erweitern. - Legacy-Begriff `printOnlyIfCodable` nur als Dokumentations-/Kompatibilitaetshinweis fuehren, nicht als neuen internen Optionsnamen verwenden. - DHL-Fehlerantworten fuer nicht codeable Adressen strukturiert auswerten. - `app/Services/DhlDataHelper.php` - Option aus Konfiguration/Settings fuer die Request-Erstellung vorbereiten. - `app/Services/DhlShipmentService.php` - DHL-Adressfehler als fachliche Validierungsfehler behandeln, nicht als generischen Systemfehler. - `app/Http/Controllers/DhlShipmentController.php` - Fehlerantwort im JSON so zurueckgeben, dass das Modal sie anzeigen kann. - `resources/views/admin/dhl/modal_create_shipment.blade.php` - DHL-Fehler bei Labelerstellung in der bestehenden Vorabpruefungsbox anzeigen. - Keine separate Browser-Alert-Meldung. - `config/dhl.php` und/oder DHL-Settings - Einstellung z. B. `print_only_if_codeable` vorbereiten. - Standard fachlich klaeren: fuer Produktion bevorzugt aktiv, fuer Tests/Sandbox ggf. konfigurierbar. Fachliche Entscheidung: - Die formale Pruefung bleibt vor DHL bestehen, damit offensichtliche Fehler frueh im Modal sichtbar sind. - `mustEncode=true` ist die finale DHL-seitige Absicherung direkt bei der Labelerstellung. - Eine abgelehnte DHL-Adresse blockiert die Labelerstellung. - Provider-Ausfall oder API-Fehler muss von einer fachlichen DHL-Adressablehnung unterscheidbar sein. - Die Fehlermeldung soll fuer Admins handlungsorientiert sein, z. B. `DHL kann diese Adresse nicht leitcodieren. Bitte Straße, Hausnummer, PLZ und Ort pruefen.` - Fuer nicht-deutsche Empfaengeradressen darf `mustEncode` nicht als vollwertige externe Adressvalidierung dargestellt werden, solange DHL diese Einschraenkung in der API-Dokumentation nennt. Offene Klaerung nach Implementierung: - Verhalten in Sandbox und Produktion. - Welche DHL-Status-/Fehlercodes fuer nicht codeable Adressen zurueckkommen. - Ob `mustEncode` fuer alle genutzten Produkte gilt: - `V01PAK` - `V62KP` - `V53PAK` - Ob internationale Sendungen ignoriert werden, Warnungen liefern oder anders behandelt werden. - Ob bestehende manuelle Admin-Uebersteuerung fachlich erlaubt sein soll oder DHL-Ablehnung immer hart blockiert. ## Empfohlene Reihenfolge 1. `V62WP` -> `V62KP`, Statuswerte und `TrackShipmentJob` korrigieren. 2. Storno stabilisieren und bessere Fehlermeldungen im Cockpit anzeigen. 3. Internationalen Produktresolver einbauen. 4. Referenzfeld und Gewichtskorrektur umsetzen. 5. Adressvalidierung integrieren. 6. Tracking-Anzeigen und Mailregeln final testen. 7. DHL-seitige Adressvalidierung ueber `mustEncode=true` integrieren, sobald API-Verhalten und Fehlerrueckgaben geklaert sind. ## Teststrategie - Feature-Tests fuer Controller-Endpunkte: Label erstellen, Storno, Tracking-Mail, Tracking-Update. - Unit-Tests fuer Produktresolver, Gewichtskalkulation und Adressvalidierung. - HTTP-Fakes fuer DHL API Responses inklusive Fehlerfaelle. - Regression-Test fuer `V62KP` Payload. - Command-Test fuer `dhl:update-tracking --send-emails`. ## Entwicklungsprotokoll Dieser Abschnitt dokumentiert die tatsaechlich umgesetzten Entwicklungsschritte. Jede Phase wird hier nach Abschluss mit Ziel, betroffenen Dateien, fachlicher Entscheidung und Verifikation ergaenzt. ### 27.05.2026 - Phase 1: Pflichtkorrekturen vor DHL-Frist Status: abgeschlossen. Ziel: - Neue DHL-Labels duerfen nicht mehr mit `V62WP` Warenpost erstellt werden. - `V62KP` DHL Kleinpaket wird als neues Produkt fuer nationale Kleinpaket-Sendungen eingefuehrt. - Historische Sendungen mit `V62WP` bleiben im System lesbar. Umsetzung: - `config/dhl.php` - `DHL_ACCOUNT_NUMBER_V62KP` als neuer Konfigurationskey eingefuehrt. - `V62KP` in `account_numbers` und `dimensions` aufgenommen. - `V62WP` aus der produktiven Konfiguration fuer neue Label entfernt. - `app/Http/Controllers/SettingController.php` - DHL-Konfiguration liefert nun `account_numbers.V62KP` und `dimensions.V62KP`. - Altes Datenbank-Setting `dhl_account_v62wp` wird nur noch als Fallback genutzt, falls `dhl_account_v62kp` noch nicht gepflegt ist. - Altes Default-Produkt `V62WP` wird beim Lesen auf `V62KP` normalisiert. - `resources/views/admin/settings/index.blade.php` - Admin-Auswahl fuer Standard-Produkt von `V62WP - Warenpost National` auf `V62KP - DHL Kleinpaket` umgestellt. - Admin-Feld `dhl_account_v62kp` eingefuehrt. - Bestehender Wert aus `dhl_account_v62wp` wird im Formular als Fallback angezeigt, damit Bestandskonfigurationen nicht leer starten. - `app/Services/DhlModalService.php` - Produktauswahl im DHL-Cockpit bietet `V62KP - DHL Kleinpaket` statt `V62WP - DHL Warenpost National` an. - Fallback-Produktsatz ebenfalls auf `V62KP` umgestellt. - `packages/acme-laravel-dhl/src/Services/ShippingService.php` - Validierung fuer neue Label erlaubt `V62KP`. - `V62WP` wird fuer neue Label abgelehnt. - Billing-Nummer-Aufloesung nutzt automatisch den neuen Setting-Key `dhl_account_v62kp`. - `resources/lang/de/dhl.php`, `resources/lang/en/dhl.php`, `resources/lang/es/dhl.php`, `resources/lang/fr/dhl.php` - `V62KP` als DHL Kleinpaket ergaenzt. - `V62WP` als Legacy-Anzeige belassen, damit historische Sendungen weiterhin verstaendlich dargestellt werden. - `tests/Pest.php` - TestCase-Bootstrapping fuer `tests/Unit/Dhl` registriert. - `tests/Unit/Dhl/ShippingServiceProductCodeTest.php` - Neuer Regression-Test fuer Phase 1 ergaenzt. Fachliche Entscheidung: - `V62WP` wird nicht hart aus allen Anzeigen entfernt, weil bestehende DHL-Sendungen mit diesem Produktcode historisch nachvollziehbar bleiben muessen. - Neue Labelerstellung blockiert `V62WP` bewusst ueber die `ShippingService`-Validierung. - Eine Datenmigration fuer bestehende Settings wurde noch nicht angelegt. Stattdessen wird `dhl_account_v62wp` temporaer als Fallback verwendet. Eine spaetere Migration kann den Wert dauerhaft nach `dhl_account_v62kp` ueberfuehren. Verifikation: - `./vendor/bin/pint --dirty --format agent` - `php artisan test --compact tests/Unit/Dhl/ShippingServiceProductCodeTest.php` - Ergebnis: 3 Tests bestanden, 5 Assertions. - IDE/Linter-Pruefung der geaenderten Dateien: - Ergebnis: keine Fehler. Offene Folgepunkte: - Phase 2: `TrackShipmentJob` auf aktuelles DHL-Model und aktuellen `DhlTrackingService` umstellen. Erledigt am 27.05.2026. - Phase 2: Storno-Statuswerte zwischen `canceled` und `cancelled` vereinheitlichen. Erledigt am 27.05.2026. - Optional: Migration oder Admin-Hinweis fuer das alte Setting `dhl_account_v62wp`, sobald die Fallback-Phase beendet werden soll. ### 27.05.2026 - Phase 2: Tracking-Job und Storno-Status stabilisiert Status: abgeschlossen fuer Tracking-Job, Statusvereinheitlichung und strukturierte Storno-Fehlerbasis. Ziel: - Asynchrones Tracking darf nicht mehr auf das alte, nicht mehr produktive DHL-Model `App\Models\DhlShipment` zugreifen. - Storno-Statuswerte werden intern auf `canceled` vereinheitlicht. - Alte Daten mit `cancelled` bleiben lesbar und filterbar. - Storno-Fehler werden fachlich verwertbarer im Shipment protokolliert. Umsetzung: - `app/Jobs/TrackShipmentJob.php` - Import von `App\Models\DhlShipment` auf `Acme\Dhl\Models\DhlShipment` umgestellt. - Abhaengigkeit auf den alten `DhlApiService` entfernt. - Job nutzt nun `DhlTrackingService::updateTrackingNow()` ueber Laravel-Dependency-Injection im `handle()`-Methodenparameter. - Queue-Tracking fuehrt dadurch synchron innerhalb des Jobs aus und dispatcht nicht erneut rekursiv in dieselbe Queue. - Logging verwendet jetzt `dhl_shipment_no` statt des alten/inkonsistenten `tracking_number`. - `app/Services/DhlTrackingService.php` - `updateTrackingNow()` ergaenzt, um Tracking bewusst ohne Queue-Dispatch auszufuehren. - Bestehender Controller-/Service-Pfad `updateTracking()` bleibt unveraendert und entscheidet weiterhin anhand der DHL-Konfiguration zwischen sync/async. - `packages/acme-laravel-dhl/src/Models/DhlShipment.php` - `cancelled` als Legacy-Alias fuer `canceled` eingefuehrt. - Statusuebersetzung und Badge-Klasse normalisieren alte `cancelled`-Werte auf `canceled`. - Terminal-Statusliste enthaelt `canceled` und `cancelled`, damit Alt-Datensaetze nicht weiter getrackt werden. - `packages/acme-laravel-dhl/src/Services/ShippingService.php` - Erfolgreiche Stornierung setzt weiterhin intern `status = canceled`. - Erfolgreiche Storno-Antwort wird unter `api_response_data.cancellation` gespeichert. - Fehlgeschlagene Stornos werden unter `api_response_data.cancellation_error` strukturiert gespeichert: - `status` - `http_status` - `dhl_code` - `detail` - `exception_class` - `occurred_at` - `app/Services/DhlShipmentService.php` - Lokale Storno-Validierungsfehler wie fehlende DHL-Sendungsnummer oder nicht stornierbarer Status werden ebenfalls in `api_response_data.cancellation_error` protokolliert. - Admin-Feedback fuer nicht stornierbaren Status nutzt die uebersetzte Statusbezeichnung. - `app/Http/Controllers/DhlShipmentController.php` - Statusfilter normalisiert `cancelled` auf `canceled`. - Bei Filter `canceled` werden neue `canceled`- und alte `cancelled`-Sendungen gemeinsam gefunden. - DataTable-Badges verwenden intern `canceled`. - `resources/views/admin/dhl/cockpit.blade.php` - Statusfilter zeigt `canceled` als neuen Wert fuer "Storniert". - Alte URL-/Request-Werte mit `cancelled` bleiben im Select kompatibel. - `resources/views/admin/dhl/show.blade.php` - Detailansicht behandelt `canceled` und `cancelled` gleich. - Aktionsbereich wird fuer beide Storno-Statuswerte ausgeblendet. - `resources/views/public/tracking.blade.php` - Public-Tracking behandelt `canceled` und `cancelled` gleich. - `resources/lang/de/dhl.php`, `resources/lang/en/dhl.php`, `resources/lang/es/dhl.php`, `resources/lang/fr/dhl.php` - Neuer Statuskey `canceled` ergaenzt. - Legacy-Key `cancelled` bleibt erhalten. - `tests/Unit/Dhl/DhlShipmentStatusTest.php` - Neue Tests fuer Statusnormalisierung, Uebersetzung und Badge-Klasse. - `tests/Unit/Dhl/TrackShipmentJobTest.php` - Neuer Test, dass der Queue-Job den aktuellen `DhlTrackingService` nutzt. Fachliche Entscheidung: - Intern gilt ab jetzt `canceled` als kanonischer DHL-Storno-Status. - `cancelled` wird nicht migriert oder entfernt, weil es in bestehenden Daten/URLs/Views vorkommen kann und weiterhin verstaendlich angezeigt werden soll. - Storno-Fehler werden zunaechst in `api_response_data` gespeichert, um keine neue Datenbankmigration fuer den ersten Stabilisierungsschritt zu erzwingen. Falls spaeter gezielte Admin-Auswertungen gebraucht werden, koennen dedizierte Spalten oder eine eigene Fehler-Tabelle folgen. Verifikation: - `./vendor/bin/pint --dirty --format agent` - `php artisan test --compact tests/Unit/Dhl` - Ergebnis: 6 Tests bestanden, 12 Assertions. - IDE/Linter-Pruefung der geaenderten Dateien: - Ergebnis: keine Fehler. Offene Folgepunkte: - Phase 2 Rest: Tests fuer echte Storno-API-Fehler mit HTTP-Fakes/Mocking weiter ausbauen. - Phase 2 Rest: Admin-Detailanzeige fuer gespeicherte `api_response_data.cancellation_error` bei Bedarf sichtbar machen. - Phase 3: Internationalen Produkt-/Billing-Resolver einfuehren. Erledigt am 27.05.2026. ### 27.05.2026 - Phase 3: Internationaler Produkt- und Laenderresolver Status: abgeschlossen fuer zentrale Produkt-/Laenderentscheidung, initiale Laenderfreigabe und harten Zielland-Fallback-Stopp. Ziel: - Produktcode, Zielland und DHL-Laendercode werden zentral entschieden. - Deutschland nutzt fuer neue Label nur erlaubte nationale Produkte `V01PAK` oder `V62KP`. - Oesterreich und Spanien nutzen initial `V53PAK`. - Unbekannte oder nicht freigegebene Ziellaender duerfen nicht mehr still auf `DEU` fallen. - Das DHL-Cockpit schlaegt den Produktcode anhand des Ziellands vor, laesst aber erlaubte Admin-Korrekturen zu. Umsetzung: - `app/Services/DhlProductResolver.php` - Neuer zentraler Resolver fuer DHL-Produkt- und Laenderentscheidungen. - Regeln initial: - `DE`: erlaubt `V01PAK`, `V62KP` - `AT`: erlaubt `V53PAK` - `ES`: erlaubt `V53PAK` - DHL-Laendercode-Konvertierung zentralisiert, z. B. `DE -> DEU`, `AT -> AUT`, `ES -> ESP`. - Unbekannte Laendercodes loesen eine klare Exception aus statt auf Deutschland zurueckzufallen. - Explizit falsch gewaehlte Produkt-/Laender-Kombinationen werden abgelehnt. - Fehlende Billing-Nummern werden ueber `assertBillingNumber()` fachlich klar abgebrochen. - `app/Services/DhlDataHelper.php` - Empfaengerland ist jetzt Pflicht fuer die DHL-Datenaufbereitung. - Produktcode wird ueber `DhlProductResolver::resolveForShipment()` bestimmt. - Der alte Empfaengerland-Fallback auf `DE` wurde entfernt. - Dimensionen werden anhand des aufgeloesten Produktcodes gelesen. - `packages/acme-laravel-dhl/src/Services/ShippingService.php` - Payload-Erstellung nutzt den Resolver fuer Zielland und Produktcode. - Billing-Nummer wird gegen den aufgeloesten Produktcode validiert. - `convertCountryCode()` nutzt den Resolver und gibt keinen `DEU`-Fallback mehr zurueck. - Normale Empfaengeradressen und Packstation/Paketbox-Payloads verwenden die gleiche harte Laendercode-Validierung. - `app/Services/DhlModalService.php` - Modal-Daten enthalten nun `productSuggestions` und `selectedProductCode`. - Initialer Produktvorschlag wird aus dem Zielland abgeleitet. - Servervalidierung prueft Produkt-/Zielland-Kombinationen ueber den Resolver. - `resources/views/admin/dhl/modal_in_order_shipment.blade.php` - Produktauswahl markiert den vorgeschlagenen Produktcode. - Laenderoptionen tragen den ISO-Code als `data-country-code`. - Hinweistext ergaenzt, dass der Produktcode anhand des Ziellands vorgeschlagen wird. - `resources/views/admin/dhl/modal_create_shipment.blade.php` - Bei Ziellandwechsel wird der passende Produktvorschlag automatisch gesetzt. - `app/Http/Controllers/ModalController.php` - Fallback-Daten fuer das DHL-Modal um Produktvorschlaege und Default-Produkt ergaenzt. - `tests/Unit/Dhl/DhlProductResolverTest.php` - Neue Tests fuer Deutschland, Oesterreich, Spanien, nicht freigegebene Laender, unbekannte Laendercodes und fehlende Billing-Nummern. - `tests/Unit/Dhl/ShippingServiceProductCodeTest.php` - Payload-Test fuer internationales Paket nach Oesterreich ergaenzt. - Regression-Test ergaenzt, dass nicht freigegebene Laender nicht auf Deutschland fallen. Fachliche Entscheidung: - `DE`, `AT` und `ES` sind die initial freigegebenen DHL-Versandlaender fuer diese Phase. - Weitere Laender werden nicht implizit erlaubt, auch wenn eine ISO-Konvertierung technisch bekannt ist. Sie muessen fachlich freigegeben und im Resolver ergaenzt werden. - Falls im Backend kein Produktcode explizit uebergeben wird, kann der Resolver fuer `AT`/`ES` automatisch `V53PAK` vorschlagen. Wenn ein Admin explizit ein nicht erlaubtes Produkt waehlt, wird die Labelerstellung serverseitig abgelehnt. - Der alte `DEU`-Fallback wurde bewusst entfernt, weil ein falsches Zielland zu falschen Labels und kostenpflichtigen Stornos fuehren kann. Verifikation: - `./vendor/bin/pint --dirty --format agent` - `php artisan test --compact tests/Unit/Dhl` - Ergebnis: 15 Tests bestanden, 32 Assertions. - IDE/Linter-Pruefung der geaenderten Dateien: - Ergebnis: keine Fehler. Offene Folgepunkte: - Phase 3 Erweiterung: Weitere Laender erst nach fachlicher Freigabe in `DhlProductResolver` aufnehmen. - Phase 3 Erweiterung: Optional Admin-Setting fuer freigegebene internationale Ziellaender einfuehren. - Phase 4: Adressvalidierung vor Labelerstellung integrieren. Erledigt als formale Basisvalidierung am 27.05.2026. ### 27.05.2026 - Phase 4: Formale Adressvalidierung vor Labelerstellung Status: abgeschlossen fuer serverseitige Basisvalidierung, Warn-/Fehlerstatus und Cockpit-Vorpruefung. Externe postalische Validierung ist noch offen. Ziel: - Vor der Labelerstellung wird eine DHL-Adresse serverseitig bewertet. - Offensichtlich nicht versandfaehige Adressen blockieren die Labelerstellung. - Pruefbeduerftige Adressen erzeugen Warnungen und muessen im Cockpit bewusst bestaetigt werden. - Packstation/Paketbox-Faelle werden strenger validiert. - Die bestehende Labelerstellung bleibt auch serverseitig abgesichert, falls die UI-Pruefung umgangen wird. Umsetzung: - `app/Services/DhlAddressValidator.php` - Neuer zentraler Validator fuer formale DHL-Adresspruefung. - Rueckgabeformat: - `status`: `valid`, `warning` oder `error` - `can_create_label`: true/false - `errors` - `warnings` - `normalized` - Blockierende Pruefungen: - Pflichtfelder fuer Strasse, PLZ, Ort und Land. - Name/Firma muss vorhanden sein. - Zielland muss im `DhlProductResolver` freigegeben sein. - PLZ-Format fuer `DE`, `AT`, `ES`. - Packstation/Paketbox nur fuer Deutschland. - DHL Postnummer muss bei Packstation/Paketbox vorhanden und 6-10-stellig sein. - Packstation-/Paketbox-Nummer muss vorhanden und 3-stellig im Bereich 100-999 sein. - Warnungen: - Telefonnummer fehlt. - E-Mail-Adresse fehlt. - Hausnummer enthaelt keine Ziffer. - Postnummer ist gesetzt, aber Strasse/Nr. sieht nicht nach Packstation/Paketbox aus. - `app/Services/DhlModalService.php` - Bestehende Modal-Adresspruefung nutzt nun den neuen `DhlAddressValidator`. - `validateShipmentData()` fuehrt die Adressvalidierung auch serverseitig vor der Labelerstellung aus. - `app/Http/Controllers/DhlShipmentController.php` - Neuer Endpoint `validateAddress()`. - Antwortet mit Status, Fehlern, Warnungen und `can_create_label`. - Gibt HTTP 422 zurueck, wenn die Adresse nicht label-faehig ist. - `routes/domains/crm.php` - Neue Route `POST /admin/dhl/validate-address` mit Name `admin.dhl.validate-address`. - `resources/views/admin/dhl/modal_create_shipment.blade.php` - Modal ruft vor der eigentlichen Labelerstellung den neuen Validierungsendpunkt auf. - Bei Fehlern wird die Labelerstellung blockiert. - Bei Warnungen muss der Admin bewusst bestaetigen, bevor das Label erstellt wird. - `tests/Unit/Dhl/DhlAddressValidatorTest.php` - Neue Tests fuer: - formal gueltige Adresse - nicht freigegebenes Zielland - falsches PLZ-Format fuer aktiviertes Land - Warnstatus ohne Blockade - gueltige Packstation - ungueltige Packstation-Postnummer - fehlende Packstation-Pflichtdaten Fachliche Entscheidung: - Diese Phase implementiert noch keine echte postalische Existenzpruefung von Strasse/PLZ/Ort. - Der Validator verhindert aber bereits die teuersten formalen Fehler vor Labelerstellung. - Warnungen blockieren nicht automatisch, weil Admins im Cockpit bewusst korrigierte oder fachlich bekannte Sonderfaelle versenden koennen sollen. - Die spaetere Provider-Integration kann hinter dem gleichen `DhlAddressValidator` ergaenzt werden. Verifikation: - `./vendor/bin/pint --dirty --format agent` - `php artisan test --compact tests/Unit/Dhl` - Ergebnis: 22 Tests bestanden, 50 Assertions. - IDE/Linter-Pruefung der geaenderten Dateien: - Ergebnis: keine Fehler. Offene Folgepunkte: - Phase 4 Erweiterung: DHL DATAFACTORY AUTOCOMPLETE 2.0 fuer `DE`/`AT`/`CH` fachlich und technisch evaluieren. - Phase 4 Erweiterung: Falls DHL DATAFACTORY nicht fuer alle benoetigten Laender reicht, externen Provider wie Loqate, Google Address Validation oder HERE bewerten. - Phase 4 Erweiterung: Validierungsergebnis optional dauerhaft an Shipment/Order protokollieren. - Phase 5: Referenzfeld und Admin-UX umsetzen. Erledigt am 27.05.2026. ### 27.05.2026 - Phase 5: Referenzfeld und Admin-UX Status: abgeschlossen. Ziel: - Admins koennen im DHL-Cockpit eine eigene Versandreferenz setzen. - Die Referenz wird als DHL `refNo` an die Shipping API uebergeben. - Die Referenz wird an der Sendung gespeichert und in der Detailansicht angezeigt. - Das DHL-Laengenlimit von 35 Zeichen wird eingehalten. - Ohne Admin-Eingabe bleibt der bisherige Fallback `Order-{id}` erhalten. Umsetzung: - `database/migrations/2026_05_27_120253_add_reference_to_dhl_package_shipments_table.php` - Neue nullable Spalte `reference` mit Laenge 35 fuer `dhl_package_shipments`. - Migration prueft `Schema::hasColumn`, damit sie robust gegen bereits vorhandene Spalten bleibt. - `packages/acme-laravel-dhl/src/Models/DhlShipment.php` - `reference` in `$fillable` aufgenommen. - `resources/views/admin/dhl/modal_in_order_shipment.blade.php` - Neues Feld `reference` in der Sendungskonfiguration. - Default-Wert: `Order-{id}`. - `maxlength="35"` und Hilfetext zum DHL `refNo`. - `resources/views/admin/dhl/modal_create_shipment.blade.php` - Clientseitige Validierung fuer maximal 35 Zeichen ergaenzt. - `app/Http/Controllers/DhlShipmentController.php` - Servervalidierung fuer `reference` mit `max:35`. - Uebergibt `reference` in die Optionen fuer `DhlShipmentService`. - `app/Services/DhlDataHelper.php` - Uebernimmt `reference` oder alternativ `shipment_reference` aus den Optionen. - Normalisiert Leerraum. - Kuerzt programmgesteuert auf 35 Zeichen. - Nutzt `Order-{id}` als Fallback, wenn keine Referenz gesetzt ist. - `packages/acme-laravel-dhl/src/Services/ShippingService.php` - Bestehendes Mapping nach DHL `refNo` bleibt aktiv. - Gesendete Referenz wird beim Erstellen des `DhlShipment`-Datensatzes gespeichert. - `resources/views/admin/dhl/show.blade.php` - Referenz wird in den Sendungsinformationen angezeigt. - `tests/Unit/Dhl/DhlDataHelperReferenceTest.php` - Neue Tests fuer Admin-Referenz, Fallback `Order-{id}` und 35-Zeichen-Normalisierung. Fachliche Entscheidung: - Die Referenz bleibt bewusst ein kurzes Freitextfeld und wird nicht an Bestellnotizen oder interne Kommentare gekoppelt. - Das Feld wird fuer DHL `refNo` und spaetere Nachvollziehbarkeit genutzt, nicht als internes Memo. - Der Fallback `Order-{id}` bleibt erhalten, damit bestehende Prozesse ohne manuelle Referenz unveraendert funktionieren. Verifikation: - `./vendor/bin/pint --dirty --format agent` - `php artisan test --compact tests/Unit/Dhl` - Ergebnis: 25 Tests bestanden, 54 Assertions. - IDE/Linter-Pruefung der geaenderten Dateien: - Ergebnis: keine Fehler. Offene Folgepunkte: - Datenbankmigration vor Nutzung in der Zielumgebung ausfuehren. - Phase 6: DHL-Gewicht korrekt berechnen. ### 27.05.2026 - Abgleich DHL Geschaeftskundenportal: Aktivierte Dienste Status: dokumentiert als fachlicher Abgleich vor Phase 6. Quelle: - Screenshot aus dem DHL Geschaeftskundenportal vom 27.05.2026 mit aktivierten Produkten und Abrechnungsnummern. Aktivierte Dienste laut Portal: - `63144073550101` - DHL Paket GKP - `63144073550701` - DHL Retoure Online - `63144073555301` - DHL Paket International GKP - `63144073555302` - DHL Retoure Int. A - `63144073556201` - Warenpost National / DHL Kleinpaket - `63144073556601` - Warenpost International - `63144073550801` - DHL Retoure MAUL Abgleich mit aktueller Implementierung: - `DHL Paket GKP` - Aktueller Produktcode im Modul: `V01PAK` - Konfiguriert in `config/dhl.php` als `DHL_ACCOUNT_NUMBER_V01PAK`. - Wird fuer nationale Paketsendungen genutzt. - Status: abgedeckt. - `DHL Paket International GKP` - Aktueller Produktcode im Modul: `V53PAK` - Konfiguriert in `config/dhl.php` als `DHL_ACCOUNT_NUMBER_V53PAK`. - Wird aktuell nur fuer fachlich freigegebene Ziellaender `AT` und `ES` genutzt. - Status: technisch abgedeckt, Laenderfreigabe bleibt bewusst begrenzt. - `Warenpost National / DHL Kleinpaket` - Aktueller Produktcode im Modul: `V62KP`. - Konfiguriert in `config/dhl.php` als `DHL_ACCOUNT_NUMBER_V62KP`. - Ersetzt das alte `V62WP`. - Status: abgedeckt. - `DHL Retoure Online` - Konfiguriert in `config/dhl.php` als `DHL_ACCOUNT_NUMBER_V07PAK`. - Status: Konto ist konfiguriert. Der bestehende `ReturnsService` nutzt in Teilen noch Fallback-Logik und muss vor produktiver Retourennutzung separat gegen das aktive Retoure-Konto geprueft werden. - `DHL Retoure Int. A` - Konto im Portal aktiv. - Im Modul aktuell nicht als eigenstaendiger internationaler Retourenprozess modelliert. - Status: offen, eigener Folgepunkt. - `Warenpost International` - Konto im Portal aktiv. - Im Modul aktuell nicht freigeschaltet. Laut DHL API ist dafuer `V66WPI` relevant; dafuer sind Zoll-/CN22-Daten und eigene Gewichts-/Laenderregeln erforderlich. - Status: bewusst nicht Teil der bisherigen Phasen. - `DHL Retoure MAUL` - Konto im Portal aktiv. - Im Modul aktuell nicht modelliert. - Status: offen, nur nach fachlichem Bedarf umsetzen. Technische Entscheidung: - Fuer Phase 6 wird nur das Gewicht fuer die bereits freigegebenen Ausgangsprodukte betrachtet: `V01PAK`, `V53PAK`, `V62KP`. - Internationale Warenpost (`V66WPI`) wird nicht stillschweigend aktiviert, obwohl ein Konto im Portal sichtbar ist. Die Produktart benoetigt separate Regeln und ggf. Zollangaben. - Internationale Retouren und Retoure MAUL werden nicht mit bestehenden Paket-/Retoure-Fallbacks vermischt. Neue Folgepunkte aus dem Portal-Abgleich: - Retourenlogik separat gegen aktives `DHL Retoure Online` Konto pruefen und ggf. `V07PAK`/Returns-API sauber modellieren. - Internationales Retourenprodukt `DHL Retoure Int. A` fachlich klaeren. - `Warenpost International`/`V66WPI` nur als eigene Phase ergaenzen, wenn Zoll-, Gewichts- und Laenderregeln geklaert sind. - `DHL Retoure MAUL` nur bei konkretem Prozessbedarf aufnehmen. ### 27.05.2026 - Nachtrag vor Phase 6: Internationale Paketlaender per DHL-Settings steuerbar Status: abgeschlossen. Ziel: - Kunden/Admins koennen selbst entscheiden, welche Ziellaender fuer `DHL Paket International` aktiv sind. - Die Freigabe erfolgt im bestehenden Settings-Bereich ueber Checkboxen. - Der `DhlProductResolver` nutzt die gespeicherten Laender dynamisch statt einer fest kodierten `AT`/`ES`-Liste. - Deutschland bleibt separat geregelt und wird nicht als internationales Paketland gespeichert. Umsetzung: - `config/dhl.php` - Neuer Fallback `international_countries` aus `DHL_INTERNATIONAL_COUNTRIES`. - Default bleibt `AT,ES`. - `app/Http/Controllers/SettingController.php` - `getDhlConfig()` liefert nun `international_countries`. - Bei `DHL_CONFIG_SOURCE=env` wird keine Datenbankabfrage fuer diese Liste ausgefuehrt. - Bei Datenbankprioritaet wird `dhl_international_countries` aus den Settings gelesen. - `resources/views/admin/settings/index.blade.php` - Neuer Checkbox-Bereich `DHL Paket International Ziellaender`. - Es werden aktive App-Laender angezeigt, deren ISO-Code im DHL-Resolver bekannt ist. - Speicherung als Setting `dhl_international_countries` vom Typ `object`. - `app/Services/DhlProductResolver.php` - Feste internationale Liste durch `getSupportedInternationalCountries()` ersetzt. - Resolver liest je nach `DHL_CONFIG_SOURCE` entweder Config/ENV oder Datenbank-Setting. - `normalizeCountryCodeList()` normalisiert, dedupliziert und filtert unbekannte Codes sowie `DE`. - Produktvorschlaege fuer das Modal werden dynamisch aus der aktivierten Laenderliste erzeugt. - `app/Http/Controllers/ModalController.php` - Fallback-Daten fuer das DHL-Modal nutzen nun ebenfalls dynamische Produktvorschlaege. - `tests/Unit/Dhl/DhlProductResolverTest.php` - Tests fuer konfigurierbare internationale Laender ergaenzt. - Tests fuer Normalisierung und Filterung der Laenderliste ergaenzt. Fachliche Entscheidung: - Die Checkboxen aktivieren nur `V53PAK` fuer DHL Paket International. - `V66WPI` Warenpost International bleibt davon unberuehrt und wird nicht versehentlich aktiviert. - `DE` wird nicht als internationales Zielland zugelassen, weil nationale Sendungen ueber `V01PAK`/`V62KP` laufen. Verifikation: - `./vendor/bin/pint --dirty --format agent` - `php artisan test --compact tests/Unit/Dhl` - Ergebnis: 27 Tests bestanden, 60 Assertions. - IDE/Linter-Pruefung der geaenderten Dateien: - Ergebnis: keine Fehler. ### 27.05.2026 - Phase 6: DHL-Gewicht korrekt berechnen Status: abgeschlossen. Ziel: - Das DHL-Labelgewicht beruecksichtigt Kompensationsprodukte. - Die bestehende Warenkorb-, Versandkosten- und Kompensationslogik bleibt unveraendert. - Admins koennen das Gewicht weiterhin im Modal erhoehen, aber nicht unter das berechnete DHL-Mindestgewicht druecken. - Produktbezogene DHL-Gewichtsgrenzen werden geprueft. Umsetzung: - `app/Services/DhlShipmentWeightCalculator.php` - Neuer zentraler Gewichtsdienst fuer DHL-Labelerstellung. - Basisgewicht ist `ShoppingOrder->weight` in Gramm. - Fuer `shopping_order_items` mit `comp > 0` wird das verknuepfte `Product->weight` je `qty` addiert. - Falls kein Gewicht vorhanden ist, wird ein sicherer Fallback von `1.0 kg` genutzt. - Rundung erfolgt auf drei Nachkommastellen. - Produktlimits: - `V01PAK`: 31.5 kg - `V53PAK`: 31.5 kg - `V62KP`: 1.0 kg - `app/Services/DhlModalService.php` - Laedt Bestellpositionen jetzt mit Produktrelation. - Modal-Gewicht nutzt den neuen Kalkulator. - Validierung prueft das Gewicht gegen das gewaehlte DHL-Produkt. - `app/Http/Controllers/DhlShipmentController.php` - Vor Labelerstellung wird das tatsaechliche Versandgewicht auf mindestens das berechnete DHL-Gewicht gesetzt. - Ein manuell hoeheres Gewicht aus dem Formular bleibt erhalten. - `app/Services/DhlShipmentService.php` - Ermittelt serverseitig ebenfalls das Mindestgewicht, damit Queue- und Direktaufrufe abgesichert sind. - `packages/acme-laravel-dhl/src/Services/ShippingService.php` - Prueft produktbezogene DHL-Gewichtsgrenzen vor Payload-Erstellung. - `resources/views/admin/dhl/modal_in_order_shipment.blade.php` - Hinweistext am Gewichtsfeld stellt klar, dass Kompensationsprodukte eingerechnet sind. - `tests/Unit/Dhl/DhlShipmentWeightCalculatorTest.php` - Neue Tests fuer: - Basisgewicht aus Bestellung - Addition von Kompensationsprodukt-Gewichten - Fallbackgewicht - `V62KP`-Gewichtslimit - erlaubtes Paketgewicht fuer `V01PAK` Fachliche Entscheidung: - Die Berechnung wirkt nur auf das DHL-Labelgewicht. - `ShoppingOrder->weight` und die bestehende Checkout-/Versandkostenlogik werden nicht veraendert. - Kompensationsartikel werden nur dann addiert, wenn `comp > 0` und ein Produktgewicht vorhanden ist. - `V62KP` wird mit 1.0 kg begrenzt, damit Kleinpaket nicht versehentlich fuer schwerere Sendungen genutzt wird. Verifikation: - `./vendor/bin/pint --dirty --format agent` - `php artisan test --compact tests/Unit/Dhl` - Ergebnis: 32 Tests bestanden, 66 Assertions. - IDE/Linter-Pruefung der geaenderten Dateien: - Ergebnis: keine Fehler. ### 27.05.2026 - Phase 7: Tracking-Codes und Tracking-Mails finalisiert Status: abgeschlossen fuer Admin/User-Anzeige und automatische Mail-Ausloesung. Ziel: - Tracking-Codes sind in Admin- und User-Order-Details sichtbar. - Automatische Tracking-Mails werden nur einmal pro Sendung versendet. - Mails werden nur bei relevanter Statusaenderung ausgeloest. - Statusspruenge wie `created` direkt nach `out_for_delivery` werden abgedeckt. - Mehrere passende Sendungen einer Bestellung werden weiterhin in einer Mail zusammengefasst. Umsetzung: - `packages/acme-laravel-dhl/src/Models/DhlShipment.php` - Neue Konstante `TRACKING_EMAIL_TRIGGER_STATUSES`. - Relevante automatische Mail-Statuswerte: - `in_transit` - `out_for_delivery` - Neue Methode `shouldTriggerTrackingEmail()`. - Bedingung: - aktueller Status ist relevant - Status hat sich gegenueber dem vorherigen Status geaendert - Tracking-Mail wurde noch nicht markiert - Sendung hat DHL-Sendungsnummer und Empfaengeradresse - Scope `needsTrackingEmail()` nutzt die zentrale Statusliste. - `app/Console/Commands/DhlUpdateTracking.php` - Automatische Mailentscheidung nutzt nun `DhlShipment::shouldTriggerTrackingEmail()`. - Beim Zusammenfassen mehrerer Sendungen werden alle unbenachrichtigten Sendungen der Bestellung mit relevantem Status aufgenommen. - Dadurch werden direkte Spruenge nach `out_for_delivery` nicht mehr uebersehen. - `app/Services/DhlTrackingService.php` - DHL-Statusmapping erweitert: - `transit`, `in-transit`, `in_transit` -> `in_transit` - `out-for-delivery`, `out_for_delivery` -> `out_for_delivery` - weitere Varianten fuer `pre_transit` und `failed` - Mapping ist nun statisch nutzbar und separat testbar. - `resources/views/admin/dhl/show.blade.php` - Oeffentlicher Tracking-Link nutzt nun den korrekten Query-Parameter `tracking_number`. - `resources/views/admin/sales/_detail_dhl_shipments.blade.php` - Bereits vorhandener DHL-Block wird auch in den User-Order-Modalen mit eingebunden, Aktionen bleiben ueber `isAdmin` abgesichert. - `tests/Unit/Dhl/DhlShipmentStatusTest.php` - Tests fuer Tracking-Mail-Ausloesung bei `out_for_delivery`. - Tests fuer einmalige Mail-Ausloesung. - Tests, dass `delivered` keine neue automatische Tracking-Mail mehr ausloest. - Tests fuer DHL-Statusmapping-Varianten. Fachliche Entscheidung: - Automatische Tracking-Mails werden bei `in_transit` oder `out_for_delivery` versendet. - `delivered` loest keine neue automatische Tracking-Mail aus, weil die Benachrichtigung dann fachlich zu spaet waere und Mehrfachmails vermieden werden sollen. - Manuelles erneutes Senden im Admin bleibt weiterhin moeglich. - Mehrere Sendungen einer Bestellung werden zusammengefasst, sofern sie noch keine Tracking-Mail erhalten haben und einen relevanten Status besitzen. Verifikation: - `./vendor/bin/pint --dirty --format agent` - `php artisan test --compact tests/Unit/Dhl` - Ergebnis: 38 Tests bestanden, 74 Assertions. - IDE/Linter-Pruefung der geaenderten Dateien: - Ergebnis: keine Fehler. ### 27.05.2026 - Nachtrag Phase 7: Tracking-E-Mail-Historie in der Detailansicht Status: abgeschlossen. Ziel: - In der DHL-Detailansicht soll nicht nur der letzte Tracking-Mail-Status sichtbar sein. - Es soll nachvollziehbar sein, welche Tracking-E-Mails mit welchem Sendungsstatus versendet wurden. - Automatische und manuelle Versendungen sollen unterscheidbar bleiben. - Zusammengefasste Tracking-Mails sollen zeigen, welche Sendungen enthalten waren. Umsetzung: - `packages/acme-laravel-dhl/src/Models/DhlShipment.php` - `markTrackingEmailSent()` erweitert. - Bei jedem Versand wird ein Eintrag in `api_response_data.tracking_email_history` geschrieben. - Gespeicherte Felder je Eintrag: - `sent_at` - `type` (`auto` oder `manual`) - `recipient_email` - `status` - `tracking_status` - `dhl_shipment_no` - `included_shipment_ids` - Neue Methode `getTrackingEmailHistory()` liefert die Historie mit neuestem Eintrag zuerst. - Neue statische Methode `getStatusBadgeClassFor()` fuer Status-Badges in historischen Eintraegen. - `app/Http/Controllers/DhlShipmentController.php` - Manueller Tracking-Mail-Versand uebergibt Empfaenger und enthaltene Sendungen an `markTrackingEmailSent()`. - `app/Console/Commands/DhlUpdateTracking.php` - Automatischer Tracking-Mail-Versand uebergibt Empfaenger und enthaltene Sendungen an `markTrackingEmailSent()`. - `resources/views/admin/dhl/show.blade.php` - Bereich `Tracking-E-Mail Status` zeigt weiterhin den letzten Versand prominent an. - Darunter wird eine Historientabelle angezeigt mit: - Zeitpunkt - Typ - Status zum Versandzeitpunkt - Empfaenger - enthaltene Sendungs-IDs - `tests/Unit/Dhl/DhlShipmentStatusTest.php` - Tests fuer Tracking-Mail-Historie mit neuestem Eintrag zuerst. - Test fuer Legacy-Sendungen ohne Historie. Fachliche Entscheidung: - Die Historie wird im bestehenden `api_response_data` JSON der Sendung gespeichert, damit keine neue Tabelle notwendig ist. - Die bestehenden Felder `tracking_email_sent_at` und `tracking_email_type` bleiben fuer schnelle Anzeige und bestehende Logik erhalten. - Historische Sendungen, die nur die alten Felder besitzen, bleiben kompatibel und zeigen weiterhin den letzten Versandstatus. Verifikation: - `./vendor/bin/pint --dirty --format agent` - `php artisan test --compact tests/Unit/Dhl` - Ergebnis: 40 Tests bestanden, 79 Assertions. - IDE/Linter-Pruefung der geaenderten Dateien: - Ergebnis: keine Fehler. ### 27.05.2026 - Nachtrag: Modale Vorabpruefung vor Labelerstellung Status: abgeschlossen. Ziel: - Vor der finalen DHL-Labelerstellung soll im bestehenden Erstellungsmodal eine sichtbare Vorabpruefung erscheinen. - Der Admin soll Produktcode, nationale/internationale Sendungsart, Zielland und Lieferadressstatus sehen. - Fehler blockieren die Labelerstellung; Warnungen bleiben sichtbar und muessen bewusst bestaetigt werden. Umsetzung: - `app/Http/Controllers/DhlShipmentController.php` - Der bestehende Endpoint `validateAddress()` liefert nun zusaetzlich strukturierte `preflight`-Daten. - Die Produktpruefung nutzt `DhlProductResolver`. - Fehler aus Produkt-/Zielland-Kombinationen werden gemeinsam mit Adressfehlern zurueckgegeben. - `app/Services/DhlProductResolver.php` - Neue Methoden `getProductScope()` und `getProductScopeLabel()`. - Dadurch kann die UI Produktcodes fachlich als national oder international anzeigen. - `resources/views/admin/dhl/modal_in_order_shipment.blade.php` - Neuer Statusbereich `Vorabpruefung vor Labelerstellung`. - Der Statusbereich sitzt am Ende des Formulars direkt vor den Aktionsbuttons. - Initialer Buttontext: `Vorabpruefung durchfuehren`. - `app/Services/DhlAddressValidator.php` - Die Lieferadresse wird zusaetzlich auf plausible Feldinhalte geprueft. - Offensichtlich ungueltige Werte bei Straße, PLZ oder Ort fuehren jetzt zu Fehlern statt nur zu einer positiven Formalpruefung. - Fuer DE, AT und CH ist eine formale DACH-Pruefung hinterlegt. - Diese umfasst Pflichtfelder, PLZ-Format, Plausibilitaet, Platzhalter-/Testdaten und Packstation-Regeln. - DACH-Hausnummern ohne Ziffer werden als Fehler blockiert. - Die UI weist nun ausdruecklich darauf hin, dass keine echte DHL-/Adressdatenbank- oder Leitcodepruefung angebunden ist. - Fuer unterstuetzte Ziellaender ohne landesspezifische Pruefung wird ein Hinweis auf die reine Basis-Adresspruefung ausgegeben. - `resources/views/admin/dhl/modal_create_shipment.blade.php` - Der erste Klick fuehrt nur die Vorabpruefung aus. - Die separate Browser-Alert-Fehlermeldung bei Vorpruefungsfehlern wurde entfernt. - Der Validierungsstatus zeigt nun `Formale DACH-Pruefung` statt irrefuehrend `Aktiv`. - Das Ergebnis wird direkt im Modal angezeigt: - Produktcode - Sendungsart - Zielland - normalisierte Lieferadresse - Status der Adressvalidierung - Fehler und Hinweise - Erst nach erfolgreicher Vorabpruefung wechselt der Button auf `Sendung jetzt erstellen`. - Aenderungen an Formularfeldern setzen die Freigabe automatisch zurueck. - `tests/Unit/Dhl/DhlProductResolverTest.php` - Neuer Test fuer nationale und internationale Produktklassifizierung. - `tests/Unit/Dhl/DhlAddressValidatorTest.php` - Neuer Test fuer unplausible Lieferadressfelder. - Neue Tests fuer CH-Adressvalidierung und Basispruefungs-Hinweise. - Neue Tests fuer Platzhalteradressen und DACH-Hausnummern ohne Ziffer. Fachliche Entscheidung: - Die bestehende Servervalidierung bleibt die Quelle der Wahrheit. - Die Vorabpruefung ersetzt keine Validierung bei der finalen Erstellung; vor dem Erstellen wird nochmals gegen denselben Endpoint geprueft. - Dadurch werden nachtraegliche Formularaenderungen oder veraltete Modalzustande nicht ungeprueft an DHL uebergeben. Verifikation: - `./vendor/bin/pint --dirty --format agent` - `php artisan test --compact tests/Unit/Dhl` - Ergebnis: 46 Tests bestanden, 109 Assertions. - IDE/Linter-Pruefung der geaenderten Dateien: - Ergebnis: keine Fehler. ### 27.05.2026 - Phase 8: DHL-seitige Adressvalidierung ueber mustEncode Status: umgesetzt fuer deutsche Empfaengeradressen. Ziel: - DHL soll selbst die finale Adress-/Leitcodefaehigkeit pruefen. - Grundlage ist der DHL-Query-Parameter `mustEncode=true`. - `printOnlyIfCodable` ist laut DHL-Spezifikation der Legacy-Name. - Wenn diese Option gesetzt ist, soll DHL das Etikett nur dann erstellen, wenn die Adresse codeable/leitcodierbar ist. - Wird die Adresse von DHL abgelehnt, soll der Fehler im bestehenden Modal erscheinen und dort korrigierbar sein. - Laut DHL-Dokumentation ist die Funktion nur fuer deutsche Empfaengeradressen relevant. Umsetzung: - Die aktuelle formale Vorabpruefung bleibt erhalten. - `config/dhl.php` - Neue Option `print_only_if_codeable`, steuerbar ueber `DHL_PRINT_ONLY_IF_CODEABLE`. - Standard: aktiv. - `app/Http/Controllers/SettingController.php` - DHL-Konfiguration liefert `print_only_if_codeable`. - `resources/views/admin/settings/index.blade.php` - Neue Checkbox `DHL-Leitcodierung erzwingen (mustEncode)`. - `app/Services/DhlDataHelper.php` - Option wird in die Orderdaten fuer den DHL-Request uebernommen. - `packages/acme-laravel-dhl/src/Services/ShippingService.php` - Fuer deutsche Empfaengeradressen wird bei aktiver Option der Query-Parameter `mustEncode=true` an die DHL Create-Shipment-Operation uebergeben. - DHL-Responses ohne Label/Shipment oder mit Item-Fehlerstatus werden vor dem Speichern der Sendung abgefangen. - Nicht leitcodierbare Adressen werden als `DhlAddressValidationException` normalisiert. - `packages/acme-laravel-dhl/src/Exceptions/DhlAddressValidationException.php` - Neue fachliche Exception fuer DHL-Adressablehnungen. - `app/Services/DhlShipmentService.php` - DHL-Adressablehnungen werden als Validierungsfehler zurueckgegeben. - Wenn Queue aktiv ist, aber `mustEncode` fuer eine deutsche Empfaengeradresse greift, wird synchron erstellt, damit der DHL-Fehler direkt im Modal sichtbar bleibt. - `app/Http/Controllers/DhlShipmentController.php` - Gibt DHL-Adressvalidierungsfehler mit HTTP 422 zurueck. - `resources/views/admin/dhl/modal_create_shipment.blade.php` - Fehler aus der finalen DHL-Erstellung werden in der bestehenden Vorabpruefungsbox angezeigt. - Keine separate Browser-Alert-Meldung. Offene Punkte: - DHL-Sandbox-Verhalten testen. - Fehlercodes fuer nicht codeable Adressen sammeln. - Produktabdeckung fuer `V01PAK`, `V62KP` und `V53PAK` klaeren. - Klaeren, wie internationale Sendungen behandelt werden, da DHL `mustEncode` als nur fuer deutsche Empfaengeradressen relevant beschreibt. Verifikation: - `./vendor/bin/pint --dirty --format agent` - `php artisan test --compact tests/Unit/Dhl` - Ergebnis: 49 Tests bestanden, 115 Assertions. ## Legacy-Dokumentation Die bisherigen Markdown-Dateien wurden nach `dev/dhl-modul/legacy` verschoben. Sie bleiben als Historie erhalten, sind aber nicht mehr die aktuelle Arbeitsgrundlage.