# 機能設計書 11-サプライヤーパーツ表示

## 概要

本ドキュメントは、InvenTreeモバイルアプリにおけるサプライヤーパーツ表示機能の設計仕様を定義する。

### 本機能の処理概要

本機能は、パーツに関連付けられたサプライヤー情報を表示する機能である。InvenTreeで管理されるパーツは、複数のサプライヤーから調達可能な場合があり、各サプライヤーのSKU（在庫管理単位）、製造元情報、在庫状況などを一覧で確認できる。

**業務上の目的・背景**：製造業や在庫管理業務において、同一パーツを複数のサプライヤーから調達することは一般的である。この機能により、ユーザーは各サプライヤーの価格、納期、在庫状況を比較し、最適な調達先を選択できる。また、特定のサプライヤーが在庫切れの場合に代替サプライヤーを迅速に特定することが可能となる。

**機能の利用シーン**：
- 発注前にサプライヤーの在庫状況を確認する場合
- 複数サプライヤーの価格を比較する場合
- 製造元パーツ番号（MPN）を確認する場合
- サプライヤーの詳細情報にアクセスする場合

**主要な処理内容**：
1. パーツに関連付けられたサプライヤーパーツの一覧を取得
2. 各サプライヤーパーツのSKU、製造元名、MPN、サプライヤー画像を表示
3. サプライヤーパーツ選択時にサプライヤー（会社）詳細画面へ遷移
4. ページネーション対応による大量データの効率的な表示

**関連システム・外部連携**：InvenTree APIの `company/part/` エンドポイントを使用してサプライヤーパーツデータを取得する。

**権限による制御**：`part` および `purchase_order` ロールの `view` 権限が必要である。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 22 | 部品詳細画面 | 主画面 | サプライヤー項目押下時にサプライヤーパーツ一覧へ遷移 |
| 31 | 会社詳細画面 | 遷移先機能 | サプライヤーパーツ項目押下時にサプライヤーパーツ一覧へ遷移 |

## 機能種別

データ参照（READ）/ 一覧表示

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| part | int | Yes | パーツID | 正の整数 |
| filters | Map<String, String> | No | フィルタリング条件 | - |
| limit | int | No | 1ページあたりの取得件数（デフォルト25） | 正の整数 |
| offset | int | No | 取得開始位置 | 0以上の整数 |
| active | bool | No | アクティブステータスでのフィルタ | true/false/null |

### 入力データソース

- パーツ詳細画面から渡されるパーツオブジェクト（InvenTreePart）
- ユーザーによるフィルタリング条件の選択

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| pk | int | サプライヤーパーツの主キー |
| SKU | String | サプライヤー固有の在庫管理単位コード |
| supplier | int | サプライヤー（会社）ID |
| supplierName | String | サプライヤー名 |
| supplierImage | String | サプライヤーのサムネイル画像URL |
| manufacturer_part | int | 製造元パーツID |
| manufacturerName | String | 製造元名 |
| MPN | String | 製造元パーツ番号 |
| partName | String | 関連パーツ名 |
| partImage | String | パーツのサムネイル画像URL |
| in_stock | double | 在庫数量 |
| on_order | double | 発注中数量 |
| active | bool | アクティブステータス |

### 出力先

- ListView形式の画面表示
- ListTileウィジェットによる各サプライヤーパーツ情報の表示

## 処理フロー

### 処理シーケンス

```
1. パーツ詳細画面からサプライヤーパーツ表示を呼び出し
   └─ InvenTreePart.getSupplierParts()またはフィルタ付きリスト取得
2. APIリクエストの構築
   └─ company/part/ エンドポイントにパーツIDフィルタを設定
3. ページネーション付きでサプライヤーパーツ一覧を取得
   └─ InvenTreeSupplierPart().listPaginated()を呼び出し
4. 取得データをListViewで表示
   └─ 各項目をListTileとして構築
5. ユーザーがサプライヤーパーツを選択
   └─ サプライヤー詳細画面またはサプライヤーパーツ詳細画面へ遷移
```

### フローチャート

```mermaid
flowchart TD
    A[パーツ詳細画面] --> B[サプライヤーパーツ表示を選択]
    B --> C{表示方法}
    C -->|PartSupplierWidget| D[part.getSupplierParts()]
    C -->|SupplierPartList| E[PaginatedSupplierPartList]
    D --> F[サプライヤーパーツ一覧取得]
    E --> F
    F --> G[ListView表示]
    G --> H{ユーザー操作}
    H -->|項目タップ| I[会社詳細画面へ遷移]
    H -->|スクロール| J[次ページ取得]
    J --> F
    I --> K[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-11-01 | サプライヤー関連付け | パーツは複数のサプライヤーパーツを持つことができる | 常時 |
| BR-11-02 | アクティブフィルタ | APIバージョン189以降でアクティブステータスフィルタが利用可能 | supportsCompanyActiveStatus が true の場合 |
| BR-11-03 | 製造元情報表示 | サプライヤーパーツには製造元情報を関連付けることができる | manufacturer_part が設定されている場合 |

### 計算ロジック

なし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| サプライヤーパーツ取得 | SupplierPart | SELECT | パーツIDに関連するサプライヤーパーツを取得 |

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

#### SupplierPart（InvenTree API経由）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | part | 指定されたパーツID | フィルタ条件 |
| SELECT | supplier, SKU, manufacturer_part等 | - | 取得対象カラム |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | 認証エラー | トークン無効または期限切れ | 再ログインを促す |
| 403 | 権限エラー | 必要なロールが不足 | 権限確認メッセージを表示 |
| 404 | Not Found | パーツまたはサプライヤーパーツが存在しない | エラーメッセージを表示 |
| 500 | サーバーエラー | サーバー内部エラー | リトライまたはエラー表示 |

### リトライ仕様

ネットワークエラー時はプルダウンリフレッシュで再取得可能。

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

読み取り専用のため、トランザクション管理は不要。

## パフォーマンス要件

- ページサイズ：25件（PaginatedSearchState._pageSize）
- スクロールによる遅延読み込み対応
- 画像はキャッシュを使用（CachedNetworkImage）

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

- API認証トークンによるアクセス制御
- `part` および `purchase_order` ロールの `view` 権限チェック
- HTTPS通信によるデータ保護

## 備考

- PartSupplierWidgetはパーツ詳細画面から直接呼び出される簡易版
- SupplierPartListはページネーション対応の完全版リスト表示
- サプライヤーパーツ選択時の遷移先は実装により異なる（会社詳細またはサプライヤーパーツ詳細）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | company.dart | `lib/inventree/company.dart` | InvenTreeSupplierPartクラスの定義、プロパティ（SKU, supplierName, MPN等）を理解する |

**読解のコツ**:
- **117-232行目**: InvenTreeSupplierPartクラスの定義。InvenTreeModelを継承し、サプライヤーパーツの全プロパティとメソッドを定義
- **124行目**: APIエンドポイント `company/part/`
- **164-169行目**: defaultFilters()でmanufacturer_detail, supplier_detail, part_detailの詳細情報を含める設定

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | part_suppliers.dart | `lib/widget/part/part_suppliers.dart` | PartSupplierWidgetクラスのエントリーポイント、パーツからサプライヤーパーツ一覧を取得する流れを理解 |

**主要処理フロー**:
1. **12-18行目**: PartSupplierWidgetの定義、InvenTreePartを受け取る
2. **29-33行目**: request()でpart.getSupplierParts()を呼び出しサプライヤーパーツ一覧を取得
3. **44-58行目**: _supplierPartTile()で各サプライヤーパーツのListTileを構築
4. **62-69行目**: getBody()でListViewを構築

#### Step 3: ページネーション対応リストを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | supplier_part_list.dart | `lib/widget/company/supplier_part_list.dart` | PaginatedSupplierPartListクラスによるページネーション対応の一覧表示を理解 |

**主要処理フロー**:
- **34-44行目**: PaginatedSupplierPartListクラスの定義
- **57-69行目**: filterOptionsでアクティブフィルタの定義（APIバージョン189以降）
- **72-83行目**: requestPage()でInvenTreeSupplierPart().listPaginated()を呼び出し
- **86-98行目**: buildItem()で各サプライヤーパーツのListTileを構築

#### Step 4: APIリクエスト処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | model.dart | `lib/inventree/model.dart` | listPaginated()メソッドによるページネーション付きAPI呼び出しを理解 |

**主要処理フロー**:
- **721-794行目**: listPaginated()メソッドの実装、APIからページ単位でデータを取得

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

```
PartSupplierWidget / SupplierPartList
    │
    ├─ RefreshableState (ベースクラス)
    │      └─ request() / refresh()
    │
    ├─ InvenTreePart.getSupplierParts()
    │      └─ InvenTreeSupplierPart().list()
    │              └─ InvenTreeAPI.get()
    │
    └─ PaginatedSupplierPartList
           └─ PaginatedSearchState
                  ├─ requestPage()
                  │      └─ InvenTreeSupplierPart().listPaginated()
                  │              └─ InvenTreeAPI.get()
                  │
                  └─ buildItem()
                         └─ ListTile構築
```

### データフロー図

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

パーツID ───────▶ InvenTreeSupplierPart ───────▶ ListView
(filters)              .listPaginated()              (ListTile)
                              │
                              ▼
                    API: company/part/
                              │
                              ▼
                    JSON Response ──────▶ List<InvenTreeSupplierPart>
                                                    │
                                                    ▼
                                         画面表示（SKU, 製造元, MPN）
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| part_suppliers.dart | `lib/widget/part/part_suppliers.dart` | ソース | パーツ詳細から呼び出される簡易サプライヤーパーツ表示ウィジェット |
| supplier_part_list.dart | `lib/widget/company/supplier_part_list.dart` | ソース | ページネーション対応サプライヤーパーツ一覧ウィジェット |
| company.dart | `lib/inventree/company.dart` | ソース | InvenTreeSupplierPartモデルクラス定義 |
| model.dart | `lib/inventree/model.dart` | ソース | 基底モデルクラス、API呼び出し共通処理 |
| paginator.dart | `lib/widget/paginator.dart` | ソース | ページネーション対応検索ウィジェット基底クラス |
| refreshable_state.dart | `lib/widget/refreshable_state.dart` | ソース | リフレッシュ可能な状態管理基底クラス |
| api.dart | `lib/api.dart` | ソース | InvenTree API通信クラス |
| part.dart | `lib/inventree/part.dart` | ソース | InvenTreePartモデル、getSupplierParts()メソッド |
