# 画面設計書 98-在庫一括更新画面

## 概要

本ドキュメントは、Medusa Admin Dashboardの在庫一括更新画面の設計仕様を定義するものです。

### 本画面の処理概要

在庫一括更新画面は、複数の在庫アイテムの在庫数量をロケーション単位で一括更新するためのフルモーダル形式の画面です。

**業務上の目的・背景**：在庫管理において、複数の在庫アイテムの在庫数を効率的に更新したい場合があります。特に棚卸し作業や入荷処理など、複数アイテムを一度に更新する必要がある場合に、本画面を使用することで作業効率を大幅に向上できます。DataGridを使用したスプレッドシート形式のインターフェースにより、直感的な編集操作が可能です。

**画面へのアクセス方法**：在庫一覧画面で複数の在庫アイテムを選択し、「Update Stock」ボタンをクリックすることでモーダルが表示されます。URL `/inventory/stock?inventory_item_ids={ids}` に直接アクセスすることも可能です。

**主要な操作・処理内容**：
1. 選択された在庫アイテムの在庫情報をDataGridで表示
2. 各ロケーションの在庫数量を編集
3. ロケーションへの在庫割り当て有無の切り替え（チェックボックス）
4. 一括保存処理の実行

**画面遷移**：
- 遷移元：在庫一覧画面（複数選択後）
- 遷移先：保存成功時は在庫一覧画面に戻る（モーダルクローズ）

**権限による表示制御**：認証済みユーザーであれば全機能を利用可能です。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 49 | 在庫レベル管理 | 主機能 | 在庫数量の一括更新 |
| 79 | 在庫ロケーションCRUD | API連携 | ロケーション一覧の取得 |

## 画面種別

編集（モーダル形式 / フルモーダル）

## URL/ルーティング

`/inventory/stock?inventory_item_ids={ids}`

## 入出力項目

### 入力項目

| 項目名 | 項目ID | 型 | 必須 | 説明 |
|--------|--------|-----|------|------|
| 在庫数 | inventory_items.{item_id}.locations.{location_id}.quantity | number | - | ロケーション毎の在庫数量 |
| 有効フラグ | inventory_items.{item_id}.locations.{location_id}.checked | boolean | - | ロケーションへの在庫割り当て有無 |

## 表示項目

| 項目名 | 項目ID | 型 | 説明 |
|--------|--------|-----|------|
| タイトル | item.title | string | 在庫アイテムのタイトル |
| SKU | item.sku | string | 在庫管理単位コード |
| ロケーション名 | location.name | string | ロケーションの名前（カラムヘッダー） |
| 在庫数/チェック状態 | - | composite | DataGridTogglableNumberCell形式 |

## イベント仕様

### 1-Saveボタン押下

入力内容でバリデーションを実行し、在庫レベルを一括更新します。

**処理フロー**:
1. 初期値との差分を計算
2. 新規割り当て（create）：初期状態でなく、数量が入力されたロケーション
3. 更新（update）：数量が変更されたロケーション
4. 削除（delete）：チェックが外されたロケーション
5. `useBatchInventoryItemsLocationLevels` で一括API呼び出し（force: true）
6. 成功時、トースト通知（inventory.stock.successToast）を表示しモーダルをクローズ
7. エラー時、エラーメッセージをトースト表示

### 2-Cancelボタン押下

入力内容を破棄してモーダルをクローズします。

### 3-在庫数入力

DataGrid内のセルをクリックして数値を入力します。トグル可能な数値セル（DataGridTogglableNumberCell）を使用しています。

### 4-トグル切り替え

各セルのチェックボックスをクリックして、ロケーションへの在庫割り当て有無を切り替えます。予約数または入荷中数が存在する場合は削除できないため、トグルが無効化されます。

## データベース更新仕様

### 操作別データベース影響一覧

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 初期表示 | inventory_item | SELECT | 在庫アイテム情報の取得 |
| 初期表示 | inventory_level | SELECT | 在庫レベル情報の取得 |
| 初期表示 | stock_location | SELECT | ロケーション一覧の取得 |
| Save押下 | inventory_level | INSERT | 新規在庫レベルの作成 |
| Save押下 | inventory_level | UPDATE | 在庫数量の更新 |
| Save押下 | inventory_level | DELETE | 在庫レベルの削除 |

### テーブル別更新項目詳細

#### inventory_level

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | inventory_item_id | 対象在庫アイテムのID | - |
| INSERT | location_id | 対象ロケーションID | - |
| INSERT | stocked_quantity | 入力値 | - |
| UPDATE | stocked_quantity | 入力値 | 変更があった場合のみ |
| DELETE | id | 対象レベルのID | チェック解除時 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|----------|
| inventory.stock.successToast | 成功 | 在庫が正常に更新されました | 更新成功時 |
| - | エラー | エラーメッセージ（API エラー内容） | API 呼び出しエラー時 |
| inventory.stock.disabledToggleTooltip | 警告 | 予約数または入荷中数が存在するため削除できません | トグル無効時ホバー |

## 例外処理

| 例外条件 | 対応 | 画面表示 |
|---------|------|----------|
| 在庫アイテムが取得できない | エラーをスロー | エラー画面を表示 |
| ロケーションが取得できない | エラーをスロー | エラー画面を表示 |
| APIエラー | トースト通知 | エラーメッセージを表示 |

## 備考

- RouteFocusModal形式で実装
- DataGridコンポーネントで編集可能なテーブルを表示
- DataGridTogglableNumberCellで数値入力とトグル切り替えを1つのセルで実現
- 編集中はEscキーでのモーダルクローズを無効化（setCloseOnEscape）
- 初期値を保持して差分計算を行うことで、変更のあったデータのみAPI送信
- force: true オプションにより、削除と作成を同時に実行可能

---

## コードリーディングガイド

本画面を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | schema.ts | `packages/admin/dashboard/src/routes/inventory/inventory-stock/schema.ts` | InventoryStockSchemaのネスト構造（inventory_items > locations）を確認 |

**読解のコツ**: LocationQuantitySchemaでid, quantity, checked, disabledToggleの4フィールドを管理している。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | inventory-stock.tsx | `packages/admin/dashboard/src/routes/inventory/inventory-stock/inventory-stock.tsx` | メインコンポーネント。URLパラメータからIDを取得、在庫アイテムとロケーションを取得 |
| 2-2 | inventory-stock-form.tsx | `packages/admin/dashboard/src/routes/inventory/inventory-stock/components/inventory-stock-form/inventory-stock-form.tsx` | フォームコンポーネント。差分計算とAPI呼び出し |

**主要処理フロー**:
1. **11-12行目（stock）**: URLパラメータ `inventory_item_ids` からカンマ区切りのIDを取得
2. **14-16行目（stock）**: `useInventoryItems` でアイテム取得
3. **18-26行目（stock）**: `useStockLocations` でロケーション一覧取得（limit: 9999）
4. **34-37行目（form）**: getDefaultValuesで初期値を構築
5. **45-101行目（form）**: onSubmitで差分計算を行い、create/update/deleteペイロードを構築
6. **92-100行目（form）**: useBatchInventoryItemsLocationLevelsで一括更新

#### Step 3: DataGrid列定義を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | use-inventory-stock-columns.tsx | `packages/admin/dashboard/src/routes/inventory/inventory-stock/hooks/use-inventory-stock-columns.tsx` | 動的なカラム生成。ロケーション数に応じてカラムを追加 |

**読解のコツ**: locationsのmapでロケーション毎のカラムを動的生成。DataGridTogglableNumberCellで編集可能にしている。

### プログラム呼び出し階層図

```
InventoryStock (inventory-stock.tsx)
    │
    ├─ useInventoryItems (inventory.tsx)
    │      └─ sdk.admin.inventoryItem.list()
    │
    ├─ useStockLocations (stock-locations.tsx)
    │      └─ sdk.admin.stockLocation.list()
    │
    └─ InventoryStockForm (inventory-stock-form.tsx)
           │
           ├─ useInventoryStockColumns (use-inventory-stock-columns.tsx)
           │      └─ DataGrid column definitions
           │
           ├─ DataGrid
           │      └─ DataGridTogglableNumberCell
           │
           └─ useBatchInventoryItemsLocationLevels (inventory.tsx)
                  └─ sdk.admin.inventoryItem.batchInventoryItemsLocationLevels()
```

### データフロー図

```
[入力]                          [処理]                          [出力]

URLパラメータ ──────────▶ inventory_item_ids取得
(inventory_item_ids)            │
                                ▼
                    useInventoryItems / useStockLocations
                                │
                                ▼
                    getDefaultValues ──▶ initialValues (useRef)
                                │
                                ▼
                          DataGrid表示
                                │
セル編集 ───────────────────────┤
                                ▼
                       onSubmit（差分計算）
                                │
    ┌───────────────────────────┼───────────────────────────┐
    ▼                           ▼                           ▼
create配列                 update配列                  delete配列
(新規割当)              (数量変更)               (チェック解除)
    │                           │                           │
    └───────────────────────────┼───────────────────────────┘
                                ▼
               useBatchInventoryItemsLocationLevels
                                │
                                ▼
                        handleSuccess ──▶ モーダルクローズ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| inventory-stock.tsx | `packages/admin/dashboard/src/routes/inventory/inventory-stock/inventory-stock.tsx` | ソース | メインコンポーネント |
| inventory-stock-form.tsx | `packages/admin/dashboard/src/routes/inventory/inventory-stock/components/inventory-stock-form/inventory-stock-form.tsx` | ソース | フォームコンポーネント |
| use-inventory-stock-columns.tsx | `packages/admin/dashboard/src/routes/inventory/inventory-stock/hooks/use-inventory-stock-columns.tsx` | ソース | カラム定義フック |
| schema.ts | `packages/admin/dashboard/src/routes/inventory/inventory-stock/schema.ts` | ソース | バリデーションスキーマ |
| inventory.tsx | `packages/admin/dashboard/src/hooks/api/inventory.tsx` | ソース | API フック |
| data-grid-toggleable-number-cell.tsx | `packages/admin/dashboard/src/components/data-grid/components/data-grid-toggleable-number-cell.tsx` | ソース | トグル可能数値セルコンポーネント |
