# 機能設計書 18-在庫移動

## 概要

本ドキュメントは、InvenTreeモバイルアプリにおける在庫移動機能の設計仕様を定義する。

### 本機能の処理概要

本機能は、在庫アイテムを別のロケーションに移動する機能である。在庫の物理的な移動に伴い、システム上のロケーション情報を更新する。

**業務上の目的・背景**：倉庫管理業務において、在庫の物理的な移動は頻繁に発生する。保管場所の変更、ピッキング作業、倉庫間移動など、様々な理由で在庫を移動する必要がある。モバイルアプリから現場で直接ロケーション変更を行うことで、物理的な移動とシステム更新のタイムラグを最小化し、在庫の正確な追跡が可能となる。バーコードスキャンとの連携により、移動先ロケーションの指定も効率化できる。

**機能の利用シーン**：
- 在庫を別の棚に移動する場合
- ピッキング後に在庫を出荷エリアに移動する場合
- 在庫を別の倉庫に移動する場合
- 整理のために在庫を再配置する場合

**主要な処理内容**：
1. 移動先ロケーションの選択
2. 移動数量の指定（シリアル化されていない場合は部分移動可能）
3. APIリクエストによる在庫のロケーション更新
4. オプションでステータス、梱包情報の更新

**関連システム・外部連携**：InvenTree APIの `stock/transfer/` エンドポイント（POST）を使用する。

**権限による制御**：`stock` ロールの `change` 権限が必要である。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 25 | 在庫アイテム詳細画面 | 主画面 | 転送ボタン押下時にロケーション移動フォーム表示 |

## 機能種別

データ更新（UPDATE）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| items[].pk | int | Yes | 在庫アイテムID | 正の整数 |
| items[].quantity | double | Yes | 移動数量 | 正の数値、現在数量以下 |
| items[].status | int | No | 移動後のステータス | ステータスコード |
| items[].packaging | String | No | 移動後の梱包情報 | - |
| location | int | Yes | 移動先ロケーションID | 正の整数、存在確認 |
| notes | String | No | 備考（移動理由等） | - |

### 入力データソース

- 在庫詳細画面に表示されている在庫アイテム
- ユーザーによるロケーション選択、数量入力
- バーコードスキャンによるロケーション指定

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 更新後location | int | 移動後のロケーションID |

### 出力先

- InvenTreeデータベース（StockItemテーブルのlocation更新）
- 在庫詳細画面の表示更新

## 処理フロー

### 処理シーケンス

```
1. 転送ボタンの表示
   └─ canEdit権限がある場合に表示
2. ユーザーが転送アクションを選択
3. 移動ダイアログの表示
   └─ location, quantity, status, packaging, notesフィールドを表示
4. ユーザーによる入力
   └─ 移動先ロケーション、数量（オプション）を入力
5. APIリクエストの実行
   └─ POST stock/transfer/ エンドポイント
6. レスポンスの処理
   ├─ 成功：画面をリフレッシュして更新後のロケーションを表示
   └─ 失敗：エラーメッセージを表示
```

### フローチャート

```mermaid
flowchart TD
    A[在庫詳細画面] --> B{canEdit?}
    B -->|No| C[転送ボタン非表示]
    B -->|Yes| D[転送ボタン表示]
    D --> E[ユーザーが転送を選択]
    E --> F[_transferStockDialog]
    F --> G[移動ダイアログ表示]
    G --> H[ユーザー入力]
    H --> I{入力検証}
    I -->|不正| J[エラー表示]
    J --> H
    I -->|正常| K[POST stock/transfer/]
    K --> L{成功?}
    L -->|No| M[エラー表示]
    L -->|Yes| N[画面リフレッシュ]
    N --> O[更新完了メッセージ]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-18-01 | 移動先必須 | 移動先ロケーションは必須 | 常時 |
| BR-18-02 | シリアル数量固定 | シリアル化されたアイテムは数量フィールドを非表示（全量移動） | isSerialized() == true |
| BR-18-03 | 部分移動 | シリアル化されていない場合は一部の数量のみ移動可能 | isSerialized() == false |
| BR-18-04 | ステータス/梱包更新 | APIバージョン133以降でステータス、梱包の更新が可能 | supportsStockAdjustExtraFields == true |
| BR-18-05 | 履歴記録 | 移動操作は履歴として記録される | 常時 |

### 計算ロジック

- 部分移動の場合、元の在庫アイテムは数量が減少し、移動分は新しいレコードとして作成される可能性がある

## データベース操作仕様

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 移動 | StockItem | UPDATE | 在庫のロケーションを更新 |
| 履歴追加 | StockTracking | INSERT | 移動操作履歴を記録 |

### テーブル別操作詳細

#### StockItem（InvenTree API経由）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | location | 移動先ロケーションID | 必須 |
| UPDATE | status | 指定されたステータス | 任意 |
| UPDATE | packaging | 指定された梱包情報 | 任意 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 400 | バリデーションエラー | 不正なロケーションID、数量超過 | エラーメッセージを表示 |
| 401 | 認証エラー | トークン無効または期限切れ | 再ログインを促す |
| 403 | 権限エラー | change権限がない | 権限確認メッセージを表示 |
| 404 | Not Found | ロケーションが存在しない | エラーメッセージを表示 |
| 500 | サーバーエラー | サーバー内部エラー | リトライまたはエラー表示 |

### リトライ仕様

サーバーエラー時は手動でダイアログを再表示して再送信可能。

## トランザクション仕様

単一レコードの更新のため、明示的なトランザクション管理は不要。

## パフォーマンス要件

- ダイアログ表示は即座に行う
- API応答は通常数秒以内

## セキュリティ考慮事項

- API認証トークンによるアクセス制御
- `stock` ロールの `change` 権限チェック
- HTTPS通信によるデータ保護

## 備考

- バーコードスキャンでロケーションを指定する別の方法もあり（StockItemScanIntoLocationHandler）
- 部分移動の場合、元のアイテムと移動後のアイテムで在庫が分割される可能性がある
- 現在のロケーションIDがデフォルト値として設定される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | stock.dart | `lib/inventree/stock.dart` | transferFields()とtransferStockUrl()を理解する |

**読解のコツ**:
- **156-178行目**: transferFields()で移動時に使用するフィールドを定義
- **158行目**: pkをitemsの入れ子構造で設定
- **159行目**: quantityを現在値で初期化
- **160行目**: locationを現在値で初期化
- **161-162行目**: status, packagingのオプションフィールド
- **166-168行目**: シリアル化時はquantityフィールドを非表示
- **172-175行目**: APIバージョンによるフィールド制御
- **181行目**: transferStockUrl() = "stock/transfer/"

#### Step 2: ダイアログ実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | stock_detail.dart | `lib/widget/stock/stock_detail.dart` | _transferStockDialog()の実装 |

**主要処理フロー**:
- **461-476行目**: _transferStockDialog()の実装
- **462行目**: transferFields()でフィールド定義を取得
- **464-474行目**: launchApiForm()で移動ダイアログを表示

#### Step 3: バーコードスキャンによる移動を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | stock_detail.dart | `lib/widget/stock/stock_detail.dart` | barcodeButtons()でのスキャン移動 |
| 3-2 | stock.dart | `lib/barcode/stock.dart` | StockItemScanIntoLocationHandler |

**主要処理フロー（stock_detail.dart）**:
- **163-178行目**: barcodeButtons()でスキャン移動ボタンを定義
- **165-178行目**: StockItemScanIntoLocationHandlerを使用

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

```
StockDetailWidget.actionButtons()
    │
    └─ _transferStockDialog()
           │
           ├─ widget.item.transferFields()
           │      └─ フィールド定義取得（pk, quantity, location等）
           │
           └─ launchApiForm()
                  │
                  └─ POST stock/transfer/
                         │
                         └─ onSuccess: refresh()

StockDetailWidget.barcodeButtons()
    │
    └─ scanBarcode()
           └─ StockItemScanIntoLocationHandler
                  └─ バーコードからロケーション特定後に移動
```

### データフロー図

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

pk + quantity ─────▶ launchApiForm() ───▶ location更新
+ location              │                     │
                        ▼                     ▼
              POST stock/transfer/ ───▶ StockItemレコード
                        │                     │
                        └───────────▶ refresh()
                                          │
                                          ▼
                                   画面表示更新
                                  （新ロケーション表示）
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| stock_detail.dart | `lib/widget/stock/stock_detail.dart` | ソース | 移動ダイアログの実装 |
| stock.dart | `lib/inventree/stock.dart` | ソース | transferFields()、transferStockUrl()定義 |
| stock.dart | `lib/barcode/stock.dart` | ソース | StockItemScanIntoLocationHandler |
| api_form.dart | `lib/api_form.dart` | ソース | launchApiForm()関数 |
| api.dart | `lib/api.dart` | ソース | InvenTree API通信クラス、supportsStockAdjustExtraFields |
| barcode.dart | `lib/barcode/barcode.dart` | ソース | scanBarcode()関数 |
