> 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/ingestion-api/mfc-inventory.md).

# MFC Inventory — MFC 入庫（棚付き）

大阪 MFC などの出荷元の自己ロケーションに、**棚番号付き**の在庫を入庫します。1 リクエストで、棚ごとの在庫と、商品ごとの販売可能在庫（全棚の合算）が**同時に**更新されます。商品は `barcode`、棚は `shelfCode`、ロケーションは出荷元ロケーション id で解決します。

> **Base URL** `https://api.lmile.io` · **認証** `x-ingest-secret` ヘッダー または operator JWT（`Authorization: Bearer`）（[詳細](/lmile-docs/getting-started/authentication.md)） · `shop` はリクエストボディに含めます（API キーで呼ぶ場合は省略できます）

> `/api/ingest/mfc-inventory` は機械間 secret（`x-ingest-secret`）と倉庫スタッフの operator JWT のどちらでも呼べます（系統はヘッダの有無で明示判別。[詳細](https://lmile.gitbook.io/lmile-docs/ingestion-api/pages/7cB70I8yeRnMpvcdLkNu#routing-write-の-2-系統認証)）。operator JWT 経由では、誰が入庫したかが棚在庫の増減ログに記録されます（機械間経由では記録されません）。

## エンドポイント

| Method | Path                        | 用途                                                    | テーブル                                      |
| ------ | --------------------------- | ----------------------------------------------------- | ----------------------------------------- |
| `POST` | `/api/ingest/mfc-inventory` | 出荷元ロケーションへ棚付き在庫を入庫（`mode=absolute` 絶対値 / `adjust` 差分） | `lm_shelf_inventory` / `inventory_levels` |

> この入庫在庫は受注ルーティングで**優先的に引き当て**られ、MFC 引き当て時は Star ラベルに**棚番号**が印字されます。

***

## `POST` /api/ingest/mfc-inventory

「この出荷元ロケーションの、この棚に、この商品が何個あるか」を送信します。`mode=absolute`（既定）は **qty をその棚の在庫数そのもの**として、`mode=adjust` は **qty を符号付きの増減量**として反映します。棚ごとの反映と同時に、その商品の販売可能在庫（受注の引き当てに使われる数）も自動で更新されます。

### リクエスト

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

`mode=absolute`（既定。棚の在庫数を絶対値で確定する棚卸し的な入庫）:

```json
{
  "shop": "demo-store.myshopify.com",
  "fulfillmentSourceLocationId": "343ff919-1234-5678-9abc-def012345678",
  "mode": "absolute",
  "lines": [
    { "barcode": "4912345678901", "shelfCode": "SA-A-13", "qty": 3 },
    { "barcode": "4912345678918", "shelfCode": "SA-B-02", "qty": 0 }
  ]
}
```

`mode=adjust`（現在庫に対する差分。`+` で入庫、`-` で減算）:

```json
{
  "shop": "demo-store.myshopify.com",
  "fulfillmentSourceLocationId": "343ff919-1234-5678-9abc-def012345678",
  "mode": "adjust",
  "lines": [
    { "barcode": "4912345678901", "shelfCode": "SA-A-13", "qty": 2 },
    { "barcode": "4912345678918", "shelfCode": "SA-B-02", "qty": -1 }
  ]
}
```

### ボディパラメータ

| パラメータ                         | 型             |  必須 | 説明                                                                                   |
| ----------------------------- | ------------- | :-: | ------------------------------------------------------------------------------------ |
| `shop`                        | string        |  ✓  | 店舗ドメイン（API キーで呼ぶ場合は省略できます）                                                           |
| `fulfillmentSourceLocationId` | string (uuid) |  ✓  | 入庫先の出荷元ロケーション id（shop に実在が必須）                                                        |
| `mode`                        | string        |     | `absolute`（既定）= qty を絶対値で set / `adjust` = qty を符号付き差分で加減算。**省略時は `absolute`**（後方互換） |
| `lines`                       | array         |  ✓  | 入庫行の配列                                                                               |

#### `lines[]` オブジェクト

| パラメータ       | 型       |  必須 | 説明                                                                                                                                                      |
| ----------- | ------- | :-: | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `barcode`   | string  |  ✓  | 商品 barcode（事前に [Products](/lmile-docs/ingestion-api/products.md) で登録）                                                                                   |
| `shelfCode` | string  |  ✓  | 棚コード（最大 32 文字）。形式 `^[A-Z0-9]+(-[A-Z0-9]+)+$` かつ英字を 1 文字以上含む。**大文字へ正規化してから**照合します                                                                        |
| `qty`       | integer |  ✓  | `absolute` ではその棚の在庫数（**絶対値**・`≥0`。0 で棚を空にする）。`adjust` では符号付き**差分**（`+` 入庫 / `-` 減算）。`adjust` で結果が負になる行はバッチ全体を `409 insufficient_shelf_qty` で reject します |

### レスポンス

```json
{
  "ok": true,
  "applied": 2,
  "mode": "absolute"
}
```

| フィールド     | 説明                                                      |
| --------- | ------------------------------------------------------- |
| `applied` | 処理した入庫行（`lines[]`）の件数                                   |
| `mode`    | 適用したモード（`absolute` / `adjust`。リクエストで省略した場合は `absolute`） |

### 挙動・冪等性

* **引き当て済みの数を下回る減らし方は断ります**: `absolute`（0 を含む）や `adjust` の `-` で、ある商品の棚の合計が注文に引き当て済みの数を上回る状態から下回る状態に変わると `409 shelf_qty_below_active` でバッチ全体を中断し、何も変えません。同じ商品を別の棚へ動かす `-1` / `+1` は合計が変わらないので通ります。すでに下回っている在庫を減らす操作は止めません。
* 棚在庫は**店舗 × 出荷元ロケーション × 棚 × 商品ごとに 1 行**で管理されます。
* **mode**:
  * `absolute`（既定）— 送った qty がその棚の**在庫数そのもの**になります（`0` で棚を空にできます）。棚卸しや定期的な再報告向け。
  * `adjust` — 送った qty のぶん**増減**します（`+3` で 3 個入庫 / `-2` で 2 個減）。
* **棚と販売可能在庫は同時に更新されます**: 棚ごとの反映と、その商品の全棚合算にもとづく販売可能在庫の更新が 1 つのトランザクションで行われます（部分適用はありません）。同じ商品を複数の棚に分けて送っても正しく合算されます。
* **販売可能在庫は「棚の合計」そのものではありません**: 既に受注へ引き当て済みでまだ出荷していない分を差し引いた「いま売れる数」に自動補正されます。**入庫直後の販売可能在庫が棚合計と一致しないのは正常です**（引き当て済み分が引かれています。負になる場合は 0）。
* **ピッキングで棚在庫は自動で減ります**: 倉庫スタッフが商品を棚から取った時点で該当棚の qty が 1 減り、ピック取り消しで 1 戻ります。その後 `mode=absolute` で棚を再報告すれば、棚在庫は実数に上書きされて整合します（増減の積算ではないため二重に減ることはありません）。
* **再送の安全性**: `absolute` は同じリクエストを何度再送しても安全です（冪等）。`adjust` は再送すると**二重に増減します** — タイムアウト等で結果が不明なときは、そのまま再送せず適用済みかを確認してください。
* **バッチ全体で成功か失敗か**: 未登録の `barcode` や、`adjust` で在庫が負になる行が 1 行でもあると、**リクエスト全体が反映されません**（部分適用や黙ったスキップはしません。→ エラー）。

***

## エラー

エンドポイント固有:

| Status | 条件                                                | レスポンス例                                                                                                                                                |
| :----: | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|  `422` | `fulfillmentSourceLocationId` が shop に存在しない       | `{ "ok": false, "error_kind": "location_unresolved", "error": "fulfillmentSourceLocationId not found for shop" }`                                     |
|  `422` | 棚コード形式不正                                          | `{ "ok": false, "error_kind": "invalid_shelf_code", "error": "shelfCode must match ...", "shelfCode": "SA_A_13", "index": 0 }`                        |
|  `422` | `barcode` が商品マスター未登録（その行でバッチ全体を中断）                | `{ "ok": false, "error_kind": "product_not_found", "error": "product not found for barcode ...", "barcode": "...", "index": 1 }`                      |
|  `409` | 減らした結果、ある商品の棚の合計が注文に引き当て済みの数を下回る（バッチ全体を中断・何も変えない） | `{ "ok": false, "error_kind": "shelf_qty_below_active", "barcode": "...", "shelfTotal": 3, "shelfTotalAfter": 1, "active": 2 }`                       |
|  `409` | `mode=adjust` で在庫が負になる行（バッチ全体を中断）                 | `{ "ok": false, "error_kind": "insufficient_shelf_qty", "shelfCode": "SA-A-13", "barcode": "...", "available": 3, "requestedDelta": -5, "index": 0 }` |

入庫はバッチ単位で atomic です。1 行でも上記に該当するとバッチ全体を reject し、部分適用しません（silent skip しません）。共通エラー（`401` / `400` / `403` / `405` / `500` / `503`）は [Error Codes](/lmile-docs/reference/error-codes.md) を参照。

## データモデル

* `sot.lm_shelf_inventory` — 棚在庫の qty 集約行。`UNIQUE (shop, location_id, shelf_code, barcode)` / `qty >= 0`。商品参照は barcode のみ。読み取り view: `public.lm_v_shelf_inventory`（内部 service / 管理用途。`agent_read.*` には露出しません）
* `sot.lm_shelf_inventory_logs` — 棚在庫の増減ログ（append-only）。`delta` / `qty_after` / `reason` / `operator`
* `sot.inventory_levels` — 現在庫マスター（`shop + product_id + location_id` で一意）。本 API は出荷元ロケーションの `available_qty` を全棚合算から引き当て済み未出荷数を差し引いた値（`max(0, 棚合計 − 引当済未出荷)`）に更新します。read は `agent_read.inventory_levels`（safe view）経由
* `sot.lm_fulfillment_units` — 1 単位 1 行のフルフィルメント明細。「引き当て済み未出荷数」はこのテーブルの `status`（`allocated` / `secured` / `in_transit_to_mfc` / `packing`）かつ未ピック（`picked_at IS NULL`）から数えます


---

# 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/ingestion-api/mfc-inventory.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.
