# 機能設計書 4-パーツ一覧表示

## 概要

本ドキュメントは、InvenTreeモバイルアプリにおけるパーツ一覧表示機能の設計を記載する。パーツカテゴリ別にパーツを一覧表示し、ページネーション・フィルタリング・ソートに対応した機能を提供する。

### 本機能の処理概要

InvenTreeサーバーに登録されたパーツ（部品）を一覧形式で表示する機能である。大量のパーツデータを効率的に表示するためのページネーション機能、条件に応じたフィルタリング機能、各種ソートオプションを提供する。

**業務上の目的・背景**：在庫管理業務において、登録されているパーツを素早く検索・閲覧することは基本的な要件である。カテゴリ階層をナビゲートしながら目的のパーツを見つけたり、フィルタ条件を組み合わせて特定のパーツ群を抽出したりする必要がある。

**機能の利用シーン**：パーツカテゴリ画面での一覧表示、検索結果としてのパーツ一覧表示、パーツ詳細画面からのバリアント一覧表示、在庫画面からの関連パーツ一覧表示などで利用される。

**主要な処理内容**：
1. ページネーション付きパーツ一覧の取得
2. 検索クエリによるフィルタリング
3. 各種条件フィルタ（アクティブ、アセンブリ、コンポーネント等）
4. ソート順の変更（名前、在庫数、IPN）
5. サブカテゴリを含む/含まないの切り替え
6. パーツ詳細画面への遷移

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

**権限による制御**：partロールのview権限が必要。権限がない場合はパーツ一覧が表示されない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | ホーム画面 | 遷移元画面 | パーツタイル押下時にパーツカテゴリ画面へ遷移 |
| 2 | ドロワーメニュー | 遷移元画面 | パーツメニュー押下時にパーツカテゴリ画面へ遷移 |
| 3 | 検索画面 | 遷移元画面 | パーツ検索結果押下時にパーツ一覧画面へ遷移 |
| 20 | 部品カテゴリ画面 | 主画面 | カテゴリ内パーツ一覧のタブ表示 |
| 21 | 部品一覧画面 | 主画面 | パーツのページネーション付き一覧表示 |
| 22 | 部品詳細画面 | 遷移先画面 | パーツ選択時にパーツ詳細画面へ遷移 |

## 機能種別

データ参照 / 一覧表示

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| filters | Map<String, String> | No | フィルタ条件 | - |
| category | int/String | No | カテゴリID（"null"でトップレベル） | 整数または"null" |
| cascade | bool | No | サブカテゴリを含むか（デフォルト: true） | true/false |
| active | bool/null | No | アクティブ状態フィルタ | true/false/null（三値） |
| assembly | bool | No | アセンブリフィルタ | true/false |
| component | bool | No | コンポーネントフィルタ | true/false |
| is_template | bool | No | テンプレートフィルタ | true/false |
| trackable | bool | No | 追跡可能フィルタ | true/false |
| virtual | bool | No | 仮想パーツフィルタ | true/false |
| has_stock | bool | No | 在庫ありフィルタ | true/false |
| search | String | No | 検索クエリ | - |
| ordering | String | No | ソート順 | name/-name/in_stock/-in_stock/IPN/-IPN |
| limit | int | No | 1ページあたりの件数（デフォルト: 25） | 正の整数 |
| offset | int | No | 取得開始位置 | 0以上の整数 |

### 入力データソース

- カテゴリ画面から渡されるカテゴリフィルタ
- UI上のフィルタ選択
- 検索バーからの検索クエリ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| parts | List<InvenTreePart> | パーツオブジェクトのリスト |
| count | int | 総件数（ページネーション用） |
| next | String? | 次ページURL |
| previous | String? | 前ページURL |

### 出力先

- UI（ListViewによる一覧表示）

## 処理フロー

### 処理シーケンス

```
1. 一覧画面表示
   └─ 初期フィルタ条件の設定

2. API呼び出し（listPaginated）
   └─ フィルタパラメータの構築
   └─ part/エンドポイントへGETリクエスト
   └─ レスポンスのパース

3. 一覧表示
   └─ ListTileとして各パーツを表示
   └─ サムネイル、名前、説明、在庫数の表示

4. スクロール処理
   └─ 次ページのプリフェッチ
   └─ 無限スクロールによる追加読み込み

5. フィルタ/ソート変更
   └─ 条件変更時に再取得
   └─ offsetをリセットして最初から表示
```

### フローチャート

```mermaid
flowchart TD
    A[一覧画面表示] --> B[初期フィルタ設定]
    B --> C[API呼び出し]
    C --> D[レスポンス受信]
    D --> E{データあり?}
    E -->|No| F[空メッセージ表示]
    E -->|Yes| G[一覧表示]
    G --> H{ユーザー操作}
    H -->|スクロール| I{次ページあり?}
    I -->|Yes| J[次ページ取得]
    J --> K[一覧に追加]
    K --> H
    I -->|No| H
    H -->|フィルタ変更| L[offset=0]
    L --> C
    H -->|ソート変更| L
    H -->|タップ| M[詳細画面へ遷移]
    H -->|検索| N[検索クエリ設定]
    N --> L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | デフォルトサブカテゴリ含む | cascadeはデフォルトtrue | 一覧表示時 |
| BR-02 | デフォルトアクティブのみ | activeはデフォルトtrue | 一覧表示時 |
| BR-03 | 在庫数表示 | 利用可能在庫/総在庫の形式で表示 | パーツ表示時 |
| BR-04 | カテゴリ詳細含む | category_detail=trueをデフォルト | API呼び出し時 |

### 計算ロジック

**在庫数表示ロジック**:
```
if (unallocatedStock != inStock) {
  display = "${unallocatedStock} / ${inStock}"
} else {
  display = "${inStock}"
}
if (units.isNotEmpty) {
  display += " ${units}"
}
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 一覧取得 | - | - | サーバーAPIからのみ取得（ローカルDB操作なし） |

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

ローカルデータベース操作なし。サーバーAPIからのデータ取得のみ。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401/403 | 認証/権限エラー | partロールなし | エラーメッセージ表示 |
| 500+ | サーバーエラー | サーバー内部エラー | エラーメッセージ表示、リトライ可能 |
| - | 通信エラー | ネットワーク切断 | 接続エラーメッセージ表示 |

### リトライ仕様

- プルリフレッシュによる手動リトライ
- フィルタ/ソート変更時に自動再取得

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

- 読み取り専用（トランザクションなし）

## パフォーマンス要件

- 1ページあたり25件がデフォルト
- 無限スクロールによる段階的読み込み
- サムネイル画像はキャッシュ利用

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

- partロールのview権限が必要
- 認証トークンによるAPI認証

## 備考

- フィルタオプションは折りたたみ可能なUIで提供
- サムネイル画像はCachedNetworkImageでキャッシュされる

---

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

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

### 推奨読解順序

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

まず、パーツデータモデルを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | part.dart | `lib/inventree/part.dart` | InvenTreePartクラス（138-459行目） |

**読解のコツ**:
- **144行目**: URL定義（"part/"）
- **190-214行目**: `defaultFilters` - category_detail=trueの設定
- **302-341行目**: 在庫数関連プロパティ（inStock, unallocatedStock, stockString）
- **408-423行目**: fullname生成ロジック

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

ページネーション付き一覧ウィジェットを追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | part_list.dart | `lib/widget/part/part_list.dart` | PartListクラス（13-40行目） |
| 2-2 | part_list.dart | `lib/widget/part/part_list.dart` | PaginatedPartListクラス（42-133行目） |

**主要処理フロー**:
- **42-51行目**: `PaginatedPartList`ウィジェット定義
- **53-103行目**: フィルタオプションとソートオプションの定義
- **60-64行目**: `orderingOptions` - 名前、在庫数、IPN
- **67-103行目**: `filterOptions` - 各種フィルタの定義
- **105-117行目**: `requestPage` - APIページリクエスト
- **119-133行目**: `buildItem` - 各パーツのListTile構築

#### Step 3: ページネーター基底クラスを理解する

ページネーション処理の基底クラスを追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | paginator.dart | `lib/widget/paginator.dart` | PaginatedSearchWidget/Stateクラス |

**主要処理フロー**:
- 無限スクロール処理
- フィルタ/ソートUI
- 検索バー

#### Step 4: カテゴリ表示での利用を理解する

カテゴリ画面でのパーツ一覧表示を追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | category_display.dart | `lib/widget/part/category_display.dart` | CategoryDisplayWidget（17-258行目） |

**主要処理フロー**:
- **183-186行目**: `getTabs` - 詳細タブとパーツタブ
- **210-217行目**: `partsTiles` - パーツ一覧の表示

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

```
PartList / CategoryDisplayWidget
    │
    └─ PaginatedPartList
           │
           ├─ PaginatedSearchState
           │      └─ requestPage(limit, offset, params)
           │             └─ InvenTreePart().listPaginated()
           │                    └─ api.get("part/", params)
           │
           └─ buildItem(model)
                  └─ ListTile(part)
                         └─ part.goToDetailPage(context)
```

### データフロー図

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

filters ───▶ PaginatedPartList ───▶ ListView
  │                │                      │
  ├─ category     ├─ requestPage()        ├─ ListTile(part)
  ├─ cascade      ├─ API呼び出し           │    ├─ thumbnail
  ├─ active       ├─ レスポンスパース        │    ├─ fullname
  └─ ...          └─ モデル変換             │    ├─ description
                                          │    └─ stockString
                                          │
                                          └─ タップ → PartDetailWidget
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| part_list.dart | `lib/widget/part/part_list.dart` | ソース | パーツ一覧ウィジェット |
| part.dart | `lib/inventree/part.dart` | ソース | InvenTreePartモデル |
| category_display.dart | `lib/widget/part/category_display.dart` | ソース | カテゴリ画面（パーツタブ含む） |
| paginator.dart | `lib/widget/paginator.dart` | ソース | ページネーション基底クラス |
| model.dart | `lib/inventree/model.dart` | ソース | InvenTreeModel基底クラス |
| api.dart | `lib/api.dart` | ソース | API通信 |
| link_icon.dart | `lib/widget/link_icon.dart` | ソース | LargeText（在庫数表示） |
