# 機能設計書 46-在庫スキャン操作

## 概要

本ドキュメントは、InvenTreeアプリにおけるバーコードスキャンを使用した在庫移動・調整機能の設計を記述する。在庫アイテムやロケーションのバーコードをスキャンして、在庫の移動操作を効率的に行う機能である。

### 本機能の処理概要

在庫スキャン操作機能は、バーコードスキャンを活用して在庫アイテムの移動操作を行う機能である。在庫アイテムのバーコードをスキャンして特定のロケーションに移動したり、ロケーションのバーコードをスキャンして在庫アイテムをそのロケーションに移動したりすることができる。

**業務上の目的・背景**：倉庫での在庫移動作業は頻繁に発生する業務であり、効率化が求められる。バーコードスキャンによる在庫移動は、手入力による誤りを削減し、作業時間を大幅に短縮する。特に、ピッキング作業や棚卸し作業において、バーコードを活用した迅速な在庫操作は業務効率に大きく貢献する。

**機能の利用シーン**：
- 在庫アイテムを別のロケーションに移動する際、移動先ロケーションをスキャン
- 特定のロケーションに在庫アイテムを集約する際、アイテムを連続スキャン
- 入荷した在庫を棚入れする際、ロケーションバーコードをスキャンして移動
- 出荷準備エリアに在庫を移動する際の迅速な操作

**主要な処理内容**：
1. 在庫アイテム詳細画面からロケーションスキャンを起動（アイテム→ロケーション）
2. ロケーション画面から在庫アイテムスキャンを起動（ロケーション→アイテム追加）
3. スキャン確認設定に応じた確認フォーム表示/自動実行
4. transferStock APIによる在庫移動実行
5. 成功/失敗の通知

**関連システム・外部連携**：
- InvenTree REST API（stock/transfer/エンドポイント）

**権限による制御**：在庫アイテムの編集権限（stock.change）が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 25 | 在庫アイテム詳細画面 | 遷移元画面 | バーコードスキャンでロケーションへ移動 |
| 23 | 在庫ロケーション画面 | 遷移元画面 | バーコードスキャンで在庫アイテムをロケーションに追加 |
| 5 | バーコードスキャン画面 | 主画面 | ロケーション/アイテムのスキャン |

## 機能種別

データ更新 / バーコード連携 / 在庫操作

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| item | InvenTreeStockItem | 条件付き | 移動対象の在庫アイテム | pk > 0 |
| location | InvenTreeStockLocation | 条件付き | 移動先のロケーション | pk > 0 |
| barcode | String | Yes | スキャンされたバーコード | 空でないこと |

### 入力データソース

- バーコードスキャン（カメラ/ウェッジ）
- 呼び出し元画面からのアイテム/ロケーション情報

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 移動結果 | bool | 在庫移動の成功/失敗 |
| 通知メッセージ | String | 成功時: stockItemUpdated、失敗時: barcodeScanIntoLocationFailure |

### 出力先

- スナックバー（結果通知）
- 音声フィードバック（成功/失敗音）

## 処理フロー

### 処理シーケンス

```
1. 在庫スキャン操作の起動パターン

   パターンA: アイテム → ロケーション
   ├─ StockItemScanIntoLocationHandler(item)でスキャン開始
   └─ ロケーションバーコードをスキャン

   パターンB: ロケーション → アイテム
   ├─ StockLocationScanInItemsHandler(location)でスキャン開始
   └─ 在庫アイテムバーコードを連続スキャン

2. バーコード照合（onBarcodeMatched）
   ├─ stocklocation/stockitemキーを確認
   └─ pkを抽出

3. スキャン確認設定の確認（INV_STOCK_CONFIRM_SCAN）
   ├─ true: 確認フォーム表示
   │   └─ launchApiForm(transferStock)
   └─ false: 自動実行
       └─ item.transferStock(locationId)

4. 移動処理実行
   ├─ 成功時: barcodeSuccess、スナックバー表示
   └─ 失敗時: barcodeFailureTone、エラースナックバー

5. 結果に応じた画面制御
   ├─ パターンA: スキャン画面を閉じる（return true）
   └─ パターンB: スキャン継続（return false）
```

### フローチャート

```mermaid
flowchart TD
    A[在庫操作開始] --> B{起動パターン}
    B -->|アイテム→ロケーション| C[StockItemScanIntoLocationHandler]
    B -->|ロケーション→アイテム| D[StockLocationScanInItemsHandler]
    C --> E[ロケーションスキャン]
    D --> F[アイテムスキャン]
    E --> G[onBarcodeMatched]
    F --> H[onBarcodeMatched]
    G --> I{stocklocation検出?}
    H --> J{stockitem検出?}
    I -->|No| K[エラー: invalidStockLocation]
    I -->|Yes| L[onLocationScanned]
    J -->|No| M[エラー: invalidStockItem]
    J -->|Yes| N[onItemScanned]
    L --> O{確認設定?}
    N --> P{同一ロケーション?}
    P -->|Yes| Q[itemInLocation通知]
    P -->|No| R{確認設定?}
    O -->|確認あり| S[確認フォーム表示]
    O -->|確認なし| T[transferStock直接実行]
    R -->|確認あり| S
    R -->|確認なし| T
    S --> U[ユーザー確認]
    U --> V[API実行]
    T --> V
    V --> W{成功?}
    W -->|Yes| X[成功通知]
    W -->|No| Y[エラー通知]
    X --> Z{パターン}
    Y --> Z
    Z -->|A| AA[スキャン画面終了]
    Z -->|B| AB[スキャン継続]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-46-01 | スキャン確認設定 | INV_STOCK_CONFIRM_SCAN設定に応じて確認フォーム表示/自動実行 | 移動操作時 |
| BR-46-02 | 同一ロケーション検出 | 移動先が現在のロケーションと同じ場合は警告表示 | アイテム移動時 |
| BR-46-03 | 連続スキャンモード | ロケーション→アイテムパターンはスキャン継続 | パターンB時 |
| BR-46-04 | 単発スキャンモード | アイテム→ロケーションパターンはスキャン終了 | パターンA時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| バーコード照合 | StockLocation/StockItem | SELECT | バーコードからアイテム/ロケーション特定 |
| 在庫移動 | StockItem | UPDATE | location_idの更新 |
| 履歴記録 | StockItemTracking | INSERT | 移動履歴の記録 |

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

#### StockItem

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | location | 新しいロケーションID | transferStock API経由 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| E-46-01 | 無効ロケーション | スキャンがロケーションにマッチしない | invalidStockLocationスナックバー |
| E-46-02 | 無効アイテム | スキャンが在庫アイテムにマッチしない | invalidStockItemスナックバー |
| E-46-03 | 移動失敗 | transferStock APIがfalseを返却 | barcodeScanIntoLocationFailureスナックバー |
| E-46-04 | 親ロケーション設定失敗 | 無効な親ロケーション指定 | invalidStockLocationスナックバー |

### リトライ仕様

自動リトライは行わない。ユーザーは再スキャンで手動リトライ可能。

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

REST APIベースのため、各移動操作は独立したHTTPリクエストとして処理される。

## パフォーマンス要件

- バーコード照合: 1秒以内
- 在庫移動: 2秒以内

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

- 認証トークンを使用したAPIアクセス
- stock.change権限による在庫操作の制御

## 備考

- 3つのハンドラクラスが存在:
  - StockItemScanIntoLocationHandler: アイテム→ロケーション
  - StockLocationScanInItemsHandler: ロケーション→アイテム
  - ScanParentLocationHandler: ロケーション→親ロケーション設定

---

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

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

### 推奨読解順序

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

在庫スキャン操作で使用されるハンドラクラスを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | stock.dart | `lib/barcode/stock.dart` | BarcodeScanStockLocationHandler（25-60行目） |
| 1-2 | stock.dart | `lib/barcode/stock.dart` | BarcodeScanStockItemHandler（68-102行目） |

**読解のコツ**: 2つの基底ハンドラクラスがあり、それぞれロケーションスキャン用とアイテムスキャン用。onLocationScanned/onItemScannedをオーバーライドして具体的な処理を実装する。

#### Step 2: アイテム→ロケーション移動を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | stock.dart | `lib/barcode/stock.dart` | StockItemScanIntoLocationHandler（111-157行目） |

**主要処理フロー**:
1. **112行目**: コンストラクタでInvenTreeStockItemを受け取る
2. **117-121行目**: INV_STOCK_CONFIRM_SCAN設定を取得
3. **125-141行目**: confirm=trueの場合、launchApiFormで確認フォーム表示
4. **145行目**: confirm=falseの場合、item.transferStock直接実行
5. **148-153行目**: 成功/失敗に応じたフィードバック

#### Step 3: ロケーション→アイテム移動を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | stock.dart | `lib/barcode/stock.dart` | StockLocationScanInItemsHandler（166-227行目） |

**主要処理フロー**:
- **167行目**: コンストラクタでInvenTreeStockLocationを受け取る
- **175-178行目**: 在庫アイテムを取得、確認設定を取得
- **186-189行目**: 同一ロケーション検出時の警告
- **192-220行目**: 確認設定に応じた移動処理
- **225行目**: return falseで連続スキャンモードを維持

#### Step 4: 親ロケーション設定を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | stock.dart | `lib/barcode/stock.dart` | ScanParentLocationHandler（236-270行目） |

**主要処理フロー**:
- **237行目**: コンストラクタでInvenTreeStockLocationを受け取る
- **243-245行目**: location.update({parent: locationId})で親を設定
- **248-267行目**: ステータスコードに応じたエラーハンドリング

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

```
在庫スキャン操作

パターンA: アイテム → ロケーション
StockItemScanIntoLocationHandler(item)
    │
    ├─ BarcodeScanStockLocationHandler (基底)
    │      ├─ getOverlayText() → "barcodeScanLocation"
    │      └─ onBarcodeMatched()
    │             ├─ stocklocationキー確認
    │             └─ onLocationScanned(locationId)
    │
    └─ onLocationScanned(locationId)
           │
           ├─ INV_STOCK_CONFIRM_SCAN取得
           │
           ├─ confirm=true
           │      └─ launchApiForm(transferStock)
           │             └─ showSnackIcon(stockItemUpdated)
           │
           └─ confirm=false
                  └─ item.transferStock(locationId)
                         └─ barcodeSuccess/barcodeFailureTone

パターンB: ロケーション → アイテム
StockLocationScanInItemsHandler(location)
    │
    ├─ BarcodeScanStockItemHandler (基底)
    │      ├─ getOverlayText() → "barcodeScanItem"
    │      └─ onBarcodeMatched()
    │             ├─ stockitemキー確認
    │             └─ onItemScanned(itemId)
    │
    └─ onItemScanned(itemId)
           │
           ├─ InvenTreeStockItem().get(itemId)
           │
           ├─ 同一ロケーション確認
           │
           ├─ confirm=true
           │      └─ launchApiForm(transferStock)
           │
           └─ confirm=false
                  └─ item.transferStock(location.pk)
```

### データフロー図

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

アイテム詳細画面
or ロケーション画面
        │
        ▼
スキャンハンドラ生成
        │
        ▼
バーコードスキャン
        │
        ▼
processBarcode()
        │
        ▼
onBarcodeMatched()
        │
        ├─ stocklocation検出
        │      │
        │      ▼
        │  onLocationScanned(locationId)
        │      │
        │      ▼
        │  transferStock(locationId)
        │
        └─ stockitem検出
               │
               ▼
           onItemScanned(itemId)
               │
               ▼
           transferStock(location.pk)
               │
               ▼
         成功/失敗フィードバック
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| stock.dart | `lib/barcode/stock.dart` | ソース | 在庫スキャン操作ハンドラ群 |
| handler.dart | `lib/barcode/handler.dart` | ソース | BarcodeHandler基底クラス |
| barcode.dart | `lib/barcode/barcode.dart` | ソース | scanBarcode、barcodeSuccess |
| tones.dart | `lib/barcode/tones.dart` | ソース | 成功/失敗音の再生 |
| stock.dart | `lib/inventree/stock.dart` | ソース | InvenTreeStockItem、StockLocationモデル |
| api_form.dart | `lib/api_form.dart` | ソース | launchApiForm |
| preferences.dart | `lib/preferences.dart` | ソース | INV_STOCK_CONFIRM_SCAN設定 |
| snacks.dart | `lib/widget/snacks.dart` | ソース | スナックバー通知 |
| stock_detail.dart | `lib/widget/stock/stock_detail.dart` | ソース | 在庫詳細（呼び出し元） |
| location_display.dart | `lib/widget/stock/location_display.dart` | ソース | ロケーション画面（呼び出し元） |
