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>
130 lines
4.8 KiB
PHP
130 lines
4.8 KiB
PHP
<?php
|
|
|
|
use Acme\Dhl\Exceptions\DhlAddressValidationException;
|
|
use Acme\Dhl\Services\ShippingService;
|
|
use Acme\Dhl\Support\DhlClient;
|
|
|
|
function makeDhlPhaseOneShippingService(): ShippingService
|
|
{
|
|
return new ShippingService(new DhlClient('https://api-sandbox.dhl.com', null, null, null));
|
|
}
|
|
|
|
function validDhlPhaseOneOrderData(string $productCode, string $countryCode = 'DE'): array
|
|
{
|
|
return [
|
|
'order_id' => 123456,
|
|
'weight_kg' => 0.5,
|
|
'product_code' => $productCode,
|
|
'label_format' => 'PDF',
|
|
'shipper' => [
|
|
'name' => 'mivita care gmbh',
|
|
'street' => 'Leinfeld',
|
|
'houseNumber' => '2',
|
|
'postalCode' => '87755',
|
|
'city' => 'Kirchhaslach',
|
|
'country' => 'DE',
|
|
],
|
|
'consignee' => [
|
|
'name' => 'Max Mustermann',
|
|
'street' => 'Hauptstrasse',
|
|
'houseNumber' => '5',
|
|
'postalCode' => '10115',
|
|
'city' => 'Berlin',
|
|
'country' => $countryCode,
|
|
],
|
|
'reference' => 'ORDER-123456',
|
|
];
|
|
}
|
|
|
|
it('accepts DHL Kleinpaket as a product code', function () {
|
|
$service = makeDhlPhaseOneShippingService();
|
|
$method = new ReflectionMethod($service, 'validateOrderData');
|
|
$method->setAccessible(true);
|
|
|
|
$validated = $method->invoke($service, validDhlPhaseOneOrderData('V62KP'));
|
|
|
|
expect($validated['product_code'])->toBe('V62KP');
|
|
});
|
|
|
|
it('builds an international DHL parcel payload for Austria', function () {
|
|
config([
|
|
'dhl.account_numbers.V53WPAK' => '63144073555301',
|
|
'dhl.legacy.sandbox' => false,
|
|
'dhl.legacy.test_mode' => false,
|
|
]);
|
|
|
|
$service = makeDhlPhaseOneShippingService();
|
|
$method = new ReflectionMethod($service, 'buildShipmentPayload');
|
|
$method->setAccessible(true);
|
|
|
|
$payload = $method->invoke($service, validDhlPhaseOneOrderData('V53WPAK', 'AT'));
|
|
|
|
expect($payload['shipments'][0]['product'])->toBe('V53WPAK')
|
|
->and($payload['shipments'][0]['billingNumber'])->toBe('63144073555301')
|
|
->and($payload['shipments'][0]['consignee']['country'])->toBe('AUT');
|
|
});
|
|
|
|
it('rejects unsupported countries instead of falling back to Germany', function () {
|
|
$service = makeDhlPhaseOneShippingService();
|
|
$method = new ReflectionMethod($service, 'buildShipmentPayload');
|
|
$method->setAccessible(true);
|
|
|
|
$method->invoke($service, validDhlPhaseOneOrderData('V53WPAK', 'FR'));
|
|
})->throws(InvalidArgumentException::class);
|
|
|
|
it('rejects legacy Warenpost for new labels', function () {
|
|
$service = makeDhlPhaseOneShippingService();
|
|
$method = new ReflectionMethod($service, 'validateOrderData');
|
|
$method->setAccessible(true);
|
|
|
|
$method->invoke($service, validDhlPhaseOneOrderData('V62WP'));
|
|
})->throws(InvalidArgumentException::class);
|
|
|
|
it('builds a DHL Kleinpaket payload', function () {
|
|
config([
|
|
'dhl.account_numbers.V62KP' => '63144073556201',
|
|
'dhl.legacy.sandbox' => false,
|
|
'dhl.legacy.test_mode' => false,
|
|
]);
|
|
|
|
$service = makeDhlPhaseOneShippingService();
|
|
$method = new ReflectionMethod($service, 'buildShipmentPayload');
|
|
$method->setAccessible(true);
|
|
|
|
$payload = $method->invoke($service, validDhlPhaseOneOrderData('V62KP'));
|
|
|
|
expect($payload['shipments'][0]['product'])->toBe('V62KP')
|
|
->and($payload['shipments'][0]['billingNumber'])->toBe('63144073556201')
|
|
->and($payload['shipments'][0]['refNo'])->toBe('ORDER-123456');
|
|
});
|
|
|
|
it('uses mustEncode only for German consignee addresses', function () {
|
|
$service = makeDhlPhaseOneShippingService();
|
|
$method = new ReflectionMethod($service, 'shouldUseMustEncode');
|
|
$method->setAccessible(true);
|
|
|
|
expect($method->invoke($service, validDhlPhaseOneOrderData('V01PAK', 'DE') + ['print_only_if_codeable' => true]))->toBeTrue()
|
|
->and($method->invoke($service, validDhlPhaseOneOrderData('V53WPAK', 'AT') + ['print_only_if_codeable' => true]))->toBeFalse()
|
|
->and($method->invoke($service, validDhlPhaseOneOrderData('V01PAK', 'DE') + ['print_only_if_codeable' => false]))->toBeFalse();
|
|
});
|
|
|
|
it('turns non-codeable DHL responses into address validation errors', function () {
|
|
$service = makeDhlPhaseOneShippingService();
|
|
$method = new ReflectionMethod($service, 'assertSuccessfulShipmentResponse');
|
|
$method->setAccessible(true);
|
|
|
|
$method->invoke($service, [
|
|
'status' => [
|
|
'statusCode' => 200,
|
|
],
|
|
'items' => [[
|
|
'sstatus' => [
|
|
'statusCode' => 400,
|
|
'detail' => 'Consignee address is not encodable.',
|
|
],
|
|
'validationMessages' => [[
|
|
'validationMessage' => 'Address is not encodable.',
|
|
]],
|
|
]],
|
|
], true);
|
|
})->throws(DhlAddressValidationException::class, 'DHL kann diese Adresse nicht leitcodieren.');
|