> For the complete documentation index, see [llms.txt](https://lmile.gitbook.io/lmile-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lmile.gitbook.io/lmile-docs/fulfillment-api/picking-list.md).

# Picking List — ピッキングリスト

ルーティング済みの注文（[Routing](/lmile-docs/routing-api/orders.md) で出荷指示まで生成された注文）から、**在庫ロケーション単位の「何を・いくつピックするか」** を商品名付きで生成します。生成元は永続化された routing データ（`sot.lm_routing_orders` + `sot.lm_routing_decisions`）で、`format=json` なら構造化データ、`format=pdf` なら**出荷元ロケーション単位の作業指示書 PDF**（スキャン用 CODE128 バーコード・商品画像・1 点 1 行のピックチェック）を Storage に保存します。

> ピックの素材は **applied な unit 単位 decision**（`selected_location_id` = 引き当て先在庫ロケーション）です。引き当て不能だった unit は `unpickable[]`（ピック対象外）として明示します。配送ラベル発行・物理印刷はピッキングリストとは別の出荷実行ステップです（本エンドポイントには含みません）。

> **Base URL** `https://api.lmile.io` · **認証** `x-ingest-secret` ヘッダー（[詳細](/lmile-docs/getting-started/authentication.md)） · `shop` はリクエストボディに含めます（省略するとキーのアカウント）

## エンドポイント

| Method | Path                              | 用途                                                     |
| ------ | --------------------------------- | ------------------------------------------------------ |
| `POST` | `/api/routing/picking-list`       | ルーティング済み**1 注文**からピッキングリストを生成（JSON / PDF）              |
| `POST` | `/api/routing/picking-list/batch` | **複数注文**を在庫拠点単位に統合した 1 つのピッキングリストを生成（JSON / PDF / CSV） |

> 入力フィールドは **camelCase**（`sourceSystem` / `externalOrderRef` / `routingOrderId` / `routingOrderIds` / `format`）です。

***

## `POST` /api/routing/picking-list

対象注文を `routingOrderId`、または `sourceSystem + externalOrderRef` で指定します（`routingOrderId` 優先）。`format` を省略すると JSON を返します。

### リクエスト

```http
POST https://api.lmile.io/api/routing/picking-list
x-ingest-secret: {INGEST_SECRET}
Content-Type: application/json
```

```json
{
  "shop": "demo-store.myshopify.com",
  "sourceSystem": "shopify",
  "externalOrderRef": "1001",
  "format": "json"
}
```

### ボディパラメータ

| パラメータ              | 型             |  必須 | 説明                                                                                 |
| ------------------ | ------------- | :-: | ---------------------------------------------------------------------------------- |
| `shop`             | string        |     | 店舗ドメイン（省略するとキーのアカウント）                                                              |
| `routingOrderId`   | string (uuid) |  △  | routing order の id。指定時はこれで解決（優先）                                                   |
| `sourceSystem`     | string        |  △  | カートの種別。`routingOrderId` 未指定時は `externalOrderRef` とセットで必須                           |
| `externalOrderRef` | string        |  △  | カート側の注文番号。`routingOrderId` 未指定時は `sourceSystem` とセットで必須                            |
| `format`           | string        |     | `json`（既定）または `pdf`                                                                |
| `locationId`       | string (uuid) |     | `format=pdf` 時のみ。指定した出荷元ロケーションの**単票 PDF を本体で返す**。省略時は全ロケーション分を生成し JSON で URL 一覧を返す |

> `routingOrderId`、または `sourceSystem` と `externalOrderRef` の両方、のいずれかが必須です。どちらも欠けると `400 invalid_input`。

### レスポンス（`format=json`）

```json
{
  "ok": true,
  "pickingList": {
    "shop": "demo-store.myshopify.com",
    "routingOrderId": "0f1e2d3c-4b5a-6789-0abc-def123456789",
    "sourceSystem": "shopify",
    "externalOrderRef": "1001",
    "idempotencyKey": "shopify:1001",
    "tag": "express",
    "status": "dispatched",
    "shipTo": { "name": "山田 太郎", "company": null, "country": "JP", "prefecture": "東京都", "city": "渋谷区", "postalCode": "150-0001" },
    "locations": [
      {
        "locationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "locationLabel": "東京DC",
        "items": [
          { "barcode": "1234567890123", "sku": "DEMO-CASE-001", "productName": "スマートフォンケース", "imageUrl": "https://cdn.shopify.com/.../case.jpg", "quantity": 2, "properties": null },
          { "barcode": "1234567890456", "sku": "DEMO-HOLDER-001", "productName": "レザーケーブルホルダー", "imageUrl": null, "quantity": 1, "properties": { "刻印": "T.Y" } }
        ],
        "totalQuantity": 3
      }
    ],
    "unpickable": [],
    "totals": { "locationCount": 1, "itemKinds": 2, "totalQuantity": 3, "unpickableQuantity": 0 },
    "generatedAt": "2026-06-03T06:00:00.000Z"
  }
}
```

| フィールド                      | 説明                                                                                                                                                      |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pickingList.locations[]`  | 在庫ロケーション単位のピック群。`locationId` / `locationLabel` と `items[]`（`barcode` / `sku` / `productName` / `imageUrl` / `quantity` / `properties`）+ `totalQuantity` |
| `pickingList.unpickable[]` | 引き当て不能（在庫不足等）でピック対象外の明細。`barcode` / `sku` / `productName` / `imageUrl` / `quantity` / `reason`（`no_stock_anywhere` 等）/ `properties`                     |
| `pickingList.totals`       | `locationCount` / `itemKinds`（location×barcode×properties 種類数）/ `totalQuantity` / `unpickableQuantity`                                                  |
| `pickingList.shipTo`       | 配送先サマリ（氏名・国・都道府県・市区町村・郵便番号。詳細住所/電話/email は含めない）                                                                                                         |
| `pickingList.generatedAt`  | 生成時刻（ISO 8601, UTC）                                                                                                                                     |

### レスポンス（`format=pdf`）

PDF は **出荷元ロケーション単位で 1 枚ずつ**（= 1 出荷先 = 1 作業指示書）生成し、`picking-lists` bucket（public read）に保存します。

**(a) `locationId` 指定時** — その単票を `application/pdf` 本体で返し、公開 URL を `X-Picking-List-Url` ヘッダーに載せます（保存と返却の両方）。

```http
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: inline; filename="picking_1001_a1b2c3d4_20260603060000.pdf"
X-Picking-List-Url: https://<project>.supabase.co/storage/v1/object/public/picking-lists/demo-store.myshopify.com/picking_1001_a1b2c3d4_20260603060000.pdf
```

**(b) `locationId` 省略時** — 全ロケーション分を生成・保存し、URL 一覧を JSON で返します。

```json
{
  "ok": true,
  "pickingList": { "...": "(format=json と同じ構造)" },
  "pdfs": [
    { "locationId": "a1b2c3d4-...", "locationLabel": "東京DC", "url": "https://<project>.supabase.co/storage/v1/object/public/picking-lists/demo-store.myshopify.com/picking_1001_a1b2c3d4_20260603060000.pdf", "storagePath": "demo-store.myshopify.com/picking_1001_a1b2c3d4_20260603060000.pdf", "units": 3 }
  ]
}
```

PDF の内容・体裁（mfc の運用フォーマットを参考）:

* A4 縦。日本語は IPAex ゴシックをサブセット埋め込み（テキストは選択・抽出可能）。
* 上部: **注文スキャンバーコード（CODE128, `externalOrderRef` をエンコード）** + 注文番号 + 出荷元ラベル + ソース/タグ/合計点数 +（再発行時）印刷回数。右に Ship To ブロック。
* 明細: **数量分を 1 点 1 行に展開**し、`商品画像`（`sot.products.primary_image_url`。取得不可は「No Image」枠）/ `SKU`・`商品名`・`barcode` / `ピッキングラベル貼付枠`（空欄）/ `Pick` チェックボックス。`properties` のある明細は `キー: 値` を「 / 」区切りの 1 行で表示（収まらない分は末尾省略）。
* 保存 path は `{shop}/picking_{externalOrderRef}_{locationId先頭8}_{YYYYMMDDHHMMSS}.pdf`。

### 挙動・冪等性

* **生成元は routing データ**: 出力は常に `sot.lm_routing_decisions`（`decision_status = applied`）の現在値から組み立てます。売越し・再引き当て（[Oversell / Reallocate](/lmile-docs/oversell-api/oversell-reallocate.md)）後に再生成すると最新の引き当て先が反映されます。
* **ロケーション単位の分割**: PDF は出荷元（在庫ロケーション）ごとに 1 枚。1 注文が複数拠点に分割された場合は拠点数ぶんの作業指示書になります。
* **明細単位 properties**: [Routing](/lmile-docs/routing-api/orders.md) の `lines[].properties`（刻印テキスト等）は `sot.lm_routing_decisions.line_properties` から各 item に透過します。**同一 `barcode` でも `properties` が異なれば別行**として集計・表示します（なしは `properties: null`）。
* **非冪等（再発行前提）**: 生成のたびに新しい PDF をタイムスタンプ付きファイル名で保存し、`sot.lm_picking_lists` に 1 行追記します（再印刷の履歴を残す append-only ログ）。冪等なのは入力となる routing データ側です。
* **印刷回数**: 当該注文の過去 PDF 生成回数 +1 を PDF 上に表示（2 回目以降）。
* **商品名・画像解決**: `barcode` → `sot.products`（`product_name` / `sku` / `primary_image_url`）。未登録 barcode は `null`。画像は jpg/png のみ埋め込み、取得不可（webp 等含む）は「No Image」枠。
* **ロケーション名**: `sot.locations.display_label`。

***

## `POST` /api/routing/picking-list/batch

出荷作業一覧で**複数の注文を選択**して、**在庫拠点単位に統合した 1 つのピッキングリスト**を生成します（LM-1053）。対象は `routingOrderIds[]`（routing order の id 配列）で指定します。選択した注文の applied decision を在庫ロケーション横断でまとめ、各明細に**注文番号**と**スキャン用 CODE128 バーコード**を付けます。`format=pdf` は統合 PDF を 1 つ、`format=csv` は共有用 CSV を、いずれも Storage に保存して**公開 URL を返します**（本体ではなく URL）。

### リクエスト

```http
POST https://api.lmile.io/api/routing/picking-list/batch
x-ingest-secret: {INGEST_SECRET}
Content-Type: application/json
```

```json
{
  "shop": "demo-store.myshopify.com",
  "routingOrderIds": [
    "0f1e2d3c-4b5a-6789-0abc-def123456789",
    "1a2b3c4d-5e6f-7890-abcd-ef1234567890"
  ],
  "format": "pdf"
}
```

### ボディパラメータ

| パラメータ              | 型                |  必須 | 説明                                                                                    |
| ------------------ | ---------------- | :-: | ------------------------------------------------------------------------------------- |
| `shop`             | string           |     | 店舗ドメイン（省略するとキーのアカウント）                                                                 |
| `routingOrderIds`  | string\[] (uuid) |  ✓  | 対象 routing order の id 配列（1〜500 件）                                                     |
| `format`           | string           |     | `pdf`（既定）/ `csv` / `json`                                                             |
| `locale`           | string           |     | 帳票の表示言語。`ja`（既定）/ `en`。見出しと拠点名に効きます（商品名は実データなので変わりません）                                |
| `labelShipmentIds` | string\[] (uuid) |     | 配送ラベルを同じ PDF の後ろに綴じる出荷の id（最大 100 件）。`format=pdf` のときだけ効きます。省略すると従来どおりピッキングリストだけを返します |

### レスポンス（`format=pdf` / `format=csv`）

統合 PDF / CSV を `picking-lists` bucket（public read）に保存し、公開 URL を JSON で返します。`missing`（見つからなかった id）・`unrouted`（applied decision が無い注文）は **silent skip せず**応答に含めます。

```json
{
  "ok": true,
  "format": "pdf",
  "url": "https://<project>.supabase.co/storage/v1/object/public/picking-lists/demo-store.myshopify.com/batch_20260624010000_0f1e2d3c_2.pdf",
  "storagePath": "demo-store.myshopify.com/batch_20260624010000_0f1e2d3c_2.pdf",
  "totals": { "orderCount": 2, "locationCount": 2, "itemKinds": 4, "totalQuantity": 6, "unpickableQuantity": 0 },
  "missing": [],
  "unrouted": []
}
```

| フィールド            | 説明                                                                                              |
| ---------------- | ----------------------------------------------------------------------------------------------- |
| `url`            | 生成物（PDF / CSV）の公開 URL。新規タブで開く・共有する想定                                                            |
| `storagePath`    | Storage 上の保存パス                                                                                  |
| `totals`         | `orderCount`（対象になった注文数）/ `locationCount` / `itemKinds` / `totalQuantity` / `unpickableQuantity` |
| `missing`        | 見つからなかった `routingOrderId`（shop 不一致 / 不在）                                                        |
| `unrouted`       | 見つかったが applied decision が無い注文（`routingOrderId` / `orderLabel`）                                  |
| `labelsIncluded` | PDF の後ろに綴じた配送ラベルの枚数（`labelShipmentIds` 未指定なら `0`）                                               |
| `labelsSkipped`  | 綴じられなかった出荷の id。ラベル未発行 / 破棄済み / 指定した注文に属さない出荷。**silent skip しません**                               |

### レスポンス（`format=json`）

```json
{
  "ok": true,
  "pickingList": {
    "shop": "demo-store.myshopify.com",
    "generatedAt": "2026-06-24T01:00:00.000Z",
    "orders": [ { "routingOrderId": "0f1e2d3c-...", "orderLabel": "BJP1009011", "sourceSystem": "shopify", "externalOrderRef": "1001" } ],
    "missing": [],
    "unrouted": [],
    "locations": [
      {
        "locationId": "a1b2c3d4-...",
        "locationLabel": "玉川髙島屋",
        "items": [
          { "routingOrderId": "0f1e2d3c-...", "orderLabel": "BJP1009011", "sourceSystem": "shopify", "externalOrderRef": "1001", "barcode": "4589903578268", "sku": "AA-1", "productName": "ヴィットーリア バッグ", "imageUrl": null, "quantity": 2, "properties": { "_Letters": "R.Y" } }
        ],
        "totalQuantity": 2
      }
    ],
    "unpickable": [],
    "totals": { "orderCount": 1, "locationCount": 1, "itemKinds": 1, "totalQuantity": 2, "unpickableQuantity": 0 }
  }
}
```

PDF の内容・体裁:

* A4 縦、日本語は IPAex ゴシックをサブセット埋め込み。
* 上部に `PICKING LIST (一括)` + 生成時刻 + 注文数 / 拠点数 / 合計点数。`missing` / `unrouted` / 引当不能があれば注意行を表示。
* **在庫拠点ごとにセクション**（`拠点: {ラベル}（計 N 点）`）→ 明細行: `注文番号`（+ 刻印）/ `商品画像` / `SKU`・`商品名` / **`商品バーコード`（CODE128, 数字併記）** / `数量` / `Pick` チェック。
* 保存 path は `{shop}/batch_{YYYYMMDDHHMMSS}_{先頭注文id8}_{注文数}.pdf|csv`。

CSV の列（1 行 = 1 明細・拠点順）: `No.` / `注文番号` / `拠点` / `SKU` / `barcode` / `商品名` / `数量` / `刻印`（`_Letters` の実値）/ `お届け予定日`（`YYYY-MM-DD`、幅があるときは `YYYY-MM-DD~YYYY-MM-DD`）/ `配達指定日` / `配達指定時間帯`。Excel 対応の BOM 付き。

お届け予定日は、購入者のお届け状況ページと同じ計算で注文ごとに出します。配達日の指定がある注文は指定日になります。荷物が 2 つ以上に分かれる注文は、その受注の荷物の予定日です。予定日を出せない注文は空欄です。

### 挙動・冪等性

* **生成元は routing データ**: 単一版と同じく `sot.lm_routing_decisions`（`applied`）の現在値から組み立てます。
* **在庫拠点で統合**: 選択した全注文の明細を `selected_location_id` 単位でまとめ、拠点内は `注文番号 → 商品名 → properties` で決定的にソート。同一注文×同一 `barcode`×同一 `properties` は数量に集計、**刻印違いは別行**。
* **部分結果を明示**: `missing`（不在 id）・`unrouted`（未ルーティング注文）・`unpickable[]`（引き当て不能明細）は応答・PDF に必ず出し、silent skip しません。
* **非冪等**: 生成のたびにタイムスタンプ付きで Storage 保存（同名は上書き）。単一版と異なり **`sot.lm_picking_lists` への追記は行いません**（複数注文にまたがるため。生成物は URL が成果物）。
* **配送ラベルを綴じる**: `labelShipmentIds` を送ると、その出荷の発行済みラベル PDF を **ピッキングリストの後ろに指定順で追加**した 1 つの PDF を返します（現場が 1 枚として開いて印刷できるように）。対象は `routingOrderIds` に属する出荷だけで、他注文のラベルは綴じません。ラベル未発行 / 破棄済み / 読み出せなかった出荷は `labelsSkipped` に載せます（枚数は `labelsIncluded`）。ラベルが 1 枚も綴じられなくても、ピッキングリスト単体の PDF として成功で返るので、**綴じられたかどうかは `labelsIncluded` で判定してください**。

## エラー

エンドポイント固有:

| Status | 条件                                                      | レスポンス例                                                                                                                                                           |
| :----: | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|  `400` | バリデーション失敗（注文キー欠落含む）                                     | `{ "ok": false, "error": "invalid_input", "details": [{ "path": "externalOrderRef", "message": "..." }] }`                                                       |
|  `404` | 対象 routing order が無い                                    | `{ "ok": false, "error_kind": "not_found", "error": "routing order not found for shop" }`                                                                        |
|  `422` | applied decision が無い（未ルーティング）                           | `{ "ok": false, "error_kind": "not_routed", "error": "routing order has no applied decisions (not routed yet)", "routingOrderId": "...", "status": "received" }` |
|  `422` | 指定 `locationId` にピック対象が無い                               | `{ "ok": false, "error_kind": "location_not_in_picking_list", "error": "specified location has no pickable units in this order" }`                               |
|  `404` | （batch）対象注文が 1 件も無い                                     | `{ "ok": false, "error_kind": "not_found", "error": "no routing orders found for shop", "missing": ["..."] }`                                                    |
|  `422` | （batch）選択注文にピック対象が 1 点も無い                               | `{ "ok": false, "error_kind": "not_routed", "error": "selected orders have no pickable units", "missing": [...], "unrouted": [...] }`                            |
|  `500` | （batch・CSV）お届け予定日を組めない注文がある（`delivery_estimate_failed`） | `{ "ok": false, "error_kind": "delivery_estimate_failed", "error": "failed to build delivery estimates" }`                                                       |
|  `500` | フォント読込失敗（`picking_list_font_unavailable`）               | `{ "ok": false, "error": "internal_server_error" }`                                                                                                              |

引き当て不能の明細はエラーではなく、`pickingList.unpickable[]` に `reason` 付きで返ります（正常処理を止めません）。共通エラー（`401` / `400` / `403` / `405` / `500` / `503`）は [Error Codes](/lmile-docs/reference/error-codes.md) を参照。

## データモデル

* `sot.lm_picking_lists` — ピッキングリスト生成ログ（append-only。`routing_order_id` / `format` / `pdf_url` / `storage_path` / `payload` + 集計列 `location_count` / `item_kinds` / `total_quantity` / `unpickable_count` / `generated_at`）。**単一注文版のみ追記**（batch は複数注文にまたがるため追記しない）。
* 入力（read）: `sot.lm_routing_orders`（注文ヘッダ・`order_name`）/ `sot.lm_routing_decisions`（applied unit）/ `sot.products`（商品名・SKU・`primary_image_url`）/ `sot.locations`（ラベル）
* PDF / CSV 保存先: Storage bucket `picking-lists`（public read）
* 公開 view（read）: `public.lm_v_picking_lists`

> ピッキングリストは routing 結果の **派生表現** です。引き当てロジックそのものは [Routing](/lmile-docs/routing-api/orders.md) を参照してください。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://lmile.gitbook.io/lmile-docs/fulfillment-api/picking-list.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
