# 画面設計書 22-部品詳細画面

## 概要

本ドキュメントは、InvenTree Flutterアプリケーションにおける「部品詳細画面」の設計仕様を定義する。部品の詳細情報を表示し、在庫管理、BOM、価格情報、添付ファイルなど多岐にわたる関連情報へのアクセスを提供する画面である。

### 本画面の処理概要

部品詳細画面は、個別の部品（Part）に関するすべての詳細情報を集約して表示する中核的な画面である。2つのタブ（詳細・在庫）で情報を整理し、ユーザーは部品の基本情報から在庫状況、BOM、価格、サプライヤー情報まで一元的に確認できる。

**業務上の目的・背景**：製造業や在庫管理において、部品情報は業務判断の基盤となる。この画面は、部品の在庫状況確認、発注判断、BOM構成確認、価格分析など、多様な業務ニーズに対応する統合的なインターフェースを提供する。部品に関連するあらゆる情報への「ハブ」として機能し、業務効率を大幅に向上させる。

**画面へのアクセス方法**：
- 部品一覧画面から部品をタップして遷移
- 部品カテゴリ画面から部品をタップして遷移
- 在庫アイテム詳細画面から部品リンクをタップ
- バーコードスキャンで部品を識別した際に遷移
- 検索画面から部品検索結果をタップ

**主要な操作・処理内容**：
1. 部品基本情報の表示（名前、説明、カテゴリ、IPN、リビジョン）
2. 在庫情報の表示（利用可能在庫、デフォルトロケーション）
3. 部品要件情報の表示（製造中、ビルド可能数、引当状況）
4. 価格情報の表示と価格詳細画面への遷移
5. BOM（部品表）一覧への遷移
6. 在庫アイテム一覧のタブ表示
7. 部品編集機能（編集権限がある場合）
8. 新規在庫アイテム作成
9. ラベル印刷機能
10. バーコード割当/解除

**画面遷移**：
- 遷移元：部品一覧画面、部品カテゴリ画面、在庫詳細画面、バーコードスキャン画面、検索画面
- 遷移先：部品カテゴリ画面、BOM一覧画面、価格詳細画面、サプライヤー部品一覧、在庫詳細画面、ノート画面、添付ファイル画面、パラメータ画面、部品画像ウィジェット

**権限による表示制御**：
- 部品の編集ボタンは `InvenTreePart().canEdit` がtrueの場合のみ表示
- 在庫作成ボタンは `InvenTreeStockItem().canCreate` がtrueの場合のみ表示
- ラベル印刷は設定で有効化されている場合のみ表示

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 5 | パーツ詳細表示 | 主機能 | パーツの詳細情報（在庫数、価格、BOM、サプライヤー等）を表示 |
| 6 | パーツ編集 | 補助機能 | 編集ボタン押下時にパーツ情報編集フォーム表示 |
| 8 | パーツ画像管理 | 補助機能 | パーツ画像タップ時に画像表示・変更画面へ遷移 |
| 9 | BOM表示 | 遷移先機能 | BOM項目押下時にBOM一覧画面へ遷移 |
| 10 | パーツ価格表示 | 遷移先機能 | 価格項目押下時に価格詳細画面へ遷移 |
| 11 | サプライヤーパーツ表示 | 遷移先機能 | サプライヤー項目押下時にサプライヤーパーツ一覧へ遷移 |
| 12 | パーツパラメータ表示 | 補助機能 | パラメータ項目押下時にパラメータ一覧表示 |
| 14 | 在庫アイテム一覧表示 | 補助機能 | 在庫タブでパーツに関連する在庫アイテム一覧表示 |
| 16 | 在庫アイテム作成 | 補助機能 | 在庫作成ボタン押下時に在庫作成フォーム表示 |
| 44 | バーコード割当 | 補助機能 | バーコード割当ボタン押下時にバーコードスキャン開始 |
| 45 | バーコード解除 | 補助機能 | バーコード解除ボタン押下時に解除処理実行 |
| 53 | ラベル印刷 | 補助機能 | ラベル印刷ボタン押下時に印刷処理実行 |
| 54 | 添付ファイル表示 | 補助機能 | 添付ファイル項目押下時に添付ファイル一覧表示 |
| 56 | ノート表示・編集 | 遷移先機能 | ノート項目押下時にノート画面へ遷移 |

## 画面種別

詳細（タブ付き）

## URL/ルーティング

Flutter Navigator によるプッシュ遷移。`PartDetailWidget` に `InvenTreePart` インスタンスを渡して表示。

## 入出力項目

| 項目名 | 入出力 | 型 | 必須 | 説明 |
|--------|--------|------|------|------|
| part | 入力 | InvenTreePart | Yes | 表示対象の部品インスタンス |

## 表示項目

### ヘッダータイル

| 項目名 | 表示位置 | データ型 | 説明 |
|--------|----------|----------|------|
| 部品サムネイル | Card.leading | Image | タップで画像詳細へ |
| 部品フルネーム | Card.title | String | IPN | 名前 | リビジョン |
| 説明 | Card.subtitle | String | 部品の説明 |
| 在庫数 | Card.trailing | String | 利用可能/総在庫 |

### 詳細タブ

| 項目名 | 表示位置 | データ型 | 表示条件 |
|--------|----------|----------|---------|
| 非アクティブ警告 | ListTile | テキスト | isActive == false |
| テンプレート部品 | ListTile | リンク | variantOf != null |
| カテゴリ | ListTile | リンク | 常時 |
| バリアント数 | ListTile | リンク | variantCount > 0 |
| 利用可能在庫 | ListTile | テキスト | 常時 |
| デフォルトロケーション | ListTile | リンク | defaultLocation != null |
| 製造中 | ListTile | プログレスバー | building > 0 かつ showRequirements |
| ビルド可能数 | ListTile | テキスト | isAssembly かつ isActive |
| ビルドオーダー引当 | ListTile | プログレスバー | requiredForBuildOrders > 0 |
| 販売注文引当 | ListTile | プログレスバー | isSalable かつ条件あり |
| 発注中 | ListTile | テキスト | isPurchaseable かつ ordering > 0 |
| 価格情報 | ListTile | リンク | showPricing |
| BOM | ListTile | リンク | isAssembly かつ showBom かつ bomCount > 0 |
| 使用先 | ListTile | リンク | isComponent かつ usedInCount > 0 |
| キーワード | ListTile | テキスト | keywords.isNotEmpty |
| 外部リンク | ListTile | リンク | hasLink |
| サプライヤー | ListTile | リンク | isPurchaseable かつ supplierCount > 0 |
| ノート | ListTile | リンク | 常時 |
| パラメータ | ListTile | リンク | parameterCount > 0 |
| 添付ファイル | ListTile | リンク | attachmentCount > 0 |

### 在庫タブ

| 項目名 | 表示位置 | データ型 | 説明 |
|--------|----------|----------|------|
| ヘッダー | Card | 部品情報 | 詳細タブと同じヘッダー |
| 在庫アイテム一覧 | PaginatedList | リスト | 部品に紐づく在庫アイテム |

## イベント仕様

### 1-部品編集ボタン押下

アプリバーの編集アイコンをタップすると、部品編集フォームが表示される。

**処理フロー**：
1. `IconButton.onPressed` で `_editPartDialog(context)` を呼び出し
2. `part.editForm()` でAPIフォームを表示
3. 成功時に `refresh(context)` と成功スナックバー表示

### 2-在庫作成ボタン押下

SpeedDialの「在庫作成」ボタンをタップすると、新規在庫アイテム作成フォームが表示される。

**処理フロー**：
1. `SpeedDialChild.onTap` で `_newStockItem(context)` を呼び出し
2. シリアル番号対応部品の場合は次番号を取得
3. `InvenTreeStockItem().createForm()` で作成フォームを表示
4. 成功時に作成された在庫アイテム詳細画面へ遷移

### 3-画像タップ

ヘッダーの部品画像をタップすると、画像表示・変更画面へ遷移する。

**処理フロー**：
1. `GestureDetector.onTap` が発火
2. `Navigator.push` で `PartImageWidget(part)` へ遷移
3. 戻り時に `refresh(context)` で再読み込み

### 4-カテゴリタップ

部品カテゴリのListTileをタップすると、カテゴリ詳細画面へ遷移する。

**処理フロー**：
1. `ListTile.onTap` でカテゴリ情報を取得
2. `InvenTreePartCategory().get(part.categoryId)` でカテゴリ取得
3. `cat.goToDetailPage(context)` で遷移

### 5-BOMタップ

BOM項目のListTileをタップすると、BOM一覧画面へ遷移する。

**処理フロー**：
1. `ListTile.onTap` が発火
2. `Navigator.push` で `BillOfMaterialsWidget(part, isParentComponent: true)` へ遷移

### 6-価格情報タップ

価格項目のListTileをタップすると、価格詳細画面へ遷移する。

**処理フロー**：
1. `ListTile.onTap` が発火
2. `Navigator.push` で `PartPricingWidget(part: part, partPricing: partPricing)` へ遷移

### 7-ラベル印刷

SpeedDialのラベル印刷ボタンをタップすると、ラベル選択・印刷処理が実行される。

**処理フロー**：
1. `SpeedDialChild.onTap` が発火
2. `selectAndPrintLabel(context, "part", widget.part.pk)` を呼び出し

### 8-バーコード割当/解除

SpeedDialのバーコードボタンをタップすると、カスタムバーコードの割当または解除を行う。

**処理フロー**：
1. `customBarcodeAction()` が呼び出される
2. 既存バーコードがある場合は解除、ない場合は割当

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示 | Part | SELECT | 部品詳細情報の取得 |
| 画面表示 | PartCategory | SELECT | カテゴリ情報の取得 |
| 画面表示 | PartPricing | SELECT | 価格情報の取得 |
| 画面表示 | PartRequirements | SELECT | 要件情報の取得 |
| 画面表示 | Attachment | SELECT | 添付ファイル数の取得 |
| 画面表示 | Parameter | SELECT | パラメータ数の取得 |
| 部品編集 | Part | UPDATE | 部品情報の更新 |
| 在庫作成 | StockItem | INSERT | 新規在庫アイテムの作成 |
| バーコード割当 | Part | UPDATE | customBarcodeフィールドの更新 |

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

#### Part テーブル

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | 全フィールド | pk = 指定ID | reload()で再取得 |
| UPDATE | name, description, IPN, revision, keywords, link, category, default_location, 各種フラグ | フォーム入力値 | editForm()経由 |
| UPDATE | custom_barcode | スキャンしたバーコード値 | バーコード割当時 |

#### StockItem テーブル

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | part, location, quantity, serial_numbers, batch, purchase_price, packaging, link | フォーム入力値 | createForm()経由 |

## メッセージ仕様

| メッセージID | メッセージ種別 | 表示条件 | メッセージ内容 |
|-------------|---------------|----------|---------------|
| MSG_PART_EDITED | 成功 | 部品編集成功時 | "Part edited"（L10().partEdited） |
| MSG_INACTIVE | 警告 | 部品が非アクティブ | "Inactive" + 詳細説明 |
| MSG_NO_PRICING | 情報 | 価格情報なし | "No pricing available" |
| MSG_NO_STOCK | 情報 | 在庫アイテムなし | "Stock items not available" |

## 例外処理

| 例外条件 | 処理内容 | ユーザーへの表示 |
|---------|---------|-----------------|
| 部品リロード失敗 | Navigator.pop()で画面を閉じる | 前の画面に戻る |
| APIエラー | Sentryに報告 | 該当項目が非表示 |
| ネットワークエラー | ローディング表示を隠す | プルダウンでリフレッシュ可能 |
| 権限不足 | 編集ボタン非表示 | 閲覧のみ可能 |

## 備考

- 表示設定（showBom, showPricing, showRequirements）はローカル設定から読み込まれる
- ラベル印刷は `api.supportsModernLabelPrinting` がtrueの場合のみ利用可能
- バリアントがある場合はバリアント一覧へのリンクが表示される
- テスト可能部品（isTestable）の場合はテストテンプレートを取得する

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | part.dart | `lib/inventree/part.dart` | InvenTreePartクラス（138-459行）。各種getter（isAssembly, isComponent, isPurchaseable等）とPricing, Requirements取得メソッド |
| 1-2 | part.dart | `lib/inventree/part.dart` | InvenTreePartPricing（498-545行）とInvenTreePartRequirements（464-493行）のデータ構造 |

**読解のコツ**: `getBool()`, `getString()`, `getDouble()` は基底クラス `InvenTreeModel` で定義されたJSONデータアクセサ。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | part_detail.dart | `lib/widget/part/part_detail.dart` | PartDetailWidget（36-43行）とコンストラクタ。InvenTreePartを受け取る |
| 2-2 | part_detail.dart | `lib/widget/part/part_detail.dart` | _PartDisplayState（45-856行）。主要なStateクラス |

**主要処理フロー**:
1. **68-69行**: `getAppBarTitle()` でタイトル設定
2. **148-310行**: `request()` でデータ取得。親部品、デフォルトロケーション、テストテンプレート、添付ファイル数等を非同期取得
3. **347-750行**: `partTiles()` で詳細タブの内容を構築

#### Step 3: タブ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | part_detail.dart | `lib/widget/part/part_detail.dart` | getTabIcons()（839-843行）とgetTabs()（845-855行）。詳細タブと在庫タブの定義 |

**主要処理フロー**:
- **839-843行**: タブアイコンとして「Details」「Stock」を返す
- **848-853行**: 詳細タブは `partTiles()` のスクロール可能リスト、在庫タブは `PaginatedStockItemList`

#### Step 4: 各種タイル構築を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | part_detail.dart | `lib/widget/part/part_detail.dart` | headerTile()（323-342行）。ヘッダーCardの構築 |
| 4-2 | part_detail.dart | `lib/widget/part/part_detail.dart` | partTiles()（347-750行）。各種情報タイルの条件分岐と構築 |

**主要処理フロー**:
- **358-369行**: 非アクティブ警告の表示
- **385-422行**: カテゴリ情報の表示とリンク
- **469-506行**: 要件情報（製造中、ビルド可能数）の表示
- **565-591行**: 価格情報の表示
- **594-612行**: BOM一覧へのリンク

#### Step 5: アクションボタンを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | part_detail.dart | `lib/widget/part/part_detail.dart` | appBarActions()（71-87行）。編集ボタンの条件表示 |
| 5-2 | part_detail.dart | `lib/widget/part/part_detail.dart` | actionButtons()（108-137行）。SpeedDialの在庫作成・ラベル印刷ボタン |
| 5-3 | part_detail.dart | `lib/widget/part/part_detail.dart` | barcodeButtons()（89-106行）。バーコード割当ボタン |

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

```
PartDetailWidget (StatefulWidget)
    │
    └─ _PartDisplayState (RefreshableState)
           │
           ├─ request()
           │      │
           │      ├─ part.reload() → API GET /api/part/{pk}/
           │      ├─ part.getPricing() → API GET /api/part/{pk}/pricing/
           │      ├─ part.getRequirements() → API GET /api/part/{pk}/requirements/
           │      ├─ InvenTreeAttachment().countAttachments()
           │      └─ InvenTreeParameter().countParameters()
           │
           ├─ getTabs()
           │      │
           │      ├─ partTiles() → ListView (詳細タブ)
           │      │      │
           │      │      ├─ headerTile() → Card
           │      │      ├─ カテゴリタイル → CategoryDisplayWidget
           │      │      ├─ 価格タイル → PartPricingWidget
           │      │      └─ BOMタイル → BillOfMaterialsWidget
           │      │
           │      └─ PaginatedStockItemList (在庫タブ)
           │
           ├─ actionButtons()
           │      │
           │      ├─ _newStockItem() → StockItem.createForm()
           │      └─ selectAndPrintLabel()
           │
           └─ _editPartDialog() → part.editForm()
```

### データフロー図

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

InvenTreePart   ───▶ _PartDisplayState              ───▶ TabBarView
                         │                                  │
                         ├─ request()                       ├─ 詳細タブ
                         │      │                          │      └─ partTiles()
                         │      ├─ reload()                 │
                         │      ├─ getPricing()             └─ 在庫タブ
                         │      └─ getRequirements()               └─ PaginatedStockItemList
                         │
                         └─ State更新
                                │
                                ├─ partPricing
                                ├─ partRequirements
                                ├─ parameterCount
                                └─ attachmentCount
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| part_detail.dart | `lib/widget/part/part_detail.dart` | ソース | 部品詳細画面のメインウィジェット |
| part.dart | `lib/inventree/part.dart` | ソース | 部品データモデル |
| stock_list.dart | `lib/widget/stock/stock_list.dart` | ソース | 在庫アイテム一覧（在庫タブで使用） |
| bom_list.dart | `lib/widget/part/bom_list.dart` | ソース | BOM一覧ウィジェット |
| part_pricing.dart | `lib/widget/part/part_pricing.dart` | ソース | 価格詳細ウィジェット |
| category_display.dart | `lib/widget/part/category_display.dart` | ソース | カテゴリ詳細ウィジェット |
| part_image_widget.dart | `lib/widget/part/part_image_widget.dart` | ソース | 画像表示・変更ウィジェット |
| supplier_part_list.dart | `lib/widget/company/supplier_part_list.dart` | ソース | サプライヤー部品一覧 |
| notes_widget.dart | `lib/widget/notes_widget.dart` | ソース | ノート表示・編集 |
| attachment_widget.dart | `lib/widget/attachment_widget.dart` | ソース | 添付ファイル一覧 |
| parameter_widget.dart | `lib/widget/parameter_widget.dart` | ソース | パラメータ一覧 |
| refreshable_state.dart | `lib/widget/refreshable_state.dart` | ソース | リフレッシュ可能なState基底クラス |
| labels.dart | `lib/labels.dart` | ソース | ラベル印刷機能 |
| barcode.dart | `lib/barcode/barcode.dart` | ソース | バーコード機能 |
| preferences.dart | `lib/preferences.dart` | ソース | 設定管理（表示オプション） |
