# Skladovanie a výdaj

Naskladnite tovar na skladovanie a potom naskladnené položky vydajte: konfigurácia → cenová ponuka → vytvorenie skladovania → platba → zoznam položiek stále na sklade → odhad výdaja → vytvorenie výdaja → platba → sledovanie → zrušenie.

Táto príručka dokumentuje iba nasledujúce operácie. Vykonajte ich v poradí. Overenie používa účet **zákazníka**.

Nahraďte `YOUR_HOST` a `ACCESS_TOKEN` hodnotami z vášho prostredia.

## 1. Prihlásenie (zákazník)

```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 používa rovnakú hlavičku na `POST /api/graphql`.

**Overenie:** prihlásenie vráti `access_token`. Neskoršie volanie bez neho vráti `401`.

## 2. Konfigurácia skladovania

**REST:** `GET /api/v1/customer/storage-orders/config` — [Príručka REST](/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` ([Príručka GraphQL](/api/graphql/documentation#/customer/customerStorageOrderConfig))

**Overenie:** zaznamenali ste `id` skladu a, ak katalóg nie je prázdny, `id` balenia.

## 3. Cenová ponuka skladovania

**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
    }]
  }'
```

**Overenie:** `success` alebo `result` je true a máte cenu. Chýbajúce `warehouse_id` / dátumy je `400`.

## 4. Vytvorenie skladovacej objednávky

**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
    }]
  }'
```

**Overenie:** odpoveď má `data.id`. Uložte toto id skladovacej objednávky.

## 5. Platba skladovania

**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 '{}'
```

Voliteľne: najprv `GET /api/v1/customer/storage-orders/{id}/payment-info`.

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

**GraphQL:** `customerStorageOrderShow` ([Príručka GraphQL](/api/graphql/documentation#/customer/customerStorageOrderShow))

**Overenie:** skladovacia objednávka je zaplatená / potvrdená. `402` znamená dobiť peňaženku a potom skúsiť znova.

Výdaj nižšie funguje až potom, čo sú balíky v sklade **prijaté**. Na test počkajte, kým ich personál (alebo testovací príjem) označí ako prijaté, a potom pokračujte.

## 6. Zoznam položiek stále na sklade

**REST:** `GET /api/v1/customer/shipout-orders/available-items` — [Príručka REST](/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` ([Príručka GraphQL](/api/graphql/documentation#/customer/customerShipoutAvailableItems))

**Overenie:** zaznamenali ste jedno alebo viac `storage_package_ids` (príklad `5001`). Prázdny zoznam znamená, že ešte nič nie je prijaté — výdaj nevytvárajte. `403` znamená, že výdaj je pre tohto zákazníka vypnutý.

## 7. Odhad a vytvorenie výdaja

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

Zaznamenajte `service_code`.

**REST:** `POST /api/v1/customer/shipout-orders/services/{serviceCode}/estimate` — [Príručka REST](/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` — [Príručka REST](/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` ([Príručka GraphQL](/api/graphql/documentation#/customer/customerCreateShipoutOrder))

**Overenie:** odpoveď má `id` výdaja. Vybrané skladovacie balíky sú uzamknuté k tejto požiadavke.

## 8. Platba výdaja, sledovanie, udalosti

**REST:** `POST /api/v1/customer/shipout-orders/{id}/pay` — [Príručka REST](/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` ([Príručka GraphQL](/api/graphql/documentation#/customer/customerPayShipout))

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

Keď sledovacie číslo existuje:

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

[Príručka REST](/api/documentation#/operations/getPublicTracking) · [Príručka GraphQL](/api/graphql/documentation#/tracking/trackingPublic)

**REST:** `PUT /api/v1/webhook-settings` — [Príručka REST](/api/documentation#/paths/v1-webhook-settings/put)

**GraphQL:** `webhookSettingsUpdate` ([Príručka GraphQL](/api/graphql/documentation#/webhooks/webhookSettingsUpdate)).

Overujte **v2**: `HMAC_SHA256(timestamp + "." + raw_body, secret)` proti `X-Webhook-Signature-V2`. Deduplikujte podľa `X-Webhook-Event-Id`. Odpovedzte **2xx do 3 sekúnd**.

```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;
}
```

**Overenie:** platba zaznamená sumu (alebo `402` / `422` s jasným dôvodom). Verejné sledovanie zásielku nájde, hneď ako číslo existuje.

## 9. Zrušenie testovacieho výdaja

**REST:** `POST /api/v1/customer/shipout-orders/{id}/cancel` — [Príručka REST](/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` ([Príručka GraphQL](/api/graphql/documentation#/customer/customerCancelShipout))

Tým sa uvoľní zámok skladovacích balíkov. Samotné skladovanie sa ruší pomocou `POST /api/v1/customer/storage-orders/{id}/cancel`, kým je to ešte povolené.

**Overenie:** `422` znamená, že tento stav nemožno zrušiť. Po úspešnom zrušení výdaja krok 6 znova vypíše balíky.

## Zoznam testov

- [ ] Konfigurácia skladovania vráti `id` skladu.
- [ ] Cenová ponuka skladovania vráti cenu.
- [ ] Vytvorenie skladovania vráti `data.id`.
- [ ] Platba skladovania uspeje, **alebo** ste potvrdili, že peňaženku treba dobiť.
- [ ] Zoznam dostupných položiek ukáže prijaté balíky (`storage_package_ids`).
- [ ] Odhad výdaja vráti cenu alebo `has_items_needing_quote`.
- [ ] Vytvorenie výdaja vráti `id` a uzamkne tieto balíky.
- [ ] Platba výdaja uspeje (alebo `402` / `422` je zrozumiteľné).
- [ ] Verejné sledovanie zásielku nájde, hneď ako sledovacie číslo existuje.
- [ ] Zrušenie výdaja uvoľní balíky, **alebo** tento stav nemožno zrušiť.
