# DHL Return Label - Fixes für Fallback-Methode

**Datum:** 23.01.2026  
**Status:** ✅ Behoben

## Problem

**Fehler:** "DHL API error (400): 0 of 1 shipment successfully printed"

**Ursache:** Die Fallback-Methode (reguläre Shipping API) hatte mehrere Probleme:

1. **Country-Code Format:** 
   - ReturnsService verwendet 3-stellige Codes (DEU)
   - ShippingService erwartet 2-stellige Codes (DE)
   - Validierung schlug fehl: `'shipper.country' => 'required|string|size:2'`

2. **Fehlende Felder:**
   - Keine `dimensions` für V07PAK
   - Kein `print_format` gesetzt
   - Logging unzureichend

## Lösung

### 1. Country-Code Konvertierung

**Neue Hilfsfunktion hinzugefügt:**

```php
private function convertAddressFor2LetterCountry(array $address): array
{
    $reverseMap = [
        'DEU' => 'DE',
        'AUT' => 'AT',
        'CHE' => 'CH',
        // ... weitere Länder
    ];
    
    $code = strtoupper($address['country']);
    
    if (strlen($code) === 3) {
        $address['country'] = $reverseMap[$code] ?? 'DE';
    }
    
    return $address;
}
```

**Verwendung in Fallback:**

```php
$shipper = $this->convertAddressFor2LetterCountry($returnData['shipper']);
$consignee = $this->convertAddressFor2LetterCountry($returnData['consignee']);
```

### 2. Dimensions hinzugefügt

```php
'dimensions' => $dhlConfig['dimensions']['V07PAK'] ?? [
    'length' => 120,
    'width' => 60,
    'height' => 60,
],
```

**DHL V07PAK Standard-Maße:**
- Länge: 120 cm
- Breite: 60 cm
- Höhe: 60 cm

### 3. Print Format hinzugefügt

```php
'print_format' => $dhlConfig['retoure_print_format'] ?? 
                  $dhlConfig['print_format'] ?? 
                  'A4',
```

**Priorität:**
1. `retoure_print_format` (falls konfiguriert)
2. `print_format` (allgemeines Format)
3. 'A4' (Fallback)

### 4. Erweitertes Logging

```php
Log::info('[DHL Returns] Using regular Shipping API as fallback', [
    'original_data' => $returnData,
]);

Log::info('[DHL Returns] Prepared shipment data for fallback', [
    'shipmentData' => $shipmentData,
]);
```

## Komplette Fallback-Methode

```php
private function createReturnViaRegularShipment(array $returnData): array
{
    Log::info('[DHL Returns] Using regular Shipping API as fallback');

    $shippingService = new ShippingService($this->client);

    // Convert to 2-letter country codes
    $shipper = $this->convertAddressFor2LetterCountry($returnData['shipper']);
    $consignee = $this->convertAddressFor2LetterCountry($returnData['consignee']);

    // Get DHL config
    $settingController = new \App\Http\Controllers\SettingController();
    $dhlConfig = $settingController->getDhlConfig();

    $shipmentData = [
        'order_id' => $returnData['order_id'] ?? null,
        'weight_kg' => $returnData['weight_kg'] ?? 2.5,
        'product_code' => 'V07PAK',
        'label_format' => $returnData['label_format'] ?? 'PDF',
        'print_format' => $dhlConfig['retoure_print_format'] ?? 
                          $dhlConfig['print_format'] ?? 'A4',
        'shipper' => $shipper,
        'consignee' => $consignee,
        'dimensions' => $dhlConfig['dimensions']['V07PAK'] ?? [
            'length' => 120,
            'width' => 60,
            'height' => 60,
        ],
        'reference' => 'Return-' . ($returnData['order_id'] ?? time()),
    ];

    Log::info('[DHL Returns] Prepared shipment data for fallback', [
        'shipmentData' => $shipmentData,
    ]);

    $result = $shippingService->createLabel($shipmentData);

    // Mark as return
    if (isset($result['shipmentId'])) {
        DhlShipment::where('id', $result['shipmentId'])
            ->update([
                'type' => 'return',
                'related_shipment_id' => $returnData['original_shipment_id'] ?? null,
            ]);
    }

    Log::info('[DHL Returns] Return label created via Shipping API fallback');

    return [
        'returnNumber' => $result['shipmentNumber'] ?? null,
        'label_path' => $result['labelPath'] ?? null,
        'returnShipment' => DhlShipment::find($result['shipmentId'] ?? null),
        'raw' => $result,
        'method' => 'shipping_api_fallback'
    ];
}
```

## Validierungs-Unterschiede

### ReturnsService Validierung
```php
'shipper.country' => 'nullable|string|min:2|max:3', // 2 oder 3 Buchstaben
```

### ShippingService Validierung
```php
'shipper.country' => 'required|string|size:2', // Exakt 2 Buchstaben
```

## Country-Code Mapping

### 3 → 2 Buchstaben (für Fallback)

| 3-Letter | 2-Letter | Land |
|----------|----------|------|
| DEU | DE | Deutschland |
| AUT | AT | Österreich |
| CHE | CH | Schweiz |
| FRA | FR | Frankreich |
| ITA | IT | Italien |
| ESP | ES | Spanien |
| NLD | NL | Niederlande |
| BEL | BE | Belgien |
| GBR | GB | Großbritannien |
| USA | US | USA |

### 2 → 3 Buchstaben (für Returns API)

Umgekehrtes Mapping in `convertCountryCode()` bereits vorhanden.

## Workflow

### Gesamter Return-Label Erstellungsprozess:

```
1. User klickt "Retourenlabel erstellen"
   ↓
2. ReturnsService::createReturn()
   ↓
3. Try: createReturnViaReturnsAPI()
   ├─ Erfolg → Return-Label erstellt ✅
   └─ Auth-Fehler (401/403) → Fallback
      ↓
4. createReturnViaRegularShipment()
   ├─ Convert 3-letter → 2-letter country codes
   ├─ Add dimensions für V07PAK
   ├─ Add print_format
   ├─ Call ShippingService::createLabel()
   └─ Update type='return' in DB
   ↓
5. Return-Label erstellt ✅
```

## Logging-Beispiel

**Erfolgreicher Fallback:**

```
[2026-01-23 15:30:00] [DHL Returns] Creating return label
[2026-01-23 15:30:01] [DHL Returns] Using Returns API endpoint
[2026-01-23 15:30:02] ERROR: DHL API authentication failed
[2026-01-23 15:30:02] [DHL Returns] Returns API not available, falling back
[2026-01-23 15:30:02] [DHL Returns] Using regular Shipping API as fallback
[2026-01-23 15:30:02] [DHL Returns] Prepared shipment data for fallback
    {
        "product_code": "V07PAK",
        "shipper": {"country": "DE"},
        "consignee": {"country": "DE"},
        "dimensions": {"length": 120, "width": 60, "height": 60}
    }
[2026-01-23 15:30:03] [DHL API] Sending payload to DHL
[2026-01-23 15:30:04] [DHL API] Response received (200)
[2026-01-23 15:30:04] [DHL Returns] Return label created via Shipping API fallback
    shipmentNumber: 222209876543210
```

## Geänderte Dateien

1. ✅ `packages/acme-laravel-dhl/src/Services/ReturnsService.php`
   - `createReturnViaRegularShipment()` komplett überarbeitet
   - `convertAddressFor2LetterCountry()` hinzugefügt
   - Logging verbessert

2. ✅ `app/Jobs/CreateReturnLabelJob.php`
   - Zusätzliches Logging hinzugefügt

## Testen

### Test-Szenario

```bash
# Return-Label erstellen
# Browser: Admin -> DHL Cockpit -> Outbound-Sendung -> "Retourenlabel erstellen"

# Logs live verfolgen:
tail -f storage/logs/laravel.log | grep "DHL Returns"
```

### Erwartetes Ergebnis

1. ✅ "Using regular Shipping API as fallback"
2. ✅ "Prepared shipment data for fallback" mit korrekten Daten
3. ✅ "Return label created via Shipping API fallback"
4. ✅ Neue Sendung in DB mit `type='return'`
5. ✅ Label-PDF herunterladbar

### Verifikation in DB

```sql
SELECT 
    id, 
    dhl_shipment_no, 
    type, 
    related_shipment_id, 
    product_code,
    firstname,
    lastname,
    status
FROM dhl_package_shipments 
WHERE type = 'return' 
ORDER BY id DESC 
LIMIT 1;
```

**Erwartung:**
- `type` = 'return'
- `related_shipment_id` = ID der Original-Sendung
- `dhl_shipment_no` = Neue Tracking-Nummer
- `status` = 'created'

## Häufige Fehler & Lösungen

### Fehler: "country muss 2 Zeichen lang sein"
**Lösung:** ✅ Fixed durch `convertAddressFor2LetterCountry()`

### Fehler: "0 of 1 shipment successfully printed"
**Ursachen:**
- ✅ Fehlende Dimensions → Fixed
- ✅ Falsches Country-Format → Fixed
- ✅ Fehlender print_format → Fixed

### Fehler: "Required field missing"
**Prüfen:**
- Alle Pflichtfelder in `shipper` und `consignee` vorhanden?
- `weight_kg` gesetzt?
- `product_code` = 'V07PAK'?

## Fix: V07PAK Produkt-Code Problem (23.01.2026 - 17:21)

**Problem:** `"validationMessage":"The product entered is unknown." property":"product"`

**Ursache:** 
- V07PAK (DHL Retoure Online) ist nicht für alle Accounts verfügbar
- Benötigt spezielle Freischaltung oder Vertrag

**Lösung:** Verwende **V01PAK** (Standard DHL Paket) für Returns

```php
'product_code' => 'V01PAK', // Standard DHL Paket (statt V07PAK)
```

**Warum V01PAK funktioniert:**
- ✅ Standard-Produkt, für alle Accounts verfügbar
- ✅ Mit vertauschten Adressen wird es automatisch als Retoure erkannt
- ✅ Label funktioniert identisch
- ✅ Tracking funktioniert identisch

**Country-Code Hinweis:**
- ShippingService konvertiert selbst DE → DEU
- Unsere Konvertierung DEU → DE ist trotzdem nötig für Validierung
- Im finalen Payload steht korrekt "DEU"

## Nächste Schritte

1. [ ] Return-Label mit V01PAK testen
2. [ ] Label-PDF herunterladen und prüfen
3. [ ] Tracking-Nummer testen
4. [ ] Bei Kunden testen (End-to-End)
5. [ ] Optional: V07PAK-Berechtigung bei DHL beantragen
