# 機能設計書 44-バーコード割当

## 概要

本ドキュメントは、InvenTreeアプリにおけるカスタムバーコードをアイテムに割り当てる機能の設計を記述する。未登録のバーコードをスキャンし、特定のアイテム（パーツ、在庫、ロケーション等）に紐付けて登録する機能である。

### 本機能の処理概要

バーコード割当機能は、InvenTreeデータベースに未登録のバーコードをスキャンし、そのバーコードを特定のアイテムに割り当てる機能である。これにより、任意のバーコードラベルを既存のアイテムと関連付けることができる。

**業務上の目的・背景**：在庫管理において、部品やアイテムには様々な形式のバーコードラベルが付与される場合がある。製造元のバーコード、社内管理用のバーコード、顧客指定のバーコードなど、多様なバーコード体系が存在する。本機能により、これらの任意のバーコードをInvenTree内のアイテムと紐付けることで、どのようなバーコードからでもアイテムを特定できるようになり、業務の柔軟性と効率性が向上する。

**機能の利用シーン**：
- 新規部品に社内管理用のカスタムバーコードラベルを貼付・登録
- 既存アイテムに追加のバーコード（サプライヤーラベル等）を関連付け
- 在庫ロケーションに位置特定用のバーコードを割り当て
- 棚卸し用の一時的なバーコードを在庫アイテムに割り当て

**主要な処理内容**：
1. 対象アイテムの詳細画面からバーコード割当アクションを起動
2. UniqueBarcodeHandlerでバーコードスキャンを実行
3. スキャンしたバーコードがユニーク（未登録）であることを確認
4. InvenTree APIのlinkBarcodeエンドポイントでバーコードとアイテムを紐付け
5. 成功/失敗の通知と画面のリフレッシュ

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

**権限による制御**：対象アイテムの編集権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 5 | バーコードスキャン画面 | 主画面 | カスタムバーコードをスキャン |
| 22 | 部品詳細画面 | 遷移元画面 | バーコード割当ボタン押下でスキャン開始 |
| 25 | 在庫アイテム詳細画面 | 遷移元画面 | バーコード割当ボタン押下でスキャン開始 |
| 23 | 在庫ロケーション画面 | 遷移元画面 | バーコード割当ボタン押下でスキャン開始 |

## 機能種別

データ登録 / バーコード連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| barcode | String | Yes | 割り当てるバーコード文字列（現在バーコード） | 空でないこと |
| model | String | Yes | 対象モデルタイプ（例: "part", "stockitem"） | 有効なモデルタイプ |
| pk | int | Yes | 対象アイテムのプライマリキー | 0より大きい |

### 入力データソース

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

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 登録結果 | bool | バーコード割当の成功/失敗 |
| 通知メッセージ | String | 成功時: barcodeAssigned、失敗時: barcodeNotAssigned |

### 出力先

- スナックバー（結果通知）
- 呼び出し元画面のリフレッシュ

## 処理フロー

### 処理シーケンス

```
1. バーコード割当アクション起動（customBarcodeAction）
   ├─ 現在のbarcode状態を確認
   └─ barcodeが空の場合、割当モードでスキャン開始

2. UniqueBarcodeHandlerでスキャン
   ├─ オーバーレイテキスト表示: "barcodeScanAssign"
   └─ バーコードスキャン実行

3. バーコード照合（onBarcodeMatched/onBarcodeUnknown）
   ├─ サーバーに照合リクエスト
   ├─ barcode_dataを抽出
   └─ 未登録バーコードの場合、コールバック実行

4. バーコード紐付け（linkBarcode API）
   ├─ InvenTreeAPI().linkBarcode({model: pk, barcode: barcode})
   ├─ 成功時: barcodeAssignedスナックバー
   └─ 失敗時: barcodeNotAssignedスナックバー

5. 画面リフレッシュ
   └─ state.refresh(context)
```

### フローチャート

```mermaid
flowchart TD
    A[詳細画面でバーコード割当ボタン] --> B{現在barcode登録あり?}
    B -->|Yes| C[解除モード表示]
    B -->|No| D[割当モード]
    D --> E[UniqueBarcodeHandler生成]
    E --> F[scanBarcode起動]
    F --> G[バーコードスキャン]
    G --> H[processBarcode API呼び出し]
    H --> I{レスポンス}
    I -->|success| J[onBarcodeMatched]
    I -->|error| K[onBarcodeUnknown]
    J --> L{barcode_data抽出成功?}
    K --> L
    L -->|No| M[エラースナックバー]
    L -->|Yes| N[コールバック実行]
    N --> O[linkBarcode API]
    O --> P{登録成功?}
    P -->|Yes| Q[barcodeAssignedスナックバー]
    P -->|No| R[barcodeNotAssignedスナックバー]
    Q --> S[画面リフレッシュ]
    R --> S
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-44-01 | 排他的割当 | 1つのバーコードは1つのアイテムにのみ割当可能 | 常時 |
| BR-44-02 | 既登録チェック | 既に登録済みのバーコードは別アイテムへの割当不可 | バーコードスキャン時 |
| BR-44-03 | 空バーコード条件 | 現在barcodeが空の場合のみ割当アクション表示 | アクションボタン表示時 |
| BR-44-04 | ユニークバーコード要件 | システム内で未使用のバーコードのみ割当可能 | 割当処理時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| バーコード照合 | Barcode | SELECT | バーコードの登録状況確認 |
| バーコード登録 | Barcode | INSERT | バーコードとアイテムの紐付け |

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

#### Barcode

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | barcode_data, model_type, model_pk | スキャン値、モデル情報 | linkBarcode API経由 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| E-44-01 | 既登録エラー | スキャンしたバーコードが既に別アイテムに登録済み | 失敗音、barcodeError表示 |
| E-44-02 | 紐付け失敗 | linkBarcode APIがfalseを返却 | barcodeNotAssignedスナックバー |
| E-44-03 | データ欠損 | barcode_dataが取得できない | missingDataエラー表示 |
| E-44-04 | barcode_hash欠損 | ハッシュデータが取得できない | barcodeMissingHashエラー表示 |

### リトライ仕様

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

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

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

## パフォーマンス要件

- バーコード照合: 1秒以内
- バーコード登録: 1秒以内

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

- 認証トークンを使用したAPIアクセス
- 対象アイテムの編集権限チェック（canEdit）

## 備考

- SpeedDialChildとしてアクションボタンを提供
- 現在barcodeが空の場合のみ割当オプションを表示
- barcodeが登録済みの場合は解除オプション（No.45）を表示

---

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

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

### 推奨読解順序

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

バーコード割当で使用されるハンドラクラスを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | barcode.dart | `lib/barcode/barcode.dart` | UniqueBarcodeHandlerクラスの定義（345-392行目） |

**読解のコツ**: UniqueBarcodeHandlerはBarcodeHandlerを継承し、ユニークなバーコード（未登録バーコード）を検出するために特化している。コールバック関数を通じて結果を返す。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | barcode.dart | `lib/barcode/barcode.dart` | customBarcodeAction関数（394-439行目） |

**主要処理フロー**:
1. **394-400行目**: 関数シグネチャ - context, state, barcode, model, pkを受け取る
2. **401-419行目**: barcodeが空の場合の割当処理
3. **406-417行目**: UniqueBarcodeHandlerにコールバックを設定し、linkBarcodeを呼び出す
4. **418行目**: scanBarcodeでスキャン画面を起動

#### Step 3: UniqueBarcodeHandlerの処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | barcode.dart | `lib/barcode/barcode.dart` | UniqueBarcodeHandlerの各メソッド |

**主要処理フロー**:
- **346行目**: コンストラクタでcallback関数を受け取る
- **354-360行目**: getOverlayTextでオーバーレイ表示テキストを返す
- **362-386行目**: onBarcodeMatchedでbarcode_dataを抽出しコールバック実行
- **388-391行目**: onBarcodeUnknownでも同様にonBarcodeMatchedを呼び出す

#### Step 4: API呼び出しを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | api.dart | `lib/api.dart` | linkBarcodeメソッド（barcode/link/エンドポイント） |

**主要処理フロー**:
- linkBarcode APIに{model: pk, barcode: barcode}を送信
- 成功時はtrueを返却

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

```
customBarcodeAction(context, state, barcode, model, pk)
    │
    ├─ barcode.isEmpty? → 割当モード
    │      │
    │      ├─ UniqueBarcodeHandler(callback)
    │      │      │
    │      │      ├─ getOverlayText() → "barcodeScanAssign"
    │      │      │
    │      │      ├─ processBarcode()
    │      │      │      └─ InvenTreeAPI().post("barcode/")
    │      │      │
    │      │      ├─ onBarcodeMatched(data)
    │      │      │      ├─ barcode_data抽出
    │      │      │      ├─ barcodeSuccessTone()
    │      │      │      ├─ OneContext().pop()
    │      │      │      └─ callback(barcode)
    │      │      │
    │      │      └─ onBarcodeUnknown(data)
    │      │             └─ onBarcodeMatched(data) [未登録もOK]
    │      │
    │      └─ scanBarcode(context, handler)
    │
    └─ callback実行
           │
           ├─ InvenTreeAPI().linkBarcode({model: pk, barcode: barcode})
           │
           ├─ showSnackIcon(barcodeAssigned/barcodeNotAssigned)
           │
           └─ state.refresh(context)
```

### データフロー図

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

詳細画面                    customBarcodeAction
(model, pk, barcode="")           │
        │                         ▼
        │              UniqueBarcodeHandler生成
        │                         │
        │                         ▼
        │                  scanBarcode()
        │                         │
        ▼                         ▼
バーコードスキャン ─────▶ processBarcode()
                                  │
                                  ▼
                         InvenTreeAPI.post("barcode/")
                                  │
                    ┌─────────────┴─────────────┐
                    ▼                           ▼
            onBarcodeMatched()          onBarcodeUnknown()
                    │                           │
                    └─────────────┬─────────────┘
                                  │
                                  ▼
                         barcode_data抽出
                                  │
                                  ▼
                         callback(barcode)
                                  │
                                  ▼
                    InvenTreeAPI.linkBarcode()
                                  │
                                  ▼
                         成功/失敗スナックバー
                                  │
                                  ▼
                         state.refresh()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| barcode.dart | `lib/barcode/barcode.dart` | ソース | UniqueBarcodeHandler、customBarcodeAction |
| handler.dart | `lib/barcode/handler.dart` | ソース | BarcodeHandler基底クラス |
| tones.dart | `lib/barcode/tones.dart` | ソース | 成功/失敗音の再生 |
| api.dart | `lib/api.dart` | ソース | InvenTreeAPI.linkBarcode |
| snacks.dart | `lib/widget/snacks.dart` | ソース | スナックバー通知 |
| refreshable_state.dart | `lib/widget/refreshable_state.dart` | ソース | state.refresh()メソッド |
| part_detail.dart | `lib/widget/part/part_detail.dart` | ソース | パーツ詳細（呼び出し元） |
| stock_detail.dart | `lib/widget/stock/stock_detail.dart` | ソース | 在庫詳細（呼び出し元） |
| location_display.dart | `lib/widget/stock/location_display.dart` | ソース | ロケーション画面（呼び出し元） |
