27-05-2026 Update DHL Modul v2.0
This commit is contained in:
parent
53bdba33cd
commit
036595be94
41 changed files with 3346 additions and 310 deletions
|
|
@ -2,18 +2,18 @@
|
|||
|
||||
namespace App\Services;
|
||||
|
||||
use Acme\Dhl\Exceptions\DhlAddressValidationException;
|
||||
use Acme\Dhl\Models\DhlShipment;
|
||||
use App\Models\ShoppingOrder;
|
||||
use App\Http\Controllers\SettingController;
|
||||
use App\Jobs\CreateShipmentJob;
|
||||
use App\Jobs\CancelShipmentJob;
|
||||
use App\Services\DhlDataHelper;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use App\Jobs\CreateShipmentJob;
|
||||
use App\Models\ShoppingOrder;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* DHL Shipment Service
|
||||
*
|
||||
*
|
||||
* Handles both synchronous and asynchronous shipment creation
|
||||
* based on configuration settings
|
||||
*/
|
||||
|
|
@ -21,20 +21,23 @@ class DhlShipmentService
|
|||
{
|
||||
/**
|
||||
* Create a DHL shipment (sync or async based on config)
|
||||
*
|
||||
* @param ShoppingOrder $order
|
||||
* @param float $weight
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
public function createShipment(ShoppingOrder $order, float $weight = 1.0, array $options = []): array
|
||||
{
|
||||
$weight = max($weight, (new DhlShipmentWeightCalculator)->calculate($order));
|
||||
|
||||
// Get DHL configuration
|
||||
$settingController = new SettingController();
|
||||
$settingController = new SettingController;
|
||||
$dhlConfig = $settingController->getDhlConfig();
|
||||
\Log::info('dhlConfig', $dhlConfig);
|
||||
// Check if queue should be used
|
||||
$useQueue = $dhlConfig['use_queue'] ?? false;
|
||||
if ($useQueue && $this->requiresSynchronousAddressValidation($options, $dhlConfig)) {
|
||||
Log::info('[DHL Service] Queue disabled for DHL mustEncode address validation', [
|
||||
'order_id' => $order->id,
|
||||
]);
|
||||
$useQueue = false;
|
||||
}
|
||||
|
||||
if ($useQueue) {
|
||||
return $this->createShipmentAsync($order, $weight, $options, $dhlConfig);
|
||||
|
|
@ -43,14 +46,20 @@ class DhlShipmentService
|
|||
}
|
||||
}
|
||||
|
||||
private function requiresSynchronousAddressValidation(array $options, array $dhlConfig): bool
|
||||
{
|
||||
if (! (bool) ($options['print_only_if_codeable'] ?? $dhlConfig['print_only_if_codeable'] ?? config('dhl.print_only_if_codeable', true))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$country = $options['shipping_address']['country'] ?? null;
|
||||
$countryCode = is_object($country) ? ($country->code ?? null) : ($country['code'] ?? null);
|
||||
|
||||
return strtoupper((string) $countryCode) === DhlProductResolver::DOMESTIC_COUNTRY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create shipment asynchronously using queue
|
||||
*
|
||||
* @param ShoppingOrder $order
|
||||
* @param float $weight
|
||||
* @param array $options
|
||||
* @param array $dhlConfig
|
||||
* @return array
|
||||
*/
|
||||
private function createShipmentAsync(ShoppingOrder $order, float $weight, array $options, array $dhlConfig): array
|
||||
{
|
||||
|
|
@ -60,14 +69,14 @@ class DhlShipmentService
|
|||
|
||||
Log::info('[DHL Service] Shipment creation dispatched to queue', [
|
||||
'order_id' => $order->id,
|
||||
'weight' => $weight
|
||||
'weight' => $weight,
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => 'Sendung wird erstellt. Sie erhalten eine Benachrichtigung, sobald das Versandlabel verfügbar ist.',
|
||||
'queued' => true,
|
||||
'order_id' => $order->id
|
||||
'order_id' => $order->id,
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
Log::error('[DHL Service] Failed to dispatch shipment creation', [
|
||||
|
|
@ -77,27 +86,21 @@ class DhlShipmentService
|
|||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Fehler beim Einreihen der Sendungserstellung: ' . $e->getMessage(),
|
||||
'queued' => false
|
||||
'message' => 'Fehler beim Einreihen der Sendungserstellung: '.$e->getMessage(),
|
||||
'queued' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create shipment synchronously
|
||||
*
|
||||
* @param ShoppingOrder $order
|
||||
* @param float $weight
|
||||
* @param array $options
|
||||
* @param array $dhlConfig
|
||||
* @return array
|
||||
*/
|
||||
private function createShipmentSync(ShoppingOrder $order, float $weight, array $options, array $dhlConfig): array
|
||||
{
|
||||
try {
|
||||
Log::info('[DHL Service] Creating shipment synchronously', [
|
||||
'order_id' => $order->id,
|
||||
'weight' => $weight
|
||||
'weight' => $weight,
|
||||
]);
|
||||
|
||||
// Create DHL client directly with correct base URL
|
||||
|
|
@ -132,32 +135,42 @@ class DhlShipmentService
|
|||
'label_path' => $result['labelPath'] ?? null,
|
||||
'label_url' => $result['labelUrl'] ?? null,
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
Log::error('[DHL Service] Shipment creation failed (sync)', [
|
||||
} catch (DhlAddressValidationException $e) {
|
||||
Log::warning('[DHL Service] Shipment address validation failed (sync)', [
|
||||
'order_id' => $order->id,
|
||||
'error' => $e->getMessage()
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Fehler beim Erstellen des Versandlabels: ' . $e->getMessage(),
|
||||
'type' => 'dhl_address_validation',
|
||||
'message' => $e->getMessage(),
|
||||
'errors' => [$e->getMessage()],
|
||||
'queued' => false,
|
||||
'order_id' => $order->id
|
||||
'order_id' => $order->id,
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
Log::error('[DHL Service] Shipment creation failed (sync)', [
|
||||
'order_id' => $order->id,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Fehler beim Erstellen des Versandlabels: '.$e->getMessage(),
|
||||
'queued' => false,
|
||||
'order_id' => $order->id,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a DHL shipment (sync or async based on config)
|
||||
*
|
||||
* @param DhlShipment $shipment
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
public function cancelShipment(DhlShipment $shipment, array $options = []): array
|
||||
{
|
||||
// Get DHL configuration
|
||||
$settingController = new SettingController();
|
||||
$settingController = new SettingController;
|
||||
$dhlConfig = $settingController->getDhlConfig();
|
||||
|
||||
// Check if queue should be used
|
||||
|
|
@ -172,11 +185,6 @@ class DhlShipmentService
|
|||
|
||||
/**
|
||||
* Cancel shipment asynchronously using queue
|
||||
*
|
||||
* @param DhlShipment $shipment
|
||||
* @param array $options
|
||||
* @param array $dhlConfig
|
||||
* @return array
|
||||
*/
|
||||
private function cancelShipmentAsync(DhlShipment $shipment, array $options, array $dhlConfig): array
|
||||
{
|
||||
|
|
@ -186,14 +194,14 @@ class DhlShipmentService
|
|||
|
||||
Log::info('[DHL Service] Shipment cancellation dispatched to queue', [
|
||||
'shipment_id' => $shipment->id,
|
||||
'dhl_shipment_no' => $shipment->dhl_shipment_no
|
||||
'dhl_shipment_no' => $shipment->dhl_shipment_no,
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => 'Sendung wird storniert...',
|
||||
'queued' => true,
|
||||
'shipment_id' => $shipment->id
|
||||
'shipment_id' => $shipment->id,
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
Log::error('[DHL Service] Failed to dispatch shipment cancellation', [
|
||||
|
|
@ -203,40 +211,39 @@ class DhlShipmentService
|
|||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Fehler beim Einreihen der Stornierung: ' . $e->getMessage(),
|
||||
'queued' => false
|
||||
'message' => 'Fehler beim Einreihen der Stornierung: '.$e->getMessage(),
|
||||
'queued' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel shipment synchronously
|
||||
*
|
||||
* @param DhlShipment $shipment
|
||||
* @param array $options
|
||||
* @param array $dhlConfig
|
||||
* @return array
|
||||
*/
|
||||
private function cancelShipmentSync(DhlShipment $shipment, array $options, array $dhlConfig): array
|
||||
{
|
||||
try {
|
||||
// Validate shipment has DHL number
|
||||
if (empty($shipment->dhl_shipment_no)) {
|
||||
$this->recordCancellationFailure($shipment, 'missing_shipment_number', 'Sendung hat keine DHL-Sendungsnummer und kann nicht storniert werden.');
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Sendung hat keine DHL-Sendungsnummer und kann nicht storniert werden.',
|
||||
'queued' => false,
|
||||
'shipment_id' => $shipment->id
|
||||
'shipment_id' => $shipment->id,
|
||||
];
|
||||
}
|
||||
|
||||
// Validate shipment can be cancelled
|
||||
if (! $shipment->canCancel()) {
|
||||
$this->recordCancellationFailure($shipment, 'status_not_cancelable', 'Sendung kann im aktuellen Status "'.$shipment->status.'" nicht storniert werden.');
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Sendung kann im aktuellen Status "' . $shipment->status . '" nicht storniert werden. Nur Status "created" oder "pending" sind stornierbar.',
|
||||
'message' => 'Sendung kann im aktuellen Status "'.$shipment->getStatusTranslation().'" nicht storniert werden. Nur Status "Erstellt" oder "Wartend" sind stornierbar.',
|
||||
'queued' => false,
|
||||
'shipment_id' => $shipment->id
|
||||
'shipment_id' => $shipment->id,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -244,7 +251,7 @@ class DhlShipmentService
|
|||
'shipment_id' => $shipment->id,
|
||||
'dhl_shipment_no' => $shipment->dhl_shipment_no,
|
||||
'status' => $shipment->status,
|
||||
'base_url' => $dhlConfig['base_url']
|
||||
'base_url' => $dhlConfig['base_url'],
|
||||
]);
|
||||
|
||||
// Create DHL client
|
||||
|
|
@ -263,37 +270,41 @@ class DhlShipmentService
|
|||
if ($success) {
|
||||
Log::info('[DHL Service] Shipment cancelled successfully (sync)', [
|
||||
'shipment_id' => $shipment->id,
|
||||
'dhl_shipment_no' => $shipment->dhl_shipment_no
|
||||
'dhl_shipment_no' => $shipment->dhl_shipment_no,
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => 'Sendung wurde erfolgreich storniert!',
|
||||
'queued' => false,
|
||||
'shipment_id' => $shipment->id
|
||||
'shipment_id' => $shipment->id,
|
||||
];
|
||||
} else {
|
||||
throw new Exception('Cancellation returned false');
|
||||
}
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
$this->recordCancellationFailure($shipment, 'validation_failed', $e->getMessage(), $e);
|
||||
|
||||
Log::warning('[DHL Service] Shipment cancellation validation failed', [
|
||||
'shipment_id' => $shipment->id,
|
||||
'error' => $e->getMessage()
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => $e->getMessage(),
|
||||
'queued' => false,
|
||||
'shipment_id' => $shipment->id
|
||||
'shipment_id' => $shipment->id,
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
$this->recordCancellationFailure($shipment, 'api_failed', $e->getMessage(), $e);
|
||||
|
||||
Log::error('[DHL Service] Shipment cancellation failed (sync)', [
|
||||
'shipment_id' => $shipment->id,
|
||||
'dhl_shipment_no' => $shipment->dhl_shipment_no,
|
||||
'status' => $shipment->status,
|
||||
'error' => $e->getMessage(),
|
||||
'error_trace' => $e->getTraceAsString()
|
||||
'error_trace' => $e->getTraceAsString(),
|
||||
]);
|
||||
|
||||
// Check if it's an API authentication/resource error
|
||||
|
|
@ -304,16 +315,50 @@ class DhlShipmentService
|
|||
'message' => 'Die Sendung konnte bei DHL nicht gefunden werden. Mögliche Ursachen: Sendung wurde bereits storniert, ist zu alt, oder wurde in einem anderen Modus (Sandbox/Production) erstellt.',
|
||||
'queued' => false,
|
||||
'shipment_id' => $shipment->id,
|
||||
'technical_error' => $errorMessage
|
||||
'technical_error' => $errorMessage,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Fehler beim Stornieren der Sendung: ' . $errorMessage,
|
||||
'message' => 'Fehler beim Stornieren der Sendung: '.$errorMessage,
|
||||
'queued' => false,
|
||||
'shipment_id' => $shipment->id
|
||||
'shipment_id' => $shipment->id,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
private function recordCancellationFailure(DhlShipment $shipment, string $reason, string $detail, ?Exception $exception = null): void
|
||||
{
|
||||
$apiResponseData = $shipment->api_response_data ?? [];
|
||||
$apiResponseData['cancellation_error'] = [
|
||||
'status' => 'failed',
|
||||
'reason' => $reason,
|
||||
'http_status' => $exception ? $this->extractHttpStatus($exception->getMessage()) : null,
|
||||
'dhl_code' => $exception ? $this->extractDhlErrorCode($exception->getMessage()) : null,
|
||||
'detail' => $detail,
|
||||
'exception_class' => $exception ? $exception::class : null,
|
||||
'occurred_at' => now()->toISOString(),
|
||||
];
|
||||
|
||||
$shipment->update(['api_response_data' => $apiResponseData]);
|
||||
}
|
||||
|
||||
private function extractHttpStatus(string $message): ?int
|
||||
{
|
||||
if (preg_match('/\b([45][0-9]{2})\b/', $message, $matches)) {
|
||||
return (int) $matches[1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function extractDhlErrorCode(string $message): ?string
|
||||
{
|
||||
if (preg_match('/\b([A-Z]{2}-[A-Za-z0-9_-]+)\b/', $message, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue