# 機能設計書 43-バーコード照合

## 概要

本ドキュメントは、InvenTreeアプリにおけるバーコード照合機能の設計を記述する。スキャンしたバーコードをInvenTreeサーバーのデータベースと照合し、対応するアイテム（パーツ、在庫、発注、会社等）を特定して適切な詳細画面へ遷移する機能である。

### 本機能の処理概要

バーコード照合機能は、スキャンされたバーコードデータをInvenTreeサーバーに送信し、データベース内の登録済みアイテムと照合する機能である。照合に成功した場合は、アイテムの種類に応じた詳細画面に自動的に遷移する。

**業務上の目的・背景**：在庫管理業務において、バーコードによるアイテムの即時特定は作業効率の大幅な向上に寄与する。物理的なラベルをスキャンするだけで、システム内のアイテム情報に即座にアクセスできることで、検索時間を削減し、誤認識を防止する。特に大量のアイテムを扱う倉庫業務では、この機能が業務のボトルネック解消に貢献する。

**機能の利用シーン**：
- 倉庫内で部品や在庫アイテムのバーコードをスキャンして詳細を確認
- 入荷検品時に発注伝票のバーコードをスキャンして発注情報を確認
- 棚卸し作業時にアイテムをスキャンして現在地を確認
- 顧客・サプライヤー情報のラベルをスキャンして会社情報を表示
- 製造元パーツのバーコードから製品仕様を確認

**主要な処理内容**：
1. スキャンされたバーコードデータをサーバーのbarcode/エンドポイントに送信
2. サーバーからのレスポンスを解析し、マッチしたモデルタイプを特定
3. モデルタイプに応じたデータ取得APIを呼び出し
4. 取得したデータを使用して対応する詳細画面へ遷移
5. マッチしない場合はエラー通知を表示

**関連システム・外部連携**：
- InvenTree REST API（barcode/エンドポイント）
- 各モデルのAPIエンドポイント（part/, stock/, company/等）

**権限による制御**：照合自体には特別な権限は不要だが、遷移先の詳細画面は各モデルに応じた参照権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 5 | バーコードスキャン画面 | 主画面 | バーコード照合処理の起点 |
| 22 | 部品詳細画面 | 遷移先画面 | パーツバーコード照合成功時 |
| 25 | 在庫アイテム詳細画面 | 遷移先画面 | 在庫バーコード照合成功時 |
| 23 | 在庫ロケーション画面 | 遷移先画面 | ロケーションバーコード照合成功時 |
| 27 | 購入注文詳細画面 | 遷移先画面 | 発注バーコード照合成功時 |
| 29 | 販売注文詳細画面 | 遷移先画面 | 受注バーコード照合成功時 |
| 31 | 会社詳細画面 | 遷移先画面 | 会社バーコード照合成功時 |

## 機能種別

データ照合 / 画面遷移制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| barcode | String | Yes | スキャンされたバーコード文字列 | 空文字でないこと |

### 入力データソース

- バーコードスキャン機能（No.42）からのコールバック

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 照合結果 | Map<String, dynamic> | サーバーからの照合結果（モデルタイプ、pk等） |
| 遷移先画面 | Widget | 照合成功時の詳細画面 |
| エラー通知 | SnackBar | 照合失敗時のエラーメッセージ |

### 出力先

- Navigator（画面遷移）
- スナックバー（通知表示）
- スピーカー（成功/失敗音）

## 処理フロー

### 処理シーケンス

```
1. バーコードデータ受信（onBarcodeMatched）
   └─ サーバーレスポンスのMapを受け取る

2. 有効なモデルタイプリストの定義
   ├─ stockitem
   ├─ supplierpart
   ├─ manufacturerpart
   ├─ part
   ├─ stocklocation
   ├─ company
   ├─ purchaseorder（APIバージョン107以上）
   └─ salesorder（APIバージョン107以上）

3. レスポンス解析
   ├─ 各モデルタイプのキーを順次チェック
   ├─ 含まれていればpkを抽出
   └─ 有効なpk（>0）が見つかれば処理続行

4. モデルタイプに応じた処理分岐
   ├─ stockitem → handleStockItem(pk)
   ├─ purchaseorder → handlePurchaseOrder(pk)
   ├─ salesorder → handleSalesOrder(pk)
   ├─ stocklocation → handleStockLocation(pk)
   ├─ supplierpart → handleSupplierPart(pk)
   ├─ manufacturerpart → handleManufacturerPart(pk)
   ├─ part → handlePart(pk)
   └─ company → handleCompany(pk)

5. 各ハンドラ処理
   ├─ API呼び出しでモデルデータ取得
   ├─ スキャン画面をpop
   └─ 詳細画面へpush遷移

6. 照合失敗時
   ├─ 失敗音再生
   └─ エラースナックバー表示
```

### フローチャート

```mermaid
flowchart TD
    A[バーコードスキャン完了] --> B[processBarcode API呼び出し]
    B --> C{success含む?}
    C -->|No| D[onBarcodeUnknown]
    C -->|Yes| E[onBarcodeMatched]
    D --> F[失敗音・エラー表示]
    E --> G[有効モデルタイプを順次チェック]
    G --> H{マッチするモデルあり?}
    H -->|No| I[barcodeUnknown表示]
    H -->|Yes| J{モデルタイプ判定}
    J -->|stockitem| K[handleStockItem]
    J -->|part| L[handlePart]
    J -->|stocklocation| M[handleStockLocation]
    J -->|purchaseorder| N[handlePurchaseOrder]
    J -->|salesorder| O[handleSalesOrder]
    J -->|supplierpart| P[handleSupplierPart]
    J -->|manufacturerpart| Q[handleManufacturerPart]
    J -->|company| R[handleCompany]
    K --> S[API: StockItem.get]
    L --> T[API: Part.get]
    M --> U[API: StockLocation.get]
    N --> V[API: PurchaseOrder.get]
    O --> W[API: SalesOrder.get]
    P --> X[API: SupplierPart.get]
    Q --> Y[API: ManufacturerPart.get]
    R --> Z[API: Company.get]
    S --> AA[成功音・StockDetailWidget遷移]
    T --> AB[成功音・PartDetailWidget遷移]
    U --> AC[成功音・LocationDisplayWidget遷移]
    V --> AD[成功音・PurchaseOrderDetailWidget遷移]
    W --> AE[成功音・SalesOrderDetailWidget遷移]
    X --> AF[成功音・SupplierPartDetailWidget遷移]
    Y --> AG[成功音・ManufacturerPartDetailWidget遷移]
    Z --> AH[成功音・CompanyDetailWidget遷移]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-43-01 | モデル優先順位 | 複数モデルが該当する場合、リスト定義順で最初にマッチしたものを採用 | 照合時 |
| BR-43-02 | 発注・受注の条件 | purchaseorderとsalesorderはAPIバージョン107以上で有効 | サーバー接続時 |
| BR-43-03 | 有効pkの判定 | pk値が0より大きい場合のみ有効とみなす | 照合結果解析時 |
| BR-43-04 | 照合失敗時の詳細表示 | ユーザーがスナックバーをタップすると詳細ダイアログ表示 | 照合失敗時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| バーコード照合 | 複数テーブル | SELECT | バーコードに対応するレコードを検索 |
| モデルデータ取得 | 各モデルテーブル | SELECT | pk指定でレコード取得 |

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

サーバー側で処理されるため、クライアントからは直接操作しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| E-43-01 | マッチなし | バーコードがデータベースに未登録 | 失敗音、barcodeNoMatchスナックバー |
| E-43-02 | 不正レスポンス | pkが抽出できない | 失敗音、barcodeUnknownスナックバー |
| E-43-03 | データ取得失敗 | モデルAPIからデータ取得失敗 | 処理継続せず（画面遷移しない） |
| E-43-04 | 例外発生 | pk解析時の例外 | Sentryレポート、処理継続 |

### リトライ仕様

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

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

読み取り専用のため、トランザクション管理は不要。

## パフォーマンス要件

- バーコード照合API応答: 1秒以内
- 詳細画面への遷移: 2秒以内（モデルデータ取得含む）

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

- 認証トークンを使用したAPIアクセス
- 照合結果は各モデルの参照権限に基づいて制限される

## 備考

- 照合優先順位: stockitem > supplierpart > manufacturerpart > part > stocklocation > company > purchaseorder > salesorder
- OneContextを使用したグローバルナビゲーション

---

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

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

### 推奨読解順序

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

照合対象となるモデルタイプの定義を把握する。

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

**読解のコツ**: validModelsリストで照合対象のモデルタイプが定義されている。このリストの順序が優先順位となる。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | barcode.dart | `lib/barcode/barcode.dart` | BarcodeScanHandler.onBarcodeMatched（246-339行目） |

**主要処理フロー**:
1. **246-249行目**: pk変数とmodel変数の初期化
2. **252-264行目**: validModelsリストの定義、APIバージョンによる条件追加
3. **266-280行目**: レスポンスデータから有効なモデルとpkを抽出
4. **283-315行目**: モデルタイプに応じたハンドラ呼び出し
5. **318-338行目**: マッチしない場合のエラー処理

#### Step 3: 個別ハンドラを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | barcode.dart | `lib/barcode/barcode.dart` | handlePart, handleStockItem等の個別ハンドラ |

**主要処理フロー**:
- **135-144行目**: handlePart - InvenTreePart().get()でデータ取得、PartDetailWidgetへ遷移
- **149-158行目**: handleStockItem - InvenTreeStockItem().get()、StockDetailWidgetへ遷移
- **163-172行目**: handleStockLocation - InvenTreeStockLocation().get()、LocationDisplayWidgetへ遷移
- **177-188行目**: handleSupplierPart - InvenTreeSupplierPart().get()、SupplierPartDetailWidgetへ遷移
- **193-204行目**: handleManufacturerPart - InvenTreeManufacturerPart().get()、ManufacturerPartDetailWidgetへ遷移
- **206-215行目**: handleCompany - InvenTreeCompany().get()、CompanyDetailWidgetへ遷移
- **220-231行目**: handlePurchaseOrder - InvenTreePurchaseOrder().get()、PurchaseOrderDetailWidgetへ遷移
- **234-244行目**: handleSalesOrder - InvenTreeSalesOrder().get()、SalesOrderDetailWidgetへ遷移

#### Step 4: 未照合時の処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | barcode.dart | `lib/barcode/barcode.dart` | onBarcodeUnknown |

**主要処理フロー**:
- **122-130行目**: onBarcodeUnknown - 失敗音再生、barcodeNoMatchスナックバー表示

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

```
BarcodeScanHandler
    │
    ├─ onBarcodeMatched(data)
    │      │
    │      ├─ validModels定義
    │      │      ├─ InvenTreeStockItem.MODEL_TYPE ("stockitem")
    │      │      ├─ InvenTreeSupplierPart.MODEL_TYPE ("supplierpart")
    │      │      ├─ InvenTreeManufacturerPart.MODEL_TYPE ("manufacturerpart")
    │      │      ├─ InvenTreePart.MODEL_TYPE ("part")
    │      │      ├─ InvenTreeStockLocation.MODEL_TYPE ("stocklocation")
    │      │      ├─ InvenTreeCompany.MODEL_TYPE ("company")
    │      │      ├─ InvenTreePurchaseOrder.MODEL_TYPE [API>=107]
    │      │      └─ InvenTreeSalesOrder.MODEL_TYPE [API>=107]
    │      │
    │      ├─ モデル/pk抽出ループ
    │      │
    │      └─ ハンドラ呼び出し
    │             ├─ handleStockItem(pk)
    │             │      ├─ InvenTreeStockItem().get(pk)
    │             │      ├─ OneContext().pop()
    │             │      └─ OneContext().push(StockDetailWidget)
    │             │
    │             ├─ handlePart(pk)
    │             │      ├─ InvenTreePart().get(pk)
    │             │      ├─ OneContext().pop()
    │             │      └─ OneContext().push(PartDetailWidget)
    │             │
    │             └─ [他のハンドラも同様の構造]
    │
    └─ onBarcodeUnknown(data)
           ├─ barcodeFailureTone()
           └─ showSnackIcon(L10().barcodeNoMatch)
```

### データフロー図

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

バーコードデータ
    │
    ▼
processBarcode()
    │
    ▼
InvenTreeAPI.post("barcode/")
    │
    ▼
APIResponse
    │
    ├─── success含む ───▶ onBarcodeMatched()
    │                           │
    │                           ▼
    │                    validModelsループ
    │                           │
    │                    ┌──────┴──────┐
    │                    │             │
    │                    ▼             ▼
    │              マッチあり      マッチなし
    │                    │             │
    │                    ▼             ▼
    │             handleXxx(pk)   エラー表示
    │                    │
    │                    ▼
    │             InvenTreeXxx().get(pk)
    │                    │
    │                    ▼
    │             詳細画面へ遷移
    │
    └─── error含む ────▶ onBarcodeUnknown()
                               │
                               ▼
                         失敗音・エラー表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| barcode.dart | `lib/barcode/barcode.dart` | ソース | BarcodeScanHandlerと個別ハンドラ |
| handler.dart | `lib/barcode/handler.dart` | ソース | BarcodeHandler基底クラス |
| tones.dart | `lib/barcode/tones.dart` | ソース | 成功/失敗音の再生 |
| part.dart | `lib/inventree/part.dart` | ソース | InvenTreePartモデル |
| stock.dart | `lib/inventree/stock.dart` | ソース | InvenTreeStockItem, StockLocationモデル |
| company.dart | `lib/inventree/company.dart` | ソース | InvenTreeCompany, SupplierPart, ManufacturerPartモデル |
| purchase_order.dart | `lib/inventree/purchase_order.dart` | ソース | InvenTreePurchaseOrderモデル |
| sales_order.dart | `lib/inventree/sales_order.dart` | ソース | InvenTreeSalesOrderモデル |
| 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` | ソース | ロケーション画面 |
| company_detail.dart | `lib/widget/company/company_detail.dart` | ソース | 会社詳細画面 |
| purchase_order_detail.dart | `lib/widget/order/purchase_order_detail.dart` | ソース | 発注詳細画面 |
| sales_order_detail.dart | `lib/widget/order/sales_order_detail.dart` | ソース | 受注詳細画面 |
| supplier_part_detail.dart | `lib/widget/company/supplier_part_detail.dart` | ソース | サプライヤーパーツ詳細画面 |
| manufacturer_part_detail.dart | `lib/widget/company/manufacturer_part_detail.dart` | ソース | 製造元パーツ詳細画面 |
| api.dart | `lib/api.dart` | ソース | InvenTreeAPI（supportsOrderBarcodes判定含む） |
| snacks.dart | `lib/widget/snacks.dart` | ソース | スナックバー通知 |
