# 画面設計書 26-購入注文一覧画面

## 概要

本ドキュメントは、InvenTree Flutterアプリケーションにおける「購入注文一覧画面」の設計仕様を定義する。購入注文（PurchaseOrder）をページネーション付きで一覧表示し、検索・フィルタリング・ソート機能を提供する画面である。

### 本画面の処理概要

購入注文一覧画面は、InvenTreeサーバーに登録されている購入注文をページネーション付きの一覧形式で表示する画面である。ユーザーはこの画面から注文を検索・フィルタリングし、目的の注文を素早く見つけて詳細画面へ遷移できる。また、新規注文の作成やバーコードスキャンによる受入処理も可能。

**業務上の目的・背景**：購買管理において、発注した注文の状況を一元的に把握することは極めて重要である。本画面は、未処理の注文、期限超過の注文、自分に割り当てられた注文などを効率的にフィルタリングし、購買業務の進捗管理を支援する。ステータス別の色分け表示により、注文状況を視覚的に把握できる。

**画面へのアクセス方法**：
- ホーム画面の「購入注文」タイルをタップ
- ドロワーメニューの「発注」をタップ
- 検索画面から購入注文検索結果として遷移
- 会社詳細画面からサプライヤーの注文一覧として遷移

**主要な操作・処理内容**：
1. 購入注文一覧のページネーション表示（25件ずつ取得）
2. 検索ボックスによるインクリメンタルサーチ（500msデバウンス）
3. フィルタリング（未処理、期限超過、自分に割当）
4. ソート（リファレンス、サプライヤー名、ステータス、目標日）
5. 購入注文タップで詳細画面へ遷移
6. 新規購入注文の作成
7. バーコードスキャンによる発注品受入

**画面遷移**：
- 遷移元：ホーム画面、ドロワーメニュー、検索画面、会社詳細画面
- 遷移先：購入注文詳細画面（PurchaseOrderDetailWidget）

**権限による表示制御**：
- 購入注文の閲覧権限（purchase_order）がない場合、この画面にはアクセスできない
- 新規作成ボタンは `InvenTreePurchaseOrder().canCreate` がtrueの場合のみ表示
- バーコード受入は `api.supportsBarcodePOReceiveEndpoint` がtrueの場合のみ表示

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 23 | 発注一覧表示 | 主機能 | 発注のページネーション付き一覧表示 |
| 24 | 発注詳細表示 | 遷移先機能 | 発注選択時に発注詳細画面へ遷移 |

## 画面種別

一覧

## URL/ルーティング

Flutter Navigator によるプッシュ遷移。`PurchaseOrderListWidget` ウィジェットのインスタンス化時に `filters` パラメータを渡して表示内容を制御する。

## 入出力項目

| 項目名 | 入出力 | 型 | 必須 | 説明 |
|--------|--------|------|------|------|
| filters | 入力 | Map<String, String> | No | 一覧表示時のフィルタ条件（デフォルト: 空） |

## 表示項目

| 項目名 | 表示位置 | データ型 | 説明 |
|--------|----------|----------|------|
| サプライヤーサムネイル | ListTile.leading | Image | サプライヤー会社の画像 |
| リファレンス | ListTile.title | String | 購入注文のリファレンス番号 |
| 説明 | ListTile.subtitle | String | 購入注文の説明文 |
| ステータス | ListTile.trailing | String | 注文ステータス（色付き） |
| 検索結果件数 | タイトル下 | String | "Results: N" 形式で表示 |

## イベント仕様

### 1-購入注文タップ

購入注文リストの各アイテムをタップすると、該当注文の詳細画面（PurchaseOrderDetailWidget）へ遷移する。

**処理フロー**：
1. `onTap` コールバックが発火
2. `order.goToDetailPage(context)` を呼び出し
3. `Navigator.push` で `PurchaseOrderDetailWidget(this)` へ遷移

### 2-新規作成ボタン押下

SpeedDialの「作成」ボタンをタップすると、新規購入注文作成フォームが表示される。

**処理フロー**：
1. `SpeedDialChild.onTap` で `_createPurchaseOrder(context)` を呼び出し
2. `InvenTreePurchaseOrder().createForm()` で作成フォームを表示
3. contactフィールドは除外（サプライヤー選択後に設定）
4. 成功時に作成された注文詳細画面へ遷移

### 3-バーコードスキャン（発注品受入）

SpeedDialのバーコードスキャンボタンをタップすると、発注品受入スキャンモードが起動する。

**処理フロー**：
1. `SpeedDialChild.onTap` で `scanBarcode()` を呼び出し
2. `POReceiveBarcodeHandler()` をハンドラとして設定

### 4-検索ボックス入力

検索ボックスにテキストを入力すると、500msのデバウンス後にサーバーへ検索クエリを送信する。

**処理フロー**：
1. `TextFormField.onChanged` でテキスト変更を検知
2. `updateSearchTerm()` メソッドを呼び出し
3. 500msタイマー後に `_pagingController.refresh()` で再検索

### 5-フィルタリング設定

フィルタアイコンをタップすると、ソートとフィルタリングのオプションダイアログが表示される。

**処理フロー**：
1. フィルタアイコン（`Icons.filter_alt`）をタップ
2. `_setOrderingOptions(context)` を呼び出し
3. `launchApiForm` でオプションダイアログを表示
4. 設定保存後に `_pagingController.refresh()` で一覧を再取得

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 一覧表示 | PurchaseOrder | SELECT | 購入注文データのページネーション取得 |
| 検索 | PurchaseOrder | SELECT | 検索条件に合致する注文の取得 |
| 新規作成 | PurchaseOrder | INSERT | 新規購入注文の作成 |

### テーブル別更新項目詳細

#### PurchaseOrder テーブル

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | pk, reference, description, supplier, supplier_detail, status | フィルタ条件とページネーションパラメータに基づく | APIエンドポイント: /api/order/po/ |
| INSERT | reference, supplier, description, project_code, destination, start_date, target_date, link, responsible | フォーム入力値 | createForm()経由 |

## メッセージ仕様

| メッセージID | メッセージ種別 | 表示条件 | メッセージ内容 |
|-------------|---------------|----------|---------------|
| MSG_NO_RESULTS | 情報 | 検索結果が0件 | "No results" |
| MSG_RESULTS | 情報 | 検索結果表示 | "Results: {件数}" |

## 例外処理

| 例外条件 | 処理内容 | ユーザーへの表示 |
|---------|---------|-----------------|
| APIエラー | エラーをSentryに報告 | 一覧表示が停止、エラー状態を表示 |
| ネットワークエラー | 再試行可能な状態を維持 | プルダウンでリフレッシュ可能 |
| 権限不足 | 作成ボタン非表示 | 閲覧のみ可能 |

## 備考

- ページサイズは25件固定（`_pageSize = 25`）
- フィルタ・ソート設定は `InvenTreeSettingsManager` によりローカルに永続化される
- 検索のデバウンス時間は500ms
- ステータスコードはAPI取得時に `InvenTreeAPI().PurchaseOrderStatus.load()` で事前ロード
- ステータスによる色分け表示: `InvenTreeAPI().PurchaseOrderStatus.color(order.status)`
- デフォルトフィルタで `outstanding: true` が設定されている（未処理注文を優先表示）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | purchase_order.dart | `lib/inventree/purchase_order.dart` | InvenTreePurchaseOrderクラス（18-157行）。reference, description, supplier, status, isOpen, isPending等のゲッター |
| 1-2 | orders.dart | `lib/inventree/orders.dart` | InvenTreeOrder基底クラス。共通フィールドと機能 |

**読解のコツ**: `InvenTreePurchaseOrder` は `InvenTreeOrder` を継承している。URL="order/po/" でAPIエンドポイントが定義されている。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | purchase_order_list.dart | `lib/widget/order/purchase_order_list.dart` | PurchaseOrderListWidget（19-101行）とPaginatedPurchaseOrderList（103-187行）の関係 |

**主要処理フロー**:
1. **19-28行**: `PurchaseOrderListWidget` StatefulWidgetの定義。filtersをコンストラクタで受け取る
2. **35行**: `getAppBarTitle()` で "Purchase Orders" を返す
3. **98-100行**: `getBody()` で `PaginatedPurchaseOrderList` を返す
4. **103-187行**: `_PaginatedPurchaseOrderListState` で実際のページネーションロジックを実装

#### Step 3: フィルタ・ソートオプションを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | purchase_order_list.dart | `lib/widget/order/purchase_order_list.dart` | orderingOptions（123-128行）とfilterOptions（130-148行）の定義 |

**主要処理フロー**:
- **123-128行**: ソートオプション（reference, supplier__name, status, target_date）
- **131-148行**: フィルタオプション（outstanding, overdue, assigned_to_me）
- **136-137行**: outstandingのデフォルト値がtrue

#### Step 4: リストアイテム構築を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | purchase_order_list.dart | `lib/widget/order/purchase_order_list.dart` | buildItem()メソッド（166-186行）。ListTileの構築方法 |

**主要処理フロー**:
- **168行**: InvenTreeModelをInvenTreePurchaseOrderにキャスト
- **170行**: サプライヤー情報を取得
- **172-185行**: ListTileを構築。trailingにステータス色付きでステータスラベルを表示

#### Step 5: 新規作成機能を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | purchase_order_list.dart | `lib/widget/order/purchase_order_list.dart` | actionButtons()（37-54行）と_createPurchaseOrder()（56-76行） |

**主要処理フロー**:
- **41-51行**: canCreate時のみSpeedDialChildを追加
- **58行**: formFields()でフォームフィールドを取得
- **61行**: contactフィールドを除外
- **63-75行**: createForm()で作成フォームを表示、成功時に詳細画面へ遷移

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

```
PurchaseOrderListWidget (StatefulWidget)
    │
    └─ _PurchaseOrderListWidgetState (RefreshableState)
           │
           ├─ getBody() → PaginatedPurchaseOrderList
           │                    │
           │                    └─ _PaginatedPurchaseOrderListState (PaginatedSearchState)
           │                           │
           │                           ├─ requestPage()
           │                           │      │
           │                           │      ├─ PurchaseOrderStatus.load()
           │                           │      └─ InvenTreePurchaseOrder().listPaginated()
           │                           │                      │
           │                           │                      └─ API GET /api/order/po/
           │                           │
           │                           └─ buildItem() → ListTile
           │                                              │
           │                                              └─ onTap → order.goToDetailPage()
           │
           ├─ actionButtons()
           │      │
           │      └─ _createPurchaseOrder() → InvenTreePurchaseOrder().createForm()
           │
           └─ barcodeButtons()
                  │
                  └─ POReceiveBarcodeHandler
```

### データフロー図

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

filters (Map)    ───▶ PaginatedPurchaseOrderList          ───▶ ListView
                         │
                         ├─ requestPage()
                         │      │
                         │      ├─ PurchaseOrderStatus.load()
                         │      │
                         │      └─ API: /api/order/po/?limit=25&offset=N
                         │             │
                         │             └─ InvenTreePageResponse
                         │
                         └─ buildItem()
                                │
                                └─ InvenTreePurchaseOrder → ListTile
                                         │
                                         ├─ supplier.thumbnail → leading
                                         └─ PurchaseOrderStatus.color() → trailing色
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| purchase_order_list.dart | `lib/widget/order/purchase_order_list.dart` | ソース | 購入注文一覧画面のメインウィジェット |
| paginator.dart | `lib/widget/paginator.dart` | ソース | ページネーション共通基盤クラス |
| purchase_order.dart | `lib/inventree/purchase_order.dart` | ソース | 購入注文データモデル |
| orders.dart | `lib/inventree/orders.dart` | ソース | 注文基底クラス |
| company.dart | `lib/inventree/company.dart` | ソース | 会社データモデル（サプライヤー） |
| api.dart | `lib/api.dart` | ソース | API通信クライアント、PurchaseOrderStatusステータス管理 |
| refreshable_state.dart | `lib/widget/refreshable_state.dart` | ソース | リフレッシュ可能なState基底クラス |
| barcode.dart | `lib/barcode/barcode.dart` | ソース | バーコードスキャン基盤 |
| purchase_order.dart | `lib/barcode/purchase_order.dart` | ソース | 発注関連バーコードハンドラ |
| purchase_order_detail.dart | `lib/widget/order/purchase_order_detail.dart` | ソース | 購入注文詳細（遷移先） |
