# 機能設計書 48-受注引当スキャン

## 概要

本ドキュメントは、InvenTreeアプリにおけるバーコードスキャンを使用した受注引当機能の設計を記述する。在庫アイテムのバーコードをスキャンして、受注（Sales Order）に在庫を引き当てる機能である。

### 本機能の処理概要

受注引当スキャン機能は、在庫アイテムのバーコードをスキャンして、受注の明細行に対して在庫を引き当てる機能である。これにより、出荷準備作業においてバーコードを活用した効率的な在庫割当が可能となる。

**業務上の目的・背景**：受注出荷業務において、注文された商品に対して在庫を引き当てる作業は重要なプロセスである。バーコードスキャンによる引当処理は、手入力ミスを防止し、作業効率を向上させる。特に多品種の商品を扱う場合、バーコードによる正確な在庫特定と引当が業務品質に大きく貢献する。

**機能の利用シーン**：
- 受注に対して出荷する在庫アイテムをスキャンして引当
- ピッキング作業時に商品をスキャンして出荷準備
- 出荷時に最終確認としてアイテムをスキャンして引当確定
- 部分出荷時の引当数量指定

**主要な処理内容**：
1. SOAllocateStockHandlerによるバーコードスキャン
2. barcode/so-allocate/エンドポイントへのバーコード送信
3. サーバーレスポンスの解析（明細行情報、在庫情報、数量等）
4. 自動引当/確認フォーム表示
5. 引当処理の実行と結果通知

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

**権限による制御**：受注の引当権限（sales_order.change）が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 29 | 販売注文詳細画面 | 遷移元画面 | バーコードスキャンで受注に在庫を引き当て |
| 5 | バーコードスキャン画面 | 主画面 | 在庫アイテムバーコードのスキャン |

## 機能種別

データ更新 / バーコード連携 / 受注管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| salesOrder | InvenTreeSalesOrder | No | 対象の受注 | pk > 0 |
| lineItem | InvenTreeSOLineItem | No | 対象の明細行 | pk > 0 |
| shipment | InvenTreeSalesOrderShipment | No | 対象の出荷 | pk > 0 |
| barcode | String | Yes | スキャンされたバーコード | 空でないこと |

### 入力データソース

- バーコードスキャン（カメラ/ウェッジ）
- 呼び出し元画面からの受注/明細行/出荷情報

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 引当結果 | bool | 引当の成功/失敗 |
| 通知メッセージ | String | 成功時: allocated、失敗時: エラーメッセージ |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. 受注引当スキャンの起動
   ├─ SOAllocateStockHandler(salesOrder, lineItem, shipment)でハンドラ生成
   └─ scanBarcodeでスキャン画面起動

2. バーコード処理（processBarcode）
   ├─ 追加データ構築
   │   ├─ sales_order: 受注ID
   │   ├─ shipment: 出荷ID
   │   └─ line: 明細行ID
   └─ barcode/so-allocate/エンドポイントへPOST

3. レスポンス処理
   ├─ line_item含む: allocated通知
   └─ action_required含む: 追加情報入力が必要

4. 追加情報入力（onBarcodeUnhandled）
   ├─ 引当フォームフィールド取得
   ├─ フィールド値設定（line_item, stock_item, quantity, shipment等）
   └─ launchApiFormで引当フォーム表示

5. 引当処理
   ├─ salesOrder.allocate_url APIへPOST
   ├─ 成功時: allocatedスナックバー
   └─ 失敗時: エラースナックバー
```

### フローチャート

```mermaid
flowchart TD
    A[受注引当スキャン開始] --> B[SOAllocateStockHandler生成]
    B --> C[バーコードスキャン]
    C --> D[processBarcode]
    D --> E[so_extra_data構築]
    E --> F[barcode/so-allocate/ API呼び出し]
    F --> G{レスポンス}
    G -->|line_item| H[onBarcodeMatched]
    G -->|error| I[onBarcodeUnknown]
    G -->|action_required| J[onBarcodeUnhandled]
    H --> K[allocated通知]
    I --> L[エラースナックバー]
    J --> M[allocateFormFields取得]
    M --> N[フィールド値設定]
    N --> O[launchApiForm表示]
    O --> P{ユーザー確認}
    P -->|確定| Q[allocate API実行]
    P -->|キャンセル| R[終了]
    Q --> S{成功?}
    S -->|Yes| T[allocated通知]
    S -->|No| U[エラー通知]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-48-01 | 在庫フィルタ | in_stock=true, available=trueの在庫のみ引当可能 | 引当フォーム表示時 |
| BR-48-02 | 受注限定 | salesOrder指定時はその受注の明細行のみ引当可能 | 照合時 |
| BR-48-03 | 出荷指定 | shipment指定時はその出荷に対して引当 | 引当処理時 |
| BR-48-04 | 追加情報要求 | サーバーがaction_requiredを返した場合は入力フォーム表示 | レスポンス処理時 |
| BR-48-05 | 販売可能パーツ | isSalable=trueのパーツのみ引当対象 | 照合時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| バーコード照合 | StockItem, SalesOrder | SELECT | バーコードから在庫/受注特定 |
| 引当作成 | SalesOrderAllocation | INSERT | 引当レコード作成 |
| 在庫更新 | StockItem | UPDATE | 引当数量の反映 |

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

#### SalesOrderAllocation

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | line_item, stock_item, quantity, shipment | 引当情報 | 新規引当作成 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| E-48-01 | バーコード不一致 | バーコードが在庫アイテムにマッチしない | barcodeErrorスナックバー |
| E-48-02 | 引当失敗 | allocate APIが失敗 | エラースナックバー |
| E-48-03 | 非販売品 | partNotSalableの場合 | partNotSalableスナックバー |
| E-48-04 | データ欠損 | line_item応答がない | onBarcodeUnknown呼び出し |

### リトライ仕様

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

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

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

## パフォーマンス要件

- バーコード照合: 1秒以内
- 引当処理: 2秒以内

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

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

## 備考

- APIバージョン160以上でbarcode/so-allocate/エンドポイントをサポート
- SOAddItemBarcodeHandlerは受注への明細行追加用（別機能）

---

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

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

### 推奨読解順序

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

受注引当スキャンで使用されるハンドラクラスを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | sales_order.dart | `lib/barcode/sales_order.dart` | SOAllocateStockHandler（78-163行目） |

**読解のコツ**: SOAllocateStockHandlerはBarcodeHandlerを継承し、processBarcode、onBarcodeMatched、onBarcodeUnhandled、onBarcodeUnknownをオーバーライドしている。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | sales_order.dart | `lib/barcode/sales_order.dart` | SOAllocateStockHandlerのコンストラクタとprocessBarcode |

**主要処理フロー**:
1. **79行目**: コンストラクタでsalesOrder, lineItem, shipmentを受け取る（すべてオプション）
2. **86行目**: getOverlayTextでallocateStockを返す
3. **89-101行目**: processBarcode - 追加データ構築、barcode/so-allocate/へAPI呼び出し

#### Step 3: レスポンス処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | sales_order.dart | `lib/barcode/sales_order.dart` | onBarcodeMatched、onBarcodeUnhandled |

**主要処理フロー**:
- **104-111行目**: onBarcodeMatched - line_itemで成功通知
- **114-153行目**: onBarcodeUnhandled - action_required時の追加情報入力処理
- **121-138行目**: フィールド設定（line_item, stock_item, quantity, shipment等）
- **142-152行目**: launchApiFormで引当フォーム表示

#### Step 4: 受注明細行追加を理解する（参考）

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | sales_order.dart | `lib/barcode/sales_order.dart` | SOAddItemBarcodeHandler（21-76行目） |

**主要処理フロー**:
- **22行目**: コンストラクタでsalesOrderを受け取る
- **30-75行目**: onBarcodeMatched - パーツ取得、isSalable確認、明細行追加フォーム表示

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

```
SOAllocateStockHandler(salesOrder, lineItem, shipment)
    │
    ├─ BarcodeHandler (基底)
    │
    ├─ getOverlayText() → "allocateStock"
    │
    ├─ processBarcode(barcode)
    │      │
    │      ├─ so_extra_data構築
    │      │      ├─ sales_order: salesOrder?.pk
    │      │      ├─ shipment: shipment?.pk
    │      │      └─ line: lineItem?.pk
    │      │
    │      └─ super.processBarcode(barcode, url: "barcode/so-allocate/")
    │
    ├─ onBarcodeMatched(data)
    │      │
    │      ├─ line_item検出
    │      │      └─ barcodeSuccess(allocated)
    │      │
    │      └─ それ以外
    │             └─ onBarcodeUnknown(data)
    │
    └─ onBarcodeUnhandled(data)
           │
           ├─ action_required + line_item検証
           │
           ├─ allocateFormFields取得
           │
           ├─ フィールド値設定
           │      ├─ line_item: data["line_item"]
           │      ├─ stock_item: data["stock_item"]
           │      ├─ stock_filters: {in_stock, available, part}
           │      ├─ quantity: data["quantity"]
           │      └─ shipment: data["shipment"]
           │
           └─ launchApiForm(salesOrder.allocate_url)
                  │
                  └─ onSuccess: showSnackIcon(allocated)
```

### データフロー図

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

受注詳細画面
        │
        ▼
SOAllocateStockHandler生成
(salesOrder, lineItem, shipment)
        │
        ▼
バーコードスキャン
        │
        ▼
processBarcode()
        │
        ├─ so_extra_data構築
        │
        └─ barcode/so-allocate/ API
                  │
                  ▼
           APIResponse
                  │
        ┌─────────┼─────────┐
        ▼         ▼         ▼
   line_item  action_req   error
        │         │         │
        ▼         ▼         ▼
   allocated  launchApi   エラー表示
     通知     Form()
                  │
                  ▼
           allocate API
                  │
                  ▼
            allocated通知
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| sales_order.dart | `lib/barcode/sales_order.dart` | ソース | SOAllocateStockHandler、SOAddItemBarcodeHandler |
| handler.dart | `lib/barcode/handler.dart` | ソース | BarcodeHandler基底クラス |
| barcode.dart | `lib/barcode/barcode.dart` | ソース | scanBarcode、barcodeSuccess |
| tones.dart | `lib/barcode/tones.dart` | ソース | 成功/失敗音の再生 |
| sales_order.dart | `lib/inventree/sales_order.dart` | ソース | InvenTreeSalesOrder、SOLineItemモデル |
| api_form.dart | `lib/api_form.dart` | ソース | launchApiForm |
| snacks.dart | `lib/widget/snacks.dart` | ソース | スナックバー通知 |
| sales_order_detail.dart | `lib/widget/order/sales_order_detail.dart` | ソース | 受注詳細（呼び出し元） |
