# 機能設計書 34-受注明細表示

## 概要

本ドキュメントは、InvenTreeモバイルアプリにおける受注明細行（Sales Order Line Item）表示機能の設計を定義する。受注明細は、受注に含まれる個々のパーツとその数量・引当・出荷状況を管理するための機能である。

### 本機能の処理概要

この機能では、受注に紐づく明細行を一覧表示し、各明細行の詳細（パーツ情報、引当状況、出荷状況）を確認・編集・在庫引当操作を行うことができる。

**業務上の目的・背景**：受注明細は、顧客への出荷物を正確に管理するための核心的な情報である。各明細行には出荷すべきパーツ、数量、引当済み数量、出荷済み数量が含まれる。この情報を通じて、在庫の引当作業、ピッキング、出荷準備を効率的に行うことができる。引当が完了しているか、出荷が完了しているかを視覚的に確認でき、受注処理の進捗管理を支援する。

**機能の利用シーン**：
- 受注に含まれるパーツと数量を確認したい場合
- 各明細行の引当・出荷状況を確認したい場合
- 明細行を編集したい場合
- 在庫を明細行に引き当てたい場合
- バーコードスキャンで在庫を引き当てたい場合

**主要な処理内容**：
1. 受注IDに紐づく明細行をAPIから取得
2. 各明細行のパーツ情報、進捗状況を表示
3. 明細行タップで詳細画面へ遷移
4. 詳細画面で引当操作、編集操作を提供
5. バーコードスキャンによる在庫引当

**関連システム・外部連携**：
- InvenTree REST API (`/api/order/so-line/`, `/api/order/so/{pk}/allocate/`)
- バーコードスキャン機能（SOAllocateStockHandler）

**権限による制御**：
- 明細行の編集権限がある場合のみ、編集ボタンが表示される
- 受注がOpenの場合のみ、引当操作が可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 29 | 販売注文詳細画面 | 遷移元画面 | ラインアイテムタブで受注明細一覧表示 |

## 機能種別

データ表示（CRUD操作のRead・Update）/ 一覧表示 / 詳細表示

## 入力仕様

### 入力パラメータ（一覧）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| filters | Map<String, String> | Yes | フィルタ条件（order: 受注ID） | order必須 |

### 入力パラメータ（詳細）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| item | InvenTreeSOLineItem | Yes | 対象の明細行オブジェクト | PKが1以上であること |

### 入力データソース

- 受注詳細画面からのフィルタ条件
- InvenTree REST API (`/api/order/so-line/`)

## 出力仕様

### 出力データ（一覧）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| part.name | String | パーツ名 |
| part.description | String | パーツの説明 |
| part.thumbnail | String | パーツサムネイル画像URL |
| progressString | String | 進捗文字列（"shipped / quantity"） |
| isComplete | bool | 完了フラグ |

### 出力データ（詳細）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| partName | String | パーツ名 |
| partImage | String | パーツ画像URL |
| availableStock | double | 利用可能在庫数 |
| allocated | double | 引当済み数量 |
| quantity | double | 注文数量 |
| shipped | double | 出荷済み数量 |
| reference | String | 参照番号 |
| notes | String | ノート |
| link | String | 外部リンク |

### 出力先

画面表示（一覧：ListTile、詳細：タイルリスト）

## 処理フロー

### 処理シーケンス（一覧）

```
1. ウィジェット初期化
   └─ PaginatedSOLineListがフィルタを受け取る
2. ページデータ取得
   └─ InvenTreeSOLineItem().listPaginated()でAPIリクエスト
3. 一覧表示
   └─ 各明細行をListTileとして描画
4. アイテムタップ時
   └─ 明細詳細画面へ遷移
```

### 処理シーケンス（詳細）

```
1. ウィジェット初期化
   └─ SoLineDetailWidgetが明細行オブジェクトを受け取る
2. データ読み込み
   ├─ item.reload()
   └─ 親受注の取得
3. 画面構築
   └─ 明細情報タイル表示
4. ユーザー操作
   ├─ 編集ボタン → 編集フォーム
   ├─ 引当ボタン → 引当フォーム
   └─ バーコード → 在庫引当
```

### フローチャート

```mermaid
flowchart TD
    A[画面表示開始] --> B[フィルタ条件受け取り]
    B --> C[APIからデータ取得]
    C --> D{データあり?}
    D -->|Yes| E[ListTileとして一覧表示]
    D -->|No| F[結果なしメッセージ表示]
    E --> G{ユーザー操作}
    G -->|アイテムタップ| H[item.reload]
    H --> I[SoLineDetailWidget表示]
    I --> J{詳細操作}
    J -->|パーツタップ| K[パーツ詳細画面]
    J -->|編集ボタン| L[編集フォーム]
    J -->|引当ボタン| M[引当フォーム]
    J -->|バーコード| N[在庫スキャン引当]
    G -->|スクロール| O[次ページ取得]
    O --> C
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-034-01 | 進捗色制御 | 完了（shipped >= quantity）の場合は緑、未完了は黄色 | 常時 |
| BR-034-02 | 編集ボタン表示 | 明細行の編集権限がある場合のみ表示 | canEdit == true |
| BR-034-03 | 引当ボタン表示 | 受注がOpenの場合のみ表示 | order.isOpen == true |
| BR-034-04 | パーツ編集制限 | 出荷済み数量がある場合、パーツフィールドは非表示 | shipped > 0 |
| BR-034-05 | バーコード引当 | APIサポートとorder.isOpen、作成権限が必要 | 複合条件 |
| BR-034-06 | ページサイズ | 1ページあたり25件を取得 | 常時 |

### 計算ロジック

```dart
// 引当比率
double allocatedRatio = allocated / quantity;

// 引当文字列
String allocatedString = "${allocated} / ${quantity}";

// 進捗比率
double progressRatio = shipped / quantity;

// 進捗文字列
String progressString = "${shipped} / ${quantity}";

// 未引当数量
double unallocatedQuantity = max(0, quantity - allocated);

// 完了判定
bool isComplete = shipped >= quantity;
bool isAllocated = allocated >= quantity;
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 一覧取得 | order_salesorderlineitem | SELECT | 受注IDで絞り込んだ明細行取得 |
| 詳細取得 | order_salesorderlineitem | SELECT | 明細行詳細情報取得 |
| 編集 | order_salesorderlineitem | UPDATE | 明細行情報更新 |
| 引当 | order_salesorderallocation | INSERT | 在庫引当レコード作成 |

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

#### order_salesorderlineitem

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, part_id, quantity, allocated, shipped, reference, notes, link | order_id = 指定受注ID | part_detail含む |
| UPDATE | quantity, reference, notes, link | フォーム入力値 | 編集時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | 認証エラー | APIトークンが無効 | ログイン画面へ遷移 |
| 403 | 権限エラー | 操作権限がない | エラーメッセージ表示 |
| 404 | 不存在エラー | 明細行が存在しない | 前画面へ戻る |
| 500 | サーバーエラー | サーバー側の問題 | リトライを促すメッセージ表示 |

### リトライ仕様

プルダウンによるリフレッシュ操作で再取得可能

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

- 一覧・詳細取得：読み取り専用
- 編集・引当：API側でトランザクション管理

## パフォーマンス要件

- 1ページあたり25件のデータを取得
- 詳細表示前にitem.reload()を実行
- ローディングオーバーレイで処理中を表示

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

- APIアクセスには認証トークンが必要
- 受注の閲覧権限（sales_order）が必要
- 編集操作には編集権限が必要
- 引当操作は受注のステータスに依存

## 備考

- 一覧はPaginatedSOLineListで実装
- 詳細はSoLineDetailWidgetで実装
- パーツが取得できない場合はエラーメッセージを表示

---

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

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

### 推奨読解順序

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

明細行のデータモデルを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | sales_order.dart | `lib/inventree/sales_order.dart` | InvenTreeSOLineItemクラスの構造（138-232行目） |

**読解のコツ**: InvenTreeSOLineItemはInvenTreeOrderLineを継承している。allocated, shipped, quantityプロパティから各種比率・文字列を計算する。allocateFormFields()は引当フォームのフィールド定義を返す。

#### Step 2: 一覧ウィジェットを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | so_line_list.dart | `lib/widget/order/so_line_list.dart` | PaginatedSOLineListの構造全体 |

**主要処理フロー**:
1. **17-26行目**: PaginatedSOLineListクラス定義
2. **31-43行目**: orderingOptionsの定義（part, quantity）
3. **47-59行目**: requestPage()メソッド。APIからページデータ取得
4. **61-94行目**: buildItem()メソッド。パーツ情報と進捗を表示

#### Step 3: 詳細ウィジェットを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | so_line_detail.dart | `lib/widget/order/so_line_detail.dart` | SoLineDetailWidgetの構造全体 |

**主要処理フロー**:
1. **24-31行目**: SoLineDetailWidgetクラス定義
2. **33-57行目**: appBarActions()。編集ボタン構築
3. **59-86行目**: _allocateStock()メソッド。引当フォーム表示
4. **88-105行目**: _editLineItem()メソッド。編集フォーム表示
5. **107-124行目**: actionButtons()。引当ボタン構築
6. **126-151行目**: barcodeButtons()。バーコード引当ボタン
7. **153-164行目**: request()。データ再読み込みと親受注取得
8. **166-261行目**: getTiles()。詳細タイル構築

#### Step 4: 引当フォームを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | so_line_detail.dart | `lib/widget/order/so_line_detail.dart` | _allocateStock()（59-86行目） |

**主要処理フロー**:
- **64行目**: allocateFormFields()でフォームフィールド取得
- **66-73行目**: フィルタ設定（line_item, stock_item, shipment）
- **75-85行目**: launchApiForm()でフォーム表示

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

```
PaginatedSOLineList
    │
    └─ _PaginatedSOLineListState
           ├─ orderingOptions → {part, quantity}
           ├─ requestPage() → InvenTreeSOLineItem().listPaginated()
           └─ buildItem()
                  ├─ item.part取得
                  ├─ progressString表示
                  └─ onTap → SoLineDetailWidget

SoLineDetailWidget
    │
    └─ _SOLineDetailWidgetState
           ├─ getAppBarTitle() → "Line Item"
           ├─ appBarActions() → 編集ボタン
           │      └─ _editLineItem() → editForm
           │
           ├─ request()
           │      ├─ item.reload()
           │      └─ InvenTreeSalesOrder().get()
           │
           ├─ actionButtons()
           │      └─ _allocateStock() → launchApiForm
           │
           ├─ barcodeButtons()
           │      └─ SOAllocateStockHandler
           │
           └─ getTiles()
                  ├─ パーツ情報 → InvenTreePart詳細
                  ├─ 利用可能在庫
                  ├─ 引当済み（ProgressBar）
                  ├─ 出荷済み（ProgressBar）
                  ├─ 参照番号
                  ├─ ノート
                  └─ 外部リンク
```

### データフロー図

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

フィルタ条件 ───────▶ PaginatedSOLineList
(order: pk)                 │
                            ▼
                    requestPage()
                            │
                            ▼
                    InvenTreeAPI
                    /api/order/so-line/
                    ?part_detail=true
                            │
                            ▼
                    buildItem()
                            │
                    ┌───────┴───────┐
                    ▼               ▼
            パーツ情報          進捗表示
                    │
                    ▼
            onTap → SoLineDetailWidget
                            │
                    ┌───────┴───────┐
                    ▼               ▼
            item.reload()     order取得
                            │
                            ▼
                    getTiles()
                            │
        ┌───────────────────┼───────────────────┐
        ▼                   ▼                   ▼
    パーツ情報          引当進捗            出荷進捗
    (タップ→詳細)      (ProgressBar)      (ProgressBar)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| so_line_list.dart | `lib/widget/order/so_line_list.dart` | ソース | 受注明細行一覧ウィジェット |
| so_line_detail.dart | `lib/widget/order/so_line_detail.dart` | ソース | 受注明細行詳細ウィジェット |
| sales_order.dart | `lib/inventree/sales_order.dart` | ソース | 受注・明細行データモデル |
| part.dart | `lib/inventree/part.dart` | ソース | パーツデータモデル |
| paginator.dart | `lib/widget/paginator.dart` | ソース | ページネーション共通処理 |
| refreshable_state.dart | `lib/widget/refreshable_state.dart` | ソース | リフレッシュ可能State基底クラス |
| progress.dart | `lib/widget/progress.dart` | ソース | プログレスバーウィジェット |
| api_form.dart | `lib/api_form.dart` | ソース | APIフォーム表示 |
| sales_order.dart | `lib/barcode/sales_order.dart` | ソース | 受注用バーコードハンドラ |
| helpers.dart | `lib/helpers.dart` | ソース | ヘルパー関数（simpleNumberString等） |
