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>
This commit is contained in:
parent
323024f65d
commit
8f85c0107c
14 changed files with 508 additions and 68 deletions
|
|
@ -8,6 +8,7 @@ use Acme\Dhl\Jobs\CreateShipmentJob;
|
|||
use Acme\Dhl\Models\DhlShipment;
|
||||
use Acme\Dhl\Support\DhlClient;
|
||||
use App\Models\Setting;
|
||||
use App\Services\DhlDataHelper;
|
||||
use App\Services\DhlProductResolver;
|
||||
use App\Services\DhlShipmentService;
|
||||
use App\Services\DhlShipmentWeightCalculator;
|
||||
|
|
@ -254,10 +255,16 @@ class ShippingService
|
|||
// Pre-process German addresses before validation
|
||||
$data = $this->preprocessAddresses($data);
|
||||
|
||||
// Historische Produktcodes auf die gueltige API-Schreibweise heben,
|
||||
// bevor die in:-Regel greift.
|
||||
if (! empty($data['product_code'])) {
|
||||
$data['product_code'] = (new DhlProductResolver)->normalizeProductCode($data['product_code']);
|
||||
}
|
||||
|
||||
$validator = Validator::make($data, [
|
||||
'order_id' => 'nullable|integer',
|
||||
'weight_kg' => 'required|numeric|min:0.1|max:31.5', // DHL weight limit
|
||||
'product_code' => 'nullable|string|in:V01PAK,V53PAK,V62KP,V07PAK',
|
||||
'product_code' => 'nullable|string|in:V01PAK,V53WPAK,V62KP,V07PAK',
|
||||
'label_format' => 'nullable|string|in:PDF,ZPL',
|
||||
'print_format' => 'nullable|string', // Values like A4, 910-300-700 etc.
|
||||
'retoure_print_format' => 'nullable|string',
|
||||
|
|
@ -387,17 +394,122 @@ class ShippingService
|
|||
{
|
||||
// Process shipper address
|
||||
if (isset($data['shipper'])) {
|
||||
$data['shipper'] = $this->parseAddressFields($data['shipper']);
|
||||
$data['shipper'] = $this->normalizeOptionalAddressFields($this->parseAddressFields($data['shipper']));
|
||||
}
|
||||
|
||||
// Process consignee address
|
||||
if (isset($data['consignee'])) {
|
||||
$data['consignee'] = $this->parseAddressFields($data['consignee']);
|
||||
$data['consignee'] = $this->normalizeOptionalAddressFields($this->parseAddressFields($data['consignee']));
|
||||
}
|
||||
|
||||
// refNo muss laut DHL 8-35 Zeichen lang sein. Eine zu kurze manuelle
|
||||
// Referenz darf die Sendung nicht scheitern lassen.
|
||||
$data['reference'] = $this->normalizeReference(
|
||||
$data['reference'] ?? null,
|
||||
$data['order_id'] ?? null
|
||||
);
|
||||
|
||||
if ($data['reference'] === null) {
|
||||
unset($data['reference']);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optionale Felder auf die DHL-Grenzen bringen, statt die Sendung daran scheitern
|
||||
* zu lassen. Telefonnummer und name2 sind bei DHL optional - eine unsauber erfasste
|
||||
* Telefonnummer darf keinen Versand blockieren.
|
||||
*
|
||||
* @param array<string, mixed> $addressData
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function normalizeOptionalAddressFields(array $addressData): array
|
||||
{
|
||||
if (isset($addressData['phone'])) {
|
||||
$phone = $this->normalizePhoneNumber((string) $addressData['phone']);
|
||||
|
||||
if ($phone === null) {
|
||||
unset($addressData['phone']);
|
||||
} else {
|
||||
$addressData['phone'] = $phone;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (['name', 'name2', 'city'] as $field) {
|
||||
if (! empty($addressData[$field]) && is_string($addressData[$field])) {
|
||||
$addressData[$field] = mb_substr(trim($addressData[$field]), 0, 50);
|
||||
}
|
||||
}
|
||||
|
||||
return $addressData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entfernt Trenn- und Formatzeichen aus der Telefonnummer. Bleibt danach keine
|
||||
* plausible Nummer uebrig, wird null zurueckgegeben, damit das optionale Feld
|
||||
* ganz entfaellt.
|
||||
*/
|
||||
private function normalizePhoneNumber(string $phone): ?string
|
||||
{
|
||||
$phone = trim($phone);
|
||||
|
||||
if ($phone === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$isInternational = str_starts_with($phone, '+');
|
||||
$digits = preg_replace('/\D/', '', $phone) ?? '';
|
||||
|
||||
if (strlen($digits) < 6) {
|
||||
Log::warning('DHL: Telefonnummer wird verworfen (unplausibel)', [
|
||||
'digit_count' => strlen($digits),
|
||||
]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$normalized = $isInternational ? '+'.$digits : $digits;
|
||||
|
||||
if (strlen($normalized) > 20) {
|
||||
Log::warning('DHL: Telefonnummer wird verworfen (zu lang fuer die DHL-API)', [
|
||||
'length' => strlen($normalized),
|
||||
]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* DHL verlangt fuer refNo 8-35 Zeichen. Zu kurze Eingaben werden durch die
|
||||
* Bestellreferenz ersetzt, zu lange gekuerzt.
|
||||
*/
|
||||
private function normalizeReference(?string $reference, mixed $orderId): ?string
|
||||
{
|
||||
$reference = trim((string) $reference);
|
||||
|
||||
if (mb_strlen($reference) >= DhlDataHelper::REFERENCE_MIN_LENGTH) {
|
||||
return mb_substr($reference, 0, DhlDataHelper::REFERENCE_MAX_LENGTH);
|
||||
}
|
||||
|
||||
if (empty($orderId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$fallback = 'Order-'.$orderId;
|
||||
|
||||
if ($reference !== '') {
|
||||
Log::info('DHL: Zu kurze Referenz durch Bestellreferenz ersetzt', [
|
||||
'given_length' => mb_strlen($reference),
|
||||
'reference' => $fallback,
|
||||
]);
|
||||
}
|
||||
|
||||
return str_pad($fallback, DhlDataHelper::REFERENCE_MIN_LENGTH, '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse German address to extract street and house number
|
||||
*/
|
||||
|
|
@ -752,36 +864,46 @@ class ShippingService
|
|||
return $testBillingNumber;
|
||||
}
|
||||
|
||||
// Try to get from admin settings via Setting model first (database settings override config)
|
||||
$settingKey = 'dhl_account_'.strtolower($productCode);
|
||||
// Historische Schreibweisen mitpruefen, damit vor der Umstellung
|
||||
// gespeicherte Settings (z. B. dhl_account_v53pak) weiter greifen.
|
||||
$productCodeCandidates = (new DhlProductResolver)->getProductCodeCandidates($productCode);
|
||||
|
||||
try {
|
||||
$accountNumber = Setting::getContentBySlug($settingKey);
|
||||
if ($accountNumber) {
|
||||
Log::info('Using DHL account number from database settings', [
|
||||
// Try to get from admin settings via Setting model first (database settings override config)
|
||||
foreach ($productCodeCandidates as $candidate) {
|
||||
$settingKey = 'dhl_account_'.strtolower($candidate);
|
||||
|
||||
try {
|
||||
$accountNumber = Setting::getContentBySlug($settingKey);
|
||||
if ($accountNumber) {
|
||||
Log::info('Using DHL account number from database settings', [
|
||||
'product_code' => $productCode,
|
||||
'setting_key' => $settingKey,
|
||||
'account_number' => $accountNumber,
|
||||
]);
|
||||
|
||||
return $accountNumber;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Log::warning('Could not load DHL account number from settings', [
|
||||
'product_code' => $productCode,
|
||||
'setting_key' => $settingKey,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Try to get account number from config by product code
|
||||
foreach ($productCodeCandidates as $candidate) {
|
||||
$accountNumber = config("dhl.account_numbers.{$candidate}");
|
||||
if ($accountNumber) {
|
||||
Log::info('Using DHL account number from config file', [
|
||||
'product_code' => $productCode,
|
||||
'config_key' => $candidate,
|
||||
'account_number' => $accountNumber,
|
||||
]);
|
||||
|
||||
return $accountNumber;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Log::warning('Could not load DHL account number from settings', [
|
||||
'product_code' => $productCode,
|
||||
'setting_key' => $settingKey,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
|
||||
// Try to get account number from config by product code
|
||||
$accountNumber = config("dhl.account_numbers.{$productCode}");
|
||||
if ($accountNumber) {
|
||||
Log::info('Using DHL account number from config file', [
|
||||
'product_code' => $productCode,
|
||||
'account_number' => $accountNumber,
|
||||
]);
|
||||
|
||||
return $accountNumber;
|
||||
}
|
||||
|
||||
// Fallback to default billing number
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue