# Appendix — `/v1/get_activity_time_slot_supplier_inventory_booking`

Append to `docs/TIME_SLOT_SUPPLIER_INVENTORY_API.md`.

The slot-keyed twin of `/v1/get_activity_supplier_inventory_booking`. Reads
`activity_time_slot_supplier_inventory`, filters by `time_from` / `time_to` instead of
`category_id`, and returns the same flat, buy-side shape.

```bash
# one specific slot — the booking case
curl -s -X POST "$BASE_URL/v1/get_activity_time_slot_supplier_inventory_booking" \
  -H "Content-Type: application/json" \
  -d '{
        "activity_id": 412,
        "travel_date": "2026-10-05",
        "time_from": "10:00",
        "time_to": "11:00"
      }'

# every slot on the date — the slot-picker case
curl -s -X POST "$BASE_URL/v1/get_activity_time_slot_supplier_inventory_booking" \
  -H "Content-Type: application/json" \
  -d '{ "activity_id": 412, "travel_date": "2026-10-05" }'

# one supplier, slot start only
curl -s -X POST "$BASE_URL/v1/get_activity_time_slot_supplier_inventory_booking" \
  -H "Content-Type: application/json" \
  -d '{
        "activity_id": 412,
        "travel_date": "2026-10-05",
        "time_from": "14:00",
        "supplier_id": 9
      }'
```

## Request

| Field | Req. | Notes |
|---|---|---|
| `activity_id` | yes | |
| `travel_date` | yes | `YYYY-MM-DD`. Validated — a malformed date is an error, not an empty list |
| `time_from` | no | **Exact** `from_time` match. Accepts `HH:mm` or `HH:mm:ss` |
| `time_to` | no | **Exact** `to_time` match |
| `supplier_id` | no | |

`time_from` and `time_to` are independent — send one, both, or neither. Neither returns every
slot on the date, the way omitting `category_id` returns every category today. `status = 1` is
always enforced; inactive and soft-deleted rates never appear.

## Response

```json
{
  "replyCode": "success",
  "replyMsg": "Time slot supplier inventory fetched successfully.",
  "data": [
    {
      "id": 1042,
      "activity_id": 412,
      "activity_date": "2026-10-05",
      "from_time": "10:00:00",
      "to_time": "11:00:00",
      "group_key": "activity_412_2026-10-01_2026-12-31_10:00:00_11:00:00",
      "supplier_id": 9,
      "supplier_name": "Desert Tours LLC",
      "supplier_company_name": "Desert Tours LLC",
      "supplier_email": "ops@deserttours.example",
      "supplier_mobile": "+971500000000",
      "currency": "AED",
      "adult_purchase_price": "100.00",
      "child_purchase_price": "60.00",
      "infant_purchase_price": "0.00",
      "available_quantity": 20,
      "total_quantity": 20,
      "priority": 1,
      "cancellation_policy": {
        "id": 5,
        "name": "48h free cancellation",
        "items": [
          { "id": 90, "from_day": 7, "to_day": 3, "deduction": 25 },
          { "id": 91, "from_day": 2, "to_day": 0, "deduction": 100 }
        ]
      }
    }
  ]
}
```

Field-for-field identical to `/v1/get_activity_supplier_inventory_booking` except that
`category_id` is replaced by `from_time` and `to_time`. No match is a `success` with
`data: []`; only a SQL or connection failure returns HTTP 500.

`DECIMAL` prices come back as **strings** and each row carries its own `currency` — a supplier
contract is denominated per supplier, so different rows in one response can be in different
currencies. No markup and no currency conversion is applied: these are purchase prices, and
converting or marking them up would produce a number that is neither the cost nor the selling
price.

## Ordering

```
from_time ASC, to_time ASC, priority ASC, adult_purchase_price ASC, from_day DESC
```

`from_time` leading is the one deviation from `/v1`. With no time filter the result spans
several slots, and the `/v1` ordering would interleave the 10:00 and 14:00 suppliers by
priority. Within a single slot the ordering is unchanged — priority, then purchase price — so
`data[0]` is still the cheapest highest-priority supplier for the earliest slot. Served by
`idx_ts_sup_inv_activity_date_time`.

## Errors

All returned with HTTP 200 and `replyCode: "error"`.

| `replyMsg` |
|---|
| `activity_id is required.` |
| `travel_date is required.` |
| `Invalid travel_date, use YYYY-MM-DD.` |
| `Invalid time_from, use HH:mm or HH:mm:ss.` |
| `Invalid time_to, use HH:mm or HH:mm:ss.` |

The three validations are the second deviation from `/v1`, which passes these values straight
into the query. They are bound parameters either way, so this was never an injection — but
MySQL casts a malformed date or time to NULL, which matches no row and looks exactly like "no
inventory for that slot". `ARCHITECTURE_NOTES` §18.15 records that as a finding against `/v1`,
so it is not reproduced here.

## No authentication

This route ships the buy side — purchase prices, supplier name, company, email, mobile, and
our sourcing priority — and, like `/v1/get_activity_supplier_inventory_booking`, it requires
no token. Anyone who can reach `/v1` can read it. That is inherited for symmetry, not
endorsed; §18.15 already records the same exposure for the category endpoint. Gate both
together, or use an `/xApi/*` port where a Bearer token is mandatory.
