# Opslag en uitslag

Goederen in magazijnopslag brengen en daarna opgeslagen artikelen uitslaan: configuratie → tarief opvragen → opslag aanmaken → betalen → artikelen nog op voorraad weergeven → uitslag schatten → uitslag aanmaken → betalen → volgen → annuleren.

Dit handboek documenteert uitsluitend de volgende bewerkingen. Voer ze in volgorde uit. Authenticatie gebruikt een **klantaccount**.

Vervang `YOUR_HOST` en `ACCESS_TOKEN` door de waarden van uw omgeving.

## 1. Aanmelden (klant)

```bash
curl -X POST https://YOUR_HOST/api/v1/user/customer/login \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","password":"your_password"}'
```

```
Authorization: Bearer ACCESS_TOKEN
```

GraphQL gebruikt dezelfde header op `POST /api/graphql`.

**Verificatie:** login geeft `access_token` terug. Een latere aanroep zonder token geeft `401`.

## 2. Opslagconfiguratie

**REST:** `GET /api/v1/customer/storage-orders/config` — [REST-handboek](/api/documentation#/paths/v1-customer-storage-orders-config/get)

```bash
curl https://YOUR_HOST/api/v1/customer/storage-orders/config \
  -H "Authorization: Bearer ACCESS_TOKEN"
```

**GraphQL:** `customerStorageOrderConfig` ([GraphQL-handboek](/api/graphql/documentation#/customer/customerStorageOrderConfig))

**Verificatie:** u hebt een magazijn-`id` vastgelegd en, als de catalogus niet leeg is, een verpakkings-`id`.

## 3. Opslagtarief opvragen

**REST:** `POST /api/v1/customer/storage-orders/calculate-price`

```bash
curl -X POST https://YOUR_HOST/api/v1/customer/storage-orders/calculate-price \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "warehouse_id": 7,
    "start_date": "2026-09-10",
    "end_date": "2026-10-10",
    "items": [{
      "qty": 1,
      "length": 30,
      "width": 20,
      "height": 15,
      "dimension_unit": 2,
      "weight": 2,
      "weight_unit": 2
    }]
  }'
```

**Verificatie:** `success` of `result` is true en u hebt een prijs. Ontbrekende `warehouse_id` / datums is `400`.

## 4. De opslagorder aanmaken

**REST:** `POST /api/v1/customer/storage-orders`

```bash
curl -X POST https://YOUR_HOST/api/v1/customer/storage-orders \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: dev-store-001" \
  -d '{
    "warehouse_id": 7,
    "start_date": "2026-09-10",
    "end_date": "2026-10-10",
    "items": [{
      "description": "Box A",
      "qty": 1,
      "length": 30,
      "width": 20,
      "height": 15,
      "dimension_unit": 2,
      "weight": 2,
      "weight_unit": 2,
      "value": 100
    }]
  }'
```

**Verificatie:** het antwoord heeft `data.id`. Bewaar dat opslagorder-id.

## 5. Opslag betalen

**REST:** `POST /api/v1/customer/storage-orders/{id}/pay`

```bash
curl -X POST https://YOUR_HOST/api/v1/customer/storage-orders/1024/pay \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'
```

Optioneel: eerst `GET /api/v1/customer/storage-orders/{id}/payment-info`.

**REST:** `GET /api/v1/customer/storage-orders/{id}` — [REST-handboek](/api/documentation#/paths/v1-customer-storage-orders-id/get)

**GraphQL:** `customerStorageOrderShow` ([GraphQL-handboek](/api/graphql/documentation#/customer/customerStorageOrderShow))

**Verificatie:** de opslagorder is betaald / bevestigd. `402` betekent de portemonnee opwaarderen, daarna opnieuw.

Uitslag hieronder werkt alleen nadat pakketten in het magazijn **ontvangen** zijn. Voor een test: wacht tot personeel (of een testontvangst) ze als ontvangen heeft gemarkeerd, en ga dan verder.

## 6. Artikelen nog op voorraad weergeven

**REST:** `GET /api/v1/customer/shipout-orders/available-items` — [REST-handboek](/api/documentation#/paths/v1-customer-shipout-orders-available-items/get)

```bash
curl "https://YOUR_HOST/api/v1/customer/shipout-orders/available-items?warehouse_id=7" \
  -H "Authorization: Bearer ACCESS_TOKEN"
```

**GraphQL:** `customerShipoutAvailableItems` ([GraphQL-handboek](/api/graphql/documentation#/customer/customerShipoutAvailableItems))

**Verificatie:** u hebt een of meer `storage_package_ids` vastgelegd (voorbeeld `5001`). Lege lijst betekent dat nog niets is ontvangen — geen uitslag aanmaken. `403` betekent dat uitslag voor deze klant is uitgeschakeld.

## 7. Uitslag schatten en aanmaken

**REST:** `GET /api/v1/customer/shipout-orders/services?warehouse_id=7` — [REST-handboek](/api/documentation#/paths/v1-customer-shipout-orders-services/get)

Leg een `service_code` vast.

**REST:** `POST /api/v1/customer/shipout-orders/services/{serviceCode}/estimate` — [REST-handboek](/api/documentation#/paths/v1-customer-shipout-orders-services-serviceCode--estimate/post)

```bash
curl -X POST https://YOUR_HOST/api/v1/customer/shipout-orders/services/intl_express/estimate \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "warehouse_id": 7,
    "delivery_postcode": "M5V2H1",
    "delivery_country": "CA",
    "packages": [{
      "weight": 2.5,
      "length": 30,
      "width": 20,
      "height": 15,
      "weight_unit": 2,
      "dimension_unit": 2
    }]
  }'
```

**REST:** `POST /api/v1/customer/shipout-orders/services/{serviceCode}/orders` — [REST-handboek](/api/documentation#/paths/v1-customer-shipout-orders-services-serviceCode--orders/post)

```bash
curl -X POST https://YOUR_HOST/api/v1/customer/shipout-orders/services/intl_express/orders \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: dev-shipout-001" \
  -d '{
    "warehouse_id": 7,
    "storage_package_ids": [5001],
    "delivery_name": "Jane Recipient",
    "delivery_telephone": "5555555555",
    "delivery_email": "jane@example.com",
    "delivery_address_1": "123 King St W",
    "delivery_city": "Toronto",
    "delivery_province": "ON",
    "delivery_country": "CA",
    "delivery_postcode": "M5V2H1"
  }'
```

**GraphQL:** `customerCreateShipoutOrder` ([GraphQL-handboek](/api/graphql/documentation#/customer/customerCreateShipoutOrder))

**Verificatie:** het antwoord heeft een uitslag-`id`. Geselecteerde opslagpakketten zijn aan dit verzoek vergrendeld.

## 8. Uitslag betalen, volgen, gebeurtenissen

**REST:** `POST /api/v1/customer/shipout-orders/{id}/pay` — [REST-handboek](/api/documentation#/paths/v1-customer-shipout-orders-id--pay/post)

```bash
curl -X POST https://YOUR_HOST/api/v1/customer/shipout-orders/8001/pay \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'
```

**GraphQL:** `customerPayShipout` ([GraphQL-handboek](/api/graphql/documentation#/customer/customerPayShipout))

**REST:** `GET /api/v1/customer/shipout-orders/{id}` — [REST-handboek](/api/documentation#/paths/v1-customer-shipout-orders-id/get)

Wanneer er een trackingnummer is:

```bash
curl https://YOUR_HOST/api/v1/tracking/SR123456789012
```

[REST-handboek](/api/documentation#/operations/getPublicTracking) · [GraphQL-handboek](/api/graphql/documentation#/tracking/trackingPublic)

**REST:** `PUT /api/v1/webhook-settings` — [REST-handboek](/api/documentation#/paths/v1-webhook-settings/put)

**GraphQL:** `webhookSettingsUpdate` ([GraphQL-handboek](/api/graphql/documentation#/webhooks/webhookSettingsUpdate)).

Verifieer **v2**: `HMAC_SHA256(timestamp + "." + raw_body, secret)` tegen `X-Webhook-Signature-V2`. Dedupliceer op `X-Webhook-Event-Id`. Antwoord **2xx binnen 3 seconden**.

```php
$raw = file_get_contents('php://input');
$ts  = $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE_V2'] ?? '';
$expected = hash_hmac('sha256', $ts . '.' . $raw, $sharedSecret);
if (!hash_equals($expected, $sig)) {
    http_response_code(401);
    exit;
}
```

**Verificatie:** betalen registreert een bedrag (of `402` / `422` met een duidelijke reden). Publieke tracking vindt de zending zodra er een nummer is.

## 9. Een testuitslag annuleren

**REST:** `POST /api/v1/customer/shipout-orders/{id}/cancel` — [REST-handboek](/api/documentation#/paths/v1-customer-shipout-orders-id--cancel/post)

```bash
curl -X POST https://YOUR_HOST/api/v1/customer/shipout-orders/8001/cancel \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'
```

**GraphQL:** `customerCancelShipout` ([GraphQL-handboek](/api/graphql/documentation#/customer/customerCancelShipout))

Dit geeft de opslagpakket-vergrendeling vrij. Opslag zelf wordt geannuleerd met `POST /api/v1/customer/storage-orders/{id}/cancel` zolang dat nog mag.

**Verificatie:** `422` betekent dat deze status niet kan worden geannuleerd. Na een geslaagde uitslagannulering toont stap 6 de pakketten opnieuw.

## Testlijst

- [ ] Opslagconfiguratie geeft een magazijn-`id` terug.
- [ ] Opslagtarief geeft een prijs terug.
- [ ] Opslag aanmaken geeft `data.id` terug.
- [ ] Opslag betalen slaagt, **of** u hebt bevestigd dat de portemonnee moet worden opgewaardeerd.
- [ ] De lijst met beschikbare artikelen toont ontvangen pakketten (`storage_package_ids`).
- [ ] Uitslagschatting geeft een prijs of `has_items_needing_quote`.
- [ ] Uitslag aanmaken geeft een `id` en vergrendelt die pakketten.
- [ ] Uitslag betalen slaagt (of `402` / `422` is begrepen).
- [ ] Publieke tracking vindt de zending zodra er een trackingnummer is.
- [ ] Uitslag annuleren geeft de pakketten vrij, **of** deze status kan niet worden geannuleerd.
