mivita/dev/2026-05-13-dhl-modul/ENTWICKLUNGSKONZEPT-DHL-MODUL.md

53 KiB

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.