# Packstation Return-Label Restriction

**Datum:** 23.01.2026  
**Status:** ✅ Implementiert

## Problem

**User-Feedback:**  
"Wenn ich ein Label habe aus einer Packstation, kann ich kein Return Label erzeugen."

**Fehler:**

```
Fehler beim Erstellen des Retourenlabels: DHL API error (400): 0 of 1 shipment successfully printed.
```

## Root Cause

### DHL Packstation Limitierung

**Packstationen** sind **Einwegadressen**:

- ✅ Sendungen **AN** eine Packstation: Erlaubt
- ❌ Sendungen **VON** einer Packstation: **Nicht erlaubt**

### Return-Label Logik

Bei einem Return-Label werden Sender/Empfänger vertauscht:

```
Original-Label:
  Sender:     Shop (Musterstraße 1, Berlin)
  Empfänger:  Kunde (Packstation 145, Hamburg) ✅

Return-Label (vertauscht):
  Sender:     Kunde (Packstation 145, Hamburg) ❌
  Empfänger:  Shop (Musterstraße 1, Berlin)
```

**Problem:** Packstation kann nicht als Sender verwendet werden!

## DHL API Verhalten

### Request Payload (Fallback via ShippingService)

```json
{
  "profile": "STANDARD_GRUPPENPROFIL",
  "shipments": [
    {
      "product": "V01PAK",
      "shipper": {
        "name1": "Max Mustermann",
        "addressStreet": "Packstation",
        "addressHouse": "145",
        "postalCode": "20095",
        "city": "Hamburg",
        "country": "DEU",
        "postNumber": "1234567890" // ❌ Packstation als Sender!
      },
      "consignee": {
        "name1": "Mein Shop",
        "addressStreet": "Musterstraße",
        "addressHouse": "1",
        "postalCode": "10115",
        "city": "Berlin",
        "country": "DEU"
      }
    }
  ]
}
```

### DHL API Response

```json
{
  "Status": {
    "statusCode": 400,
    "statusText": "Bad Request"
  },
  "labelData": "",
  "shipmentNumber": "",
  "errors": [
    {
      "message": "0 of 1 shipment successfully printed."
    }
  ]
}
```

## Lösung

### 1. Backend-Validation (Controller)

**File:** `app/Http/Controllers/DhlShipmentController.php`

```php
public function createReturnLabel(Request $request, DhlShipment $shipment): JsonResponse
{
    // ... existing checks ...

    // Check if recipient is a Packstation (cannot be used as return sender)
    $recipient = is_string($shipment->recipient)
        ? json_decode($shipment->recipient, true)
        : $shipment->recipient;

    $hasPostNumber = !empty($recipient['postnumber'] ?? $recipient['postNumber'] ?? '');

    if ($hasPostNumber) {
        return response()->json([
            'success' => false,
            'message' => 'Retourenlabels können nicht für Packstation-Sendungen erstellt werden. ' .
                         'DHL erlaubt keine Sendungen VON einer Packstation. ' .
                         'Der Kunde muss die Retoure über eine normale Adresse versenden.',
        ], 422);
    }

    // ... continue with return label creation ...
}
```

**Prüflogik:**

- ✅ Prüft ob `postnumber` oder `postNumber` im Empfänger-JSON existiert
- ✅ Gibt HTTP 422 (Unprocessable Entity) zurück
- ✅ Klare, verständliche Fehlermeldung

### 2. UI - DataTable (Cockpit)

**File:** `app/Http/Controllers/DhlShipmentController.php`

```php
->addColumn('actions', function ($shipment) {
    // ...

    // Return label button (only for non-Packstation outbound shipments)
    if ($shipment->type == 'outbound' && ! $shipment->returns()->count()) {
        $recipient = is_string($shipment->recipient)
            ? json_decode($shipment->recipient, true)
            : $shipment->recipient;

        $hasPostNumber = !empty($recipient['postnumber'] ?? $recipient['postNumber'] ?? '');

        if (!$hasPostNumber) {
            // Regular address: Show active button
            $buttons .= '<button type="button" class="btn btn-sm btn-outline-info create-return-btn"
                            data-shipment-id="' . $shipment->id . '"
                            data-toggle="tooltip"
                            title="Retourenlabel erstellen">
                            <i class="fas fa-undo"></i>
                        </button>';
        } else {
            // Packstation: Show disabled button with explanation
            $buttons .= '<button type="button" class="btn btn-sm btn-outline-secondary"
                            disabled
                            data-toggle="tooltip"
                            title="Retourenlabels nicht möglich für Packstation-Sendungen">
                            <i class="fas fa-undo text-muted"></i>
                        </button>';
        }
    }

    // ...
})
```

**UI-Verhalten:**

- ✅ **Packstation:** Disabled Button mit Tooltip
- ✅ **Normale Adresse:** Aktiver Button
- ✅ Icon wird grau dargestellt bei Packstation

### 3. UI - Order Detail View

**File:** `resources/views/admin/sales/_detail_dhl_shipments.blade.php`

```blade
@if($shipment->type === 'outbound' && !$shipment->returns->count())
    @php
        $recipient = is_string($shipment->recipient)
            ? json_decode($shipment->recipient, true)
            : $shipment->recipient;
        $hasPostNumber = !empty($recipient['postnumber'] ?? $recipient['postNumber'] ?? '');
    @endphp

    @if(!$hasPostNumber)
        <button type="button"
                class="btn btn-outline-info dhl-create-return-btn"
                data-shipment-id="{{ $shipment->id }}"
                title="Retourenlabel erstellen">
            <i class="fas fa-undo"></i>
        </button>
    @else
        <button type="button"
                class="btn btn-outline-secondary"
                disabled
                title="Retourenlabels nicht möglich für Packstation-Sendungen">
            <i class="fas fa-undo text-muted"></i>
        </button>
    @endif
@endif
```

### 4. UI - Shipment Detail View

**File:** `resources/views/admin/dhl/show.blade.php`

```blade
@if($shipment->type == 'outbound' && !$shipment->returns->count())
    @php
        $recipient = is_string($shipment->recipient)
            ? json_decode($shipment->recipient, true)
            : $shipment->recipient;
        $hasPostNumber = !empty($recipient['postnumber'] ?? $recipient['postNumber'] ?? '');
    @endphp

    @if(!$hasPostNumber)
        <button type="button"
                class="btn btn-info mr-2"
                id="create-return-btn"
                data-shipment-id="{{ $shipment->id }}">
            <i class="fas fa-undo"></i> Retourenlabel erstellen
        </button>
    @else
        <button type="button"
                class="btn btn-secondary mr-2"
                disabled
                title="Retourenlabels nicht möglich für Packstation-Sendungen">
            <i class="fas fa-undo"></i> Packstation-Sendung
        </button>
        <small class="text-muted d-block mb-2">
            <i class="fas fa-info-circle"></i>
            Retourenlabels können nicht für Packstation-Sendungen erstellt werden,
            da DHL keine Sendungen VON einer Packstation erlaubt.
        </small>
    @endif
@endif
```

**Besonderheit Detail-View:**

- ✅ Zusätzlicher Info-Text unter dem Button
- ✅ Icon-Indikator für besseres Verständnis

## Packstation-Erkennung

### Datenstruktur

**DhlShipment Model:**

```php
$shipment->recipient = '{
    "firstname": "Max",
    "lastname": "Mustermann",
    "street": "Packstation 145",
    "postalcode": "20095",
    "city": "Hamburg",
    "country": "DE",
    "postnumber": "1234567890"  // ← Identifikator!
}'
```

### Detection Logic

```php
$recipient = is_string($shipment->recipient)
    ? json_decode($shipment->recipient, true)
    : $shipment->recipient;

// Check both possible keys (case-insensitive)
$hasPostNumber = !empty(
    $recipient['postnumber'] ??
    $recipient['postNumber'] ??
    ''
);

if ($hasPostNumber) {
    // This is a Packstation delivery
}
```

**Keys geprüft:**

- `postnumber` (lowercase)
- `postNumber` (camelCase)

## Testing

### Test 1: Packstation-Sendung - API Call blockiert

```bash
# Via Tinker
$shipment = DhlShipment::find(24); // Packstation-Sendung
$controller = new App\Http\Controllers\DhlShipmentController();
$response = $controller->createReturnLabel(new Illuminate\Http\Request(), $shipment);

# Expected Response:
{
    "success": false,
    "message": "Retourenlabels können nicht für Packstation-Sendungen erstellt werden.
                DHL erlaubt keine Sendungen VON einer Packstation.
                Der Kunde muss die Retoure über eine normale Adresse versenden."
}
# HTTP Status: 422
```

### Test 2: Packstation-Sendung - Button disabled

**Schritte:**

1. Cockpit öffnen
2. Packstation-Sendung suchen (ID 24)
3. ✅ Return-Button ist grau/disabled
4. ✅ Tooltip zeigt "Retourenlabels nicht möglich für Packstation-Sendungen"
5. ✅ Icon ist grau (`text-muted`)

### Test 3: Normale Sendung - Return funktioniert

**Schritte:**

1. Normale Outbound-Sendung auswählen (kein postNumber)
2. ✅ Return-Button ist aktiv/blau
3. Button klicken
4. ✅ Return-Label wird erfolgreich erstellt

### Test 4: Detail-View mit Info-Text

**Schritte:**

1. Packstation-Sendung Detail-View öffnen (ID 24)
2. ✅ "Packstation-Sendung" Button ist disabled
3. ✅ Info-Text sichtbar unter dem Button
4. ✅ Erklärt warum Return nicht möglich ist

## Database Check

```sql
-- Find all Packstation shipments
SELECT
    id,
    type,
    dhl_shipment_no,
    JSON_EXTRACT(recipient, '$.postnumber') as postnumber,
    JSON_EXTRACT(recipient, '$.street') as street
FROM dhl_package_shipments
WHERE
    type = 'outbound'
    AND (
        JSON_EXTRACT(recipient, '$.postnumber') IS NOT NULL
        OR JSON_EXTRACT(recipient, '$.postNumber') IS NOT NULL
    );
```

**Example Results:**

```
id  | type     | dhl_shipment_no           | postnumber   | street
----|----------|---------------------------|--------------|------------------
24  | outbound | 0034043333301020021029524 | 1234567890   | "Packstation 145"
```

## User Experience

### Vorher (Fehler)

```
1. User klickt "Retourenlabel erstellen"
2. API Call wird gesendet
3. ❌ DHL API error (400): 0 of 1 shipment successfully printed
4. ❌ Kryptische Fehlermeldung
5. ❌ User weiß nicht warum es fehlschlägt
```

### Nachher (Proaktive Verhinderung)

```
1. User öffnet Packstation-Sendung
2. ✅ Button ist disabled mit Tooltip
3. ✅ Info-Text erklärt warum nicht möglich
4. ✅ Kein fehlgeschlagener API Call
5. ✅ Klarheit für den User
```

## Alternative Lösungen (nicht implementiert)

### Option 1: Shop-Adresse als Return-Empfänger

**Idee:** Bei Packstation-Sendungen automatisch Shop-Adresse als Rücksendeadresse verwenden.

**Problem:**

- Kunde muss Paket zur Post/DHL-Filiale bringen
- Nicht transparent für Kunden
- Komplexere Logik

**Entscheidung:** ❌ Nicht umgesetzt

### Option 2: QR-Code Return ohne Label

**Idee:** DHL QR-Code Return für Packstation-Kunden.

**Problem:**

- Erfordert separates DHL API Produkt
- Nicht in allen Accounts verfügbar
- Separate Implementierung nötig

**Entscheidung:** ❌ Nicht umgesetzt (könnte zukünftig ergänzt werden)

## Geänderte Dateien

1. ✅ `app/Http/Controllers/DhlShipmentController.php`

   - Zeile 427-437: Backend-Validation für Packstation-Check
   - Zeile 224-236: DataTable Button-Logik mit Packstation-Check

2. ✅ `resources/views/admin/sales/_detail_dhl_shipments.blade.php`

   - Zeile 108-127: Blade-Logic für Return-Button (disabled bei Packstation)

3. ✅ `resources/views/admin/dhl/show.blade.php`
   - Zeile 161-182: Detail-View mit disabled Button + Info-Text

## Status

✅ **Backend-Validation:** Implementiert  
✅ **UI-Anpassungen (Cockpit):** Implementiert  
✅ **UI-Anpassungen (Order Detail):** Implementiert  
✅ **UI-Anpassungen (Shipment Detail):** Implementiert  
✅ **Dokumentation:** Erstellt

## Nächste Schritte

1. [ ] Testen: Packstation-Sendung im Cockpit prüfen
2. [ ] Testen: Return-Button sollte disabled sein
3. [ ] Testen: Normale Sendung kann weiterhin Return-Label erstellen
4. [ ] Optional: Kunde über alternative Return-Optionen informieren (Email)

## Lessons Learned

1. **Proaktive UI-Validierung:** Buttons disablen ist besser als Fehler zeigen
2. **Klare Fehlermeldungen:** User sollten verstehen WARUM etwas nicht geht
3. **DHL Limitierungen:** Packstationen sind Einweg-Adressen (nur AN, nicht VON)
4. **Multiple Keys prüfen:** `postnumber` vs `postNumber` - beide abfangen
