# 機能設計書 47-発注受入スキャン

## 概要

本ドキュメントは、InvenTreeアプリにおけるバーコードスキャンを使用した発注品受入機能の設計を記述する。サプライヤーバーコードをスキャンして、発注品を在庫として受け入れる機能である。

### 本機能の処理概要

発注受入スキャン機能は、サプライヤーが付与したバーコードをスキャンして、発注（Purchase Order）に含まれる品目を在庫として受け入れる機能である。バーコードに含まれる情報（発注番号、数量等）を解析し、適切な受入処理を実行する。

**業務上の目的・背景**：入荷検品業務において、サプライヤーから届いた部品をシステムに登録する作業は時間がかかる上、入力ミスが発生しやすい。バーコードスキャンによる受入処理は、作業時間を短縮し、正確性を向上させる。特に大量の入荷がある場合、連続スキャンによる効率的な受入処理が業務効率に大きく貢献する。

**機能の利用シーン**：
- サプライヤーからの入荷時に、梱包ラベルをスキャンして受入処理
- 発注詳細画面から受入スキャンを起動し、明細行を選択して受入
- ロケーション画面から入荷品を直接受入してロケーションに登録
- 部分入荷時の数量確認と受入処理

**主要な処理内容**：
1. POReceiveBarcodeHandlerによるバーコードスキャン
2. barcode/po-receive/エンドポイントへのバーコード送信
3. サーバーレスポンスの解析（明細行情報、数量、ロケーション等）
4. 確認設定に応じた自動受入/確認フォーム表示
5. 受入処理の実行と結果通知

**関連システム・外部連携**：
- InvenTree REST API（barcode/po-receive/エンドポイント）
- InvenTree REST API（stock/エンドポイント - 在庫作成）

**権限による制御**：発注の受入権限（purchase_order.add）が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 27 | 購入注文詳細画面 | 遷移元画面 | バーコードスキャンで発注品を受け入れ |
| 23 | 在庫ロケーション画面 | 遷移元画面 | バーコードスキャンで発注品を受け入れ |
| 5 | バーコードスキャン画面 | 主画面 | サプライヤーバーコードのスキャン |

## 機能種別

データ登録 / バーコード連携 / 発注管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| purchaseOrder | InvenTreePurchaseOrder | No | 対象の発注（指定時はその発注に限定） | pk > 0 |
| location | InvenTreeStockLocation | No | 受入先のロケーション | pk > 0 |
| lineItem | InvenTreePOLineItem | No | 対象の明細行 | pk > 0 |
| barcode | String | Yes | スキャンされたバーコード | 空でないこと |

### 入力データソース

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

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 受入結果 | bool | 発注受入の成功/失敗 |
| 通知メッセージ | String | 成功時: receivedItem、失敗時: エラーメッセージ |
| 作成された在庫 | InvenTreeStockItem | 受入により作成された在庫アイテム |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. 発注受入スキャンの起動
   ├─ POReceiveBarcodeHandler(purchaseOrder, location, lineItem)でハンドラ生成
   └─ scanBarcodeでスキャン画面起動

2. バーコード処理（processBarcode）
   ├─ INV_PO_CONFIRM_SCAN設定を取得
   ├─ 追加データ構築
   │   ├─ purchase_order: 発注ID
   │   ├─ location: ロケーションID
   │   ├─ line_item: 明細行ID
   │   └─ auto_allocate: !confirm（自動割当フラグ）
   └─ barcode/po-receive/エンドポイントへPOST

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

4. 追加情報入力（onBarcodeUnhandled）
   ├─ 明細行IDを抽出
   ├─ InvenTreePOLineItem().get()で明細行取得
   ├─ 数量・ロケーション・バーコード情報を抽出
   ├─ スキャン画面を閉じる
   └─ lineItem.receive()で受入フォーム表示

5. 受入処理
   ├─ 成功時: receivedItemスナックバー
   └─ 失敗時: エラースナックバー
```

### フローチャート

```mermaid
flowchart TD
    A[発注受入スキャン開始] --> B[POReceiveBarcodeHandler生成]
    B --> C[バーコードスキャン]
    C --> D[processBarcode]
    D --> E[INV_PO_CONFIRM_SCAN取得]
    E --> F[barcode/po-receive/ API呼び出し]
    F --> G{レスポンス}
    G -->|success/lineitem| H[onBarcodeMatched]
    G -->|error| I[onBarcodeUnknown]
    G -->|action_required| J[onBarcodeUnhandled]
    H --> K[receivedItem通知]
    I --> L[エラースナックバー]
    J --> M[明細行ID抽出]
    M --> N[POLineItem.get]
    N --> O[スキャン画面を閉じる]
    O --> P[lineItem.receive表示]
    P --> Q{ユーザー確認}
    Q -->|確定| R[受入実行]
    Q -->|キャンセル| S[終了]
    R --> T{成功?}
    T -->|Yes| U[receivedItem通知]
    T -->|No| V[エラー通知]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-47-01 | 確認設定 | INV_PO_CONFIRM_SCANがfalseの場合は自動受入 | 受入処理時 |
| BR-47-02 | 発注限定 | purchaseOrder指定時はその発注の明細行のみ受入可能 | 照合時 |
| BR-47-03 | ロケーション指定 | location指定時はそのロケーションに受入 | 受入処理時 |
| BR-47-04 | 追加情報要求 | サーバーがaction_requiredを返した場合は入力フォーム表示 | レスポンス処理時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| バーコード照合 | PurchaseOrder, POLineItem | SELECT | バーコードから発注/明細行特定 |
| 在庫作成 | StockItem | INSERT | 受入により在庫アイテム作成 |
| 明細行更新 | POLineItem | UPDATE | 受入数量の更新 |
| 履歴記録 | StockItemTracking | INSERT | 受入履歴の記録 |

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

#### POLineItem

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | received | received + 受入数量 | 受入処理時 |

#### StockItem

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | part, location, quantity, purchase_order | 受入情報 | 新規在庫作成 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| E-47-01 | バーコード不一致 | バーコードが発注に関連付けられていない | エラースナックバー |
| E-47-02 | 明細行取得失敗 | POLineItem.get()がnullを返却 | 失敗音、処理中断 |
| E-47-03 | データ欠損 | lineitem応答にpkがない | missingDataスナックバー |
| E-47-04 | 受入失敗 | receive APIが失敗 | エラースナックバー |

### リトライ仕様

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

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

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

## パフォーマンス要件

- バーコード照合: 1秒以内
- 受入処理: 2秒以内

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

- 認証トークンを使用したAPIアクセス
- purchase_order.add権限による受入操作の制御

## 備考

- APIバージョン139以上でbarcode/po-receive/エンドポイントをサポート
- POAllocateBarcodeHandlerは発注への明細行追加用（別機能）

---

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

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

### 推奨読解順序

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | purchase_order.dart | `lib/barcode/purchase_order.dart` | POReceiveBarcodeHandler（22-126行目） |

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

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

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

**主要処理フロー**:
1. **23行目**: コンストラクタでpurchaseOrder, location, lineItemを受け取る（すべてオプション）
2. **30行目**: getOverlayTextでbarcodeReceivePartを返す
3. **33-51行目**: processBarcode - 確認設定取得、追加データ構築、API呼び出し

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

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

**主要処理フロー**:
- **55-62行目**: onBarcodeMatched - lineitemまたはsuccessで成功通知
- **65-116行目**: onBarcodeUnhandled - action_required時の追加情報入力処理
- **70-75行目**: lineItemデータの検証
- **78-91行目**: lineItem IDを取得、nullの場合は失敗音で終了
- **96-100行目**: 数量、destination、barcodeの抽出
- **102-105行目**: スキャン画面を閉じる
- **107-115行目**: lineItem.receive()で受入フォーム表示

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | purchase_order.dart | `lib/barcode/purchase_order.dart` | POAllocateBarcodeHandler（131-205行目） |

**主要処理フロー**:
- **131-134行目**: コンストラクタでpurchaseOrderを受け取る
- **140-148行目**: processBarcode - barcode/po-allocate/エンドポイントを使用
- **151-196行目**: onBarcodeMatched - サプライヤーパーツを取得し、明細行追加フォーム表示

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

```
POReceiveBarcodeHandler(purchaseOrder, location, lineItem)
    │
    ├─ BarcodeHandler (基底)
    │
    ├─ getOverlayText() → "barcodeReceivePart"
    │
    ├─ processBarcode(barcode)
    │      │
    │      ├─ INV_PO_CONFIRM_SCAN取得
    │      │
    │      ├─ po_extra_data構築
    │      │      ├─ purchase_order: purchaseOrder?.pk
    │      │      ├─ location: location?.pk
    │      │      ├─ line_item: lineItem?.pk
    │      │      └─ auto_allocate: !confirm
    │      │
    │      └─ super.processBarcode(barcode, url: "barcode/po-receive/")
    │
    ├─ onBarcodeMatched(data)
    │      │
    │      ├─ lineitem/success検出
    │      │      └─ barcodeSuccess(receivedItem)
    │      │
    │      └─ それ以外
    │             └─ onBarcodeUnknown(data)
    │
    └─ onBarcodeUnhandled(data)
           │
           ├─ action_required + lineitem検証
           │
           ├─ lineItemId抽出
           │
           ├─ InvenTreePOLineItem().get(lineItemId)
           │
           ├─ 数量/destination/barcode抽出
           │
           ├─ OneContext().pop() [スキャン画面終了]
           │
           └─ lineItem.receive(context, destination, quantity, barcode)
                  │
                  └─ onSuccess: showSnackIcon(receivedItem)
```

### データフロー図

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

発注詳細画面
or ロケーション画面
        │
        ▼
POReceiveBarcodeHandler生成
(purchaseOrder, location, lineItem)
        │
        ▼
バーコードスキャン
        │
        ▼
processBarcode()
        │
        ├─ INV_PO_CONFIRM_SCAN取得
        │
        ├─ po_extra_data構築
        │
        └─ barcode/po-receive/ API
                  │
                  ▼
           APIResponse
                  │
        ┌─────────┼─────────┐
        ▼         ▼         ▼
    success   action_req   error
        │         │         │
        ▼         ▼         ▼
  receivedItem  lineItem  エラー表示
    通知      .receive()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| purchase_order.dart | `lib/barcode/purchase_order.dart` | ソース | POReceiveBarcodeHandler、POAllocateBarcodeHandler |
| handler.dart | `lib/barcode/handler.dart` | ソース | BarcodeHandler基底クラス |
| barcode.dart | `lib/barcode/barcode.dart` | ソース | scanBarcode、barcodeSuccess |
| tones.dart | `lib/barcode/tones.dart` | ソース | 成功/失敗音の再生 |
| purchase_order.dart | `lib/inventree/purchase_order.dart` | ソース | InvenTreePurchaseOrder、POLineItemモデル |
| preferences.dart | `lib/preferences.dart` | ソース | INV_PO_CONFIRM_SCAN設定 |
| snacks.dart | `lib/widget/snacks.dart` | ソース | スナックバー通知 |
| purchase_order_detail.dart | `lib/widget/order/purchase_order_detail.dart` | ソース | 発注詳細（呼び出し元） |
| location_display.dart | `lib/widget/stock/location_display.dart` | ソース | ロケーション画面（呼び出し元） |
