# 機能設計書 32-受注一覧表示

## 概要

本ドキュメントは、InvenTreeモバイルアプリにおける受注（Sales Order）一覧表示機能の設計を定義する。受注一覧は、顧客からの注文を管理し、出荷・納品プロセスを追跡するための基盤となる機能である。

### 本機能の処理概要

この機能では、InvenTreeサーバーに登録された受注（Sales Order）をページネーション対応の一覧形式で表示し、フィルタリング・ソート・検索・新規作成を行うことができる。

**業務上の目的・背景**：受注管理は、顧客からの注文を正確に追跡し、適切なタイミングで出荷・納品を行うために不可欠である。受注一覧を通じて、未処理の注文、期限超過の注文、自分に割り当てられた注文を素早く把握することで、業務効率と顧客満足度を向上させることができる。

**機能の利用シーン**：
- 未処理の受注を確認したい場合
- 特定の顧客の受注を検索したい場合
- 新規受注を登録したい場合
- 期限超過の受注を確認してフォローアップしたい場合

**主要な処理内容**：
1. 受注ステータス情報をAPIから読み込み
2. フィルタ条件・ソート条件に基づいて受注リストを取得
3. 顧客名・参照番号・ステータス・合計金額を含む一覧を表示
4. 検索機能による受注の絞り込み
5. 受注タップで詳細画面へ遷移
6. 作成権限がある場合は新規受注作成ボタンを表示

**関連システム・外部連携**：InvenTree REST API (`/api/order/so/`)

**権限による制御**：受注の作成権限（`sales_order` ロールの`add`権限）がある場合のみ、新規作成ボタンが表示される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | ホーム画面 | 遷移元画面 | 受注タイル押下時に受注一覧画面へ遷移 |
| 2 | ドロワーメニュー | 遷移元画面 | 受注メニュー押下時に受注一覧画面へ遷移 |
| 3 | 検索画面 | 遷移元画面 | 受注検索結果押下時に受注一覧画面へ遷移 |
| 28 | 販売注文一覧画面 | 主機能 | 受注のページネーション付き一覧表示 |
| 29 | 販売注文詳細画面 | 遷移先画面 | 受注選択時に受注詳細画面へ遷移 |
| 31 | 会社詳細画面 | 遷移元画面 | 受注項目押下時に受注一覧画面へ遷移（顧客フィルタ） |

## 機能種別

データ表示（CRUD操作のRead）/ 一覧表示 / 新規作成（Create）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| filters | Map<String, String> | No | 初期フィルタ条件（customer等） | - |

### フィルタオプション

| フィルタ名 | ラベル | 説明 | デフォルト値 |
|-----------|--------|------|-------------|
| outstanding | Outstanding | 未完了の受注のみ表示 | true |
| overdue | Overdue | 期限超過の受注のみ表示 | null（未設定） |
| assigned_to_me | Assigned to Me | 自分に割り当てられた受注のみ | null（未設定） |

### ソートオプション

| フィールド名 | ラベル | 説明 |
|-------------|--------|------|
| reference | Reference | 参照番号順 |
| status | Status | ステータス順 |
| target_date | Target Date | 目標日順 |
| customer__name | Customer | 顧客名順 |

### 入力データソース

- InvenTree REST API (`/api/order/so/`)
- ユーザー入力（検索キーワード、フィルタ設定）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| reference | String | 受注参照番号 |
| description | String | 受注の説明 |
| customer.name | String | 顧客名 |
| customer.thumbnail | String | 顧客サムネイル画像URL |
| status | int | 受注ステータスコード |
| totalPrice | double | 合計金額 |
| totalPriceCurrency | String | 通貨コード |

### 出力先

画面表示（ListTileのリスト形式）

## 処理フロー

### 処理シーケンス

```
1. ウィジェット初期化
   └─ SalesOrderListWidgetが初期フィルタを受け取る
2. 画面構築
   └─ PaginatedSalesOrderListウィジェットを本体として構築
3. ステータス情報読み込み
   └─ InvenTreeAPI().SalesOrderStatus.load()
4. ページデータ取得
   └─ InvenTreeSalesOrder().listPaginated()でAPIリクエスト
5. 一覧表示
   └─ 各受注をListTileとして描画（顧客画像、参照番号、ステータス）
6. ユーザー操作
   └─ タップで詳細画面、新規作成ボタンでフォーム表示
```

### フローチャート

```mermaid
flowchart TD
    A[画面表示開始] --> B[フィルタ条件受け取り]
    B --> C[PaginatedSalesOrderList構築]
    C --> D{作成権限確認}
    D -->|権限あり| E[新規作成ボタン表示]
    D -->|権限なし| F[ボタン非表示]
    E --> G[SalesOrderStatus読み込み]
    F --> G
    G --> H[APIからデータ取得]
    H --> I{データあり?}
    I -->|Yes| J[ListTileとして一覧表示]
    I -->|No| K[結果なしメッセージ表示]
    J --> L{ユーザー操作}
    L -->|アイテムタップ| M[詳細画面へ遷移]
    L -->|新規作成| N[作成フォーム表示]
    L -->|フィルタ変更| O[フィルタ設定画面]
    L -->|検索入力| P[検索実行]
    L -->|スクロール| Q[次ページ取得]
    M --> R[終了]
    N --> R
    O --> H
    P --> H
    Q --> H
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-032-01 | 作成ボタン表示制御 | 受注の作成権限がある場合のみ新規作成ボタンを表示 | 常時 |
| BR-032-02 | デフォルトフィルタ | 初期表示では未完了（outstanding）の受注のみ表示 | フィルタ未設定時 |
| BR-032-03 | ステータス表示 | ステータスラベルと色はSalesOrderStatusから取得 | 常時 |
| BR-032-04 | 金額表示 | 合計金額が0より大きい場合のみ通貨記号付きで表示 | totalPrice > 0 |
| BR-032-05 | ページサイズ | 1ページあたり25件を取得 | 常時 |
| BR-032-06 | 顧客詳細取得 | customer_detail=trueで顧客情報を含めて取得 | 常時 |

### 計算ロジック

サブタイトル表示ロジック:
```dart
String subtitle = customer?.name ?? order.description;
if (order.totalPrice != null && order.totalPrice! > 0) {
  subtitle += " • ${order.totalPriceCurrency} ${order.totalPrice!.toStringAsFixed(2)}";
}
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 一覧取得 | order_salesorder | SELECT | フィルタ条件に基づく受注一覧取得 |
| 新規作成 | order_salesorder | INSERT | 新規受注レコード作成 |

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

#### order_salesorder

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, reference, description, status, target_date, customer_id, total_price, total_price_currency | フィルタ条件適用 | customer_detailで関連テーブルJOIN |
| INSERT | reference, customer_id, description, target_date, start_date, link, responsible_id, project_code_id | フォーム入力値 | 新規作成時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | 認証エラー | APIトークンが無効 | ログイン画面へ遷移 |
| 403 | 権限エラー | 閲覧権限がない | エラーメッセージ表示 |
| 500 | サーバーエラー | サーバー側の問題 | リトライを促すメッセージ表示 |

### リトライ仕様

- プルダウンによるリフレッシュ操作で再取得可能
- 検索入力は500msのデバウンス後に実行

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

- 一覧取得：読み取り専用、トランザクション管理不要
- 新規作成：API側でトランザクション管理

## パフォーマンス要件

- 1ページあたり25件のデータを取得
- 無限スクロールによる段階的データ読み込み
- 検索入力は500msのデバウンス処理
- ステータス情報は初回のみ読み込み

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

- APIアクセスには認証トークンが必要
- 受注の閲覧権限（sales_order）が必要
- 作成操作には受注の作成権限が必要
- 新規作成フォームでcontactフィールドは顧客確定後に設定可能

## 備考

- 新規作成時、contactフィールドは顧客が確定するまで設定できないため、初期フォームからは除外される
- 会社詳細画面からの遷移時は、customerフィルタが自動的に設定される

---

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

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

### 推奨読解順序

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

受注のデータモデルを理解する。

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

**読解のコツ**: InvenTreeSalesOrderはInvenTreeOrderを継承している。customerプロパティはcustomer_detailから生成されたInvenTreeCompanyオブジェクトを返す。ステータス判定にはapi.SalesOrderStatusを使用する。

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

一覧画面の構成を把握する。

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

**主要処理フロー**:
1. **15-23行目**: SalesOrderListWidgetクラス定義。フィルタを受け取る
2. **33-49行目**: actionButtons()メソッド。作成権限確認と新規作成ボタン構築
3. **52-71行目**: _createSalesOrder()メソッド。新規作成フォームの表示処理

#### Step 3: ページネーション処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | sales_order_list.dart | `lib/widget/order/sales_order_list.dart` | PaginatedSalesOrderListクラス（85-176行目） |

**主要処理フロー**:
- **101-110行目**: orderingOptions。ソートオプションの定義
- **112-130行目**: filterOptions。フィルタオプションの定義
- **132-146行目**: requestPage()メソッド。ステータス読み込みとAPI呼び出し
- **148-175行目**: buildItem()メソッド。ListTile構築（顧客情報、金額表示含む）

#### Step 4: 受注モデルの詳細を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | sales_order.dart | `lib/inventree/sales_order.dart` | formFields()とステータス判定メソッド |

**主要処理フロー**:
- **47-78行目**: formFields()。新規作成フォームのフィールド定義
- **80-83行目**: defaultFilters()。customer_detail=trueの設定
- **120-133行目**: isOpen, isPending, isInProgress, isComplete。ステータス判定

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

```
SalesOrderListWidget
    │
    ├─ _SalesOrderListWidgetState
    │      ├─ getAppBarTitle() → "Sales Orders"
    │      ├─ actionButtons() → 新規作成ボタン構築
    │      │      └─ _createSalesOrder() → フォーム表示
    │      │             └─ InvenTreeSalesOrder().createForm()
    │      └─ getBody() → PaginatedSalesOrderList
    │
    └─ PaginatedSalesOrderList
           └─ _PaginatedSalesOrderListState
                  ├─ orderingOptions → ソート設定
                  ├─ filterOptions → フィルタ設定
                  ├─ requestPage()
                  │      ├─ InvenTreeAPI().SalesOrderStatus.load()
                  │      └─ InvenTreeSalesOrder().listPaginated()
                  └─ buildItem()
                         ├─ order.customer取得
                         ├─ subtitle構築（顧客名、金額）
                         ├─ ステータスラベル・色取得
                         └─ order.goToDetailPage() → 詳細画面遷移
```

### データフロー図

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

フィルタ条件 ───────▶ SalesOrderListWidget ───▶ アプリバータイトル
    │                       │
    │                       ▼
    │               PaginatedSalesOrderList
    │                       │
    │                       ├─ フィルタ/ソート設定
    │                       │
    ▼                       ▼
検索キーワード ────▶ requestPage()
                            │
                            ├─ SalesOrderStatus.load()
                            │
                            ▼
                    InvenTreeAPI
                    /api/order/so/
                    ?customer_detail=true
                            │
                            ▼
                    InvenTreePageResponse
                            │
                            ▼
                    buildItem()
                            │
                            ├─ 顧客サムネイル
                            ├─ 参照番号
                            ├─ 顧客名 + 金額
                            └─ ステータスラベル
                            │
                            ▼
                    ListTile一覧表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| sales_order_list.dart | `lib/widget/order/sales_order_list.dart` | ソース | 受注一覧ウィジェット |
| sales_order.dart | `lib/inventree/sales_order.dart` | ソース | 受注データモデル |
| company.dart | `lib/inventree/company.dart` | ソース | 会社（顧客）データモデル |
| paginator.dart | `lib/widget/paginator.dart` | ソース | ページネーション共通処理 |
| refreshable_state.dart | `lib/widget/refreshable_state.dart` | ソース | リフレッシュ可能State基底クラス |
| sales_order_detail.dart | `lib/widget/order/sales_order_detail.dart` | ソース | 受注詳細画面 |
| api.dart | `lib/api.dart` | ソース | API通信・ステータス管理 |
| l10.dart | `lib/l10.dart` | ソース | 多言語対応文字列 |
