mivita/app/Services/DhlDataHelper.php
Kevin 8f85c0107c DHL: Auslandslabels mit V53WPAK, Feldlaengen und Tracking-Wiederholung
Log-Analyse 26.08. (P0-2, P1-5, P2-3):
- Produktcode fuer DHL Paket International ist V53WPAK; mit V53PAK wurde
  kein einziges Auslandslabel erzeugt. Alte Codes werden gemappt
  (LEGACY_PRODUCT_CODE_ALIASES), Abrechnungsnummer und Masse suchen auch
  unter den alten Setting-Schluesseln (dhl_account_v53pak) und Env-Namen.
- Zu lange Felder (Name, Stadt, Telefon, Referenz) werden auf die
  DHL-Grenzen gekuerzt bzw. bereinigt statt den Versand abzulehnen; refNo
  mindestens 8 Zeichen, sonst die Bestellreferenz.
- Tracking wiederholt bei HTTP 5xx der Unified-API bis zu dreimal.

Zolldaten fuer die Schweiz (P0-3) sind nicht Teil dieses Commits.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 15:41:49 +00:00

129 lines
5.8 KiB
PHP

<?php
namespace App\Services;
use App\Http\Controllers\SettingController;
use App\Models\ShoppingOrder;
use Illuminate\Support\Facades\Log;
/**
* DHL Data Helper
*
* Central class for preparing DHL API data structures
* Prevents code duplication between DhlShipmentService and CreateShipmentJob
*/
class DhlDataHelper
{
/** DHL Parcel DE Shipping API v2: refNo muss 8-35 Zeichen lang sein. */
public const REFERENCE_MIN_LENGTH = 8;
public const REFERENCE_MAX_LENGTH = 35;
/**
* Prepare order data for DHL API v2
*
* Structure matches official DHL API v2 createOrders endpoint:
* https://developer.dhl.com/api-reference/parcel-de-shipping-post-parcel-germany-v2
*
* @param array|null $dhlConfig Optional pre-loaded config (for queue jobs)
*/
public static function prepareOrderData(ShoppingOrder $order, float $weight, array $options = [], ?array $dhlConfig = null): array
{
Log::info('[DHL DataHelper] Preparing order data', [
'order_id' => $order->id,
'product_code' => $options['product_code'] ?? null,
'has_shipping_address' => isset($options['shipping_address']),
'has_reference' => ! empty($options['reference'] ?? $options['shipment_reference'] ?? null),
'print_only_if_codeable' => (bool) ($options['print_only_if_codeable'] ?? false),
]);
// die daten für das versandlabel werden immer aus dem Formular genommen, damit anpassungen möglich sind
if (! isset($options['shipping_address'])) {
throw new \Exception('shipping_address is required');
}
$shippingAddress = $options['shipping_address'];
// Get DHL configuration for shipper data
if ($dhlConfig === null) {
$settingController = new SettingController;
$dhlConfig = $settingController->getDhlConfig();
}
$resolver = new DhlProductResolver;
$destinationCountryCode = $shippingAddress['country']?->code;
if (! $destinationCountryCode) {
throw new \Exception('shipping_address.country is required');
}
$resolvedDhlProduct = $resolver->resolveForShipment(
$destinationCountryCode,
$options['product_code'] ?? null,
$dhlConfig['default_product'] ?? 'V01PAK'
);
$dimensions = $dhlConfig['dimensions'][$resolvedDhlProduct['product_code']] ?? $dhlConfig['dimensions']['default'];
return [
'order_id' => $order->id,
'weight_kg' => $weight,
'product_code' => $resolvedDhlProduct['product_code'],
'label_format' => $options['label_format'] ?? $dhlConfig['label_format'] ?? 'PDF',
'print_format' => $options['print_format'] ?? $dhlConfig['print_format'] ?? null,
'retoure_print_format' => $options['retoure_print_format'] ?? $dhlConfig['retoure_print_format'] ?? null,
'print_only_if_codeable' => (bool) ($options['print_only_if_codeable'] ?? $dhlConfig['print_only_if_codeable'] ?? config('dhl.print_only_if_codeable', true)),
// Shipper data (sender) - from admin settings
'shipper' => [
'name' => $dhlConfig['sender']['company'] ?? 'mivita care gmbh',
'name2' => $dhlConfig['sender']['name'] ?? '',
'street' => $dhlConfig['sender']['street'] ?? 'Waldstr.',
'houseNumber' => $dhlConfig['sender']['houseNumber'] ?? '3e',
'postalCode' => $dhlConfig['sender']['postalCode'] ?? '86498',
'city' => $dhlConfig['sender']['city'] ?? 'Kettershausen',
'country' => $dhlConfig['sender']['country'] ?? 'DE',
'email' => $dhlConfig['sender']['email'] ?? 'versand@mivita.care',
'phone' => $dhlConfig['sender']['phone'] ?? '+49 123 456789',
],
// Consignee data (recipient) - from modal form (can be modified)
'consignee' => [
'name' => trim(($shippingAddress['firstname'] ?? '').' '.($shippingAddress['lastname'] ?? '')),
'name2' => $shippingAddress['company'] ?? '',
'street' => $shippingAddress['address'] ?? '',
'houseNumber' => $shippingAddress['houseNumber'] ?? '',
'postalCode' => $shippingAddress['zipcode'] ?? '',
'city' => $shippingAddress['city'] ?? '',
'country' => $resolvedDhlProduct['country_code'],
'email' => $shippingAddress['email'] ?? '',
'phone' => $shippingAddress['phone'] ?? '',
// DHL Postnummer für Packstation/Paketbox
'postNumber' => $shippingAddress['postnumber'] ?? null,
// Store individual fields for easier access
'firstname' => $shippingAddress['firstname'] ?? '',
'lastname' => $shippingAddress['lastname'] ?? '',
],
// Package dimensions from options or defaults
'dimensions' => $dimensions,
// Additional services
'services' => $options['services'] ?? [],
// Custom reference for tracking
'reference' => self::normalizeReference($options['reference'] ?? $options['shipment_reference'] ?? null, $order),
];
}
/**
* DHL verlangt fuer refNo 8-35 Zeichen. Eine leere oder zu kurze Eingabe
* wird durch die Bestellreferenz ersetzt, damit die Sendung nicht an einer
* manuell erfassten Kurzreferenz scheitert.
*/
private static function normalizeReference(?string $reference, ShoppingOrder $order): string
{
$reference = trim(preg_replace('/\s+/', ' ', (string) $reference));
if (mb_strlen($reference) < self::REFERENCE_MIN_LENGTH) {
return 'Order-'.$order->id;
}
return mb_substr($reference, 0, self::REFERENCE_MAX_LENGTH);
}
}