# 機能設計書 15-在庫アイテム詳細表示

## 概要

本ドキュメントは、InvenTreeモバイルアプリにおける在庫アイテム詳細表示機能の設計仕様を定義する。

### 本機能の処理概要

本機能は、個々の在庫アイテムの詳細情報（数量、ロケーション、ステータス、関連情報等）を表示する機能である。在庫アイテムに関する包括的な情報を一画面で確認でき、各種操作（数量調整、移動、編集、削除等）へのアクセスポイントとなる。

**業務上の目的・背景**：在庫管理業務において、個々の在庫アイテムの詳細情報を確認することは日常的に必要となる。ピッキング作業での在庫確認、品質管理でのテスト結果確認、トレーサビリティのための履歴確認など、多様な業務シーンで活用される。また、この画面から各種在庫操作（数量調整、移動、編集等）を実行できるため、在庫管理のハブとなる重要な機能である。

**機能の利用シーン**：
- 在庫アイテムの詳細情報を確認する場合
- 在庫数量を調整（追加/削除/カウント）する場合
- 在庫を別のロケーションに移動する場合
- 在庫アイテムの情報を編集する場合
- テスト結果や履歴を確認する場合
- ラベルを印刷する場合

**主要な処理内容**：
1. 在庫アイテムの基本情報（パーツ名、数量、ロケーション、ステータス）を表示
2. 関連情報（サプライヤーパーツ、受注、顧客、バッチ、有効期限等）を表示
3. テスト結果・履歴へのナビゲーション
4. 添付ファイル・ノートへのアクセス
5. 各種アクション（数量調整、移動、編集、削除、印刷）の実行

**関連システム・外部連携**：InvenTree APIの `stock/{pk}/` エンドポイントを使用して在庫アイテムデータを取得・更新する。

**権限による制御**：`stock` ロールの権限に基づいてCRUD操作を制御。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 25 | 在庫アイテム詳細画面 | 主画面 | 在庫アイテムの詳細情報表示 |
| 22 | 部品詳細画面 | 遷移先機能 | パーツ項目押下時にパーツ詳細画面へ遷移 |
| 23 | 在庫ロケーション画面 | 遷移先機能 | ロケーション項目押下時にロケーション画面へ遷移 |

## 機能種別

データ参照（READ）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| item | InvenTreeStockItem | Yes | 表示対象の在庫アイテム | pk > 0 |

### 入力データソース

- 一覧画面から渡される在庫アイテムオブジェクト
- APIから再取得した最新データ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| pk | int | 在庫アイテムの主キー |
| partName | String | パーツ名 |
| partDescription | String | パーツの説明 |
| partImage | String | パーツ画像URL |
| quantity | double | 在庫数量 |
| allocated | double | 引当数量 |
| available | double | 利用可能数量 |
| serialNumber | String | シリアル番号 |
| locationId | int | ロケーションID |
| locationPathString | String | ロケーションパス |
| status | int | ステータスコード |
| batch | String | バッチコード |
| packaging | String | 梱包情報 |
| expiryDate | DateTime? | 有効期限 |
| purchasePrice | double? | 購入価格 |
| updatedDateString | String | 最終更新日 |
| stocktakeDateString | String | 最終棚卸日 |
| testResultCount | int | テスト結果数 |
| trackingItemCount | int | 履歴件数 |
| attachmentCount | int | 添付ファイル数 |

### 出力先

- スクロール可能なタイル形式の画面表示
- ListTileウィジェットによる各情報項目の表示
- SpeedDialによるアクションボタン

## 処理フロー

### 処理シーケンス

```
1. StockDetailWidgetを初期化
   └─ InvenTreeStockItemを受け取る
2. 在庫アイテム情報の取得
   └─ item.reload()でAPIからデータを再取得
3. 関連データの取得
   ├─ パーツ情報の取得
   ├─ デフォルトロケーションの取得
   ├─ テスト結果の取得（trackableの場合）
   ├─ 添付ファイル数の取得
   ├─ 受注情報の取得（hasSalesOrderの場合）
   └─ 顧客情報の取得（hasCustomerの場合）
4. 詳細タイルリストの構築
   └─ getTiles()で各情報タイルを構築
5. アクションボタンの構築
   ├─ actionButtons()：数量調整、移動、印刷、削除
   └─ barcodeButtons()：バーコードスキャン関連
```

### フローチャート

```mermaid
flowchart TD
    A[在庫詳細画面表示] --> B[item.reload]
    B --> C{reload成功?}
    C -->|No| D[前画面に戻る]
    C -->|Yes| E[関連データ取得]
    E --> F[パーツ情報取得]
    E --> G[デフォルトロケーション取得]
    E --> H[テスト結果取得]
    E --> I[添付ファイル数取得]
    F --> J[getTiles構築]
    G --> J
    H --> J
    I --> J
    J --> K[画面表示]
    K --> L{ユーザー操作}
    L -->|パーツ選択| M[パーツ詳細へ遷移]
    L -->|ロケーション選択| N[ロケーション画面へ遷移]
    L -->|数量調整| O[調整ダイアログ]
    L -->|移動| P[移動ダイアログ]
    L -->|編集| Q[編集フォーム]
    L -->|削除| R[削除確認]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-15-01 | シリアル表示 | シリアル化されている場合は数量の代わりにシリアル番号を表示 | isSerialized() == true |
| BR-15-02 | 在庫なし表示 | isInStock==falseの場合は「利用不可」を表示 | isInStock == false |
| BR-15-03 | 引当表示 | allocated > 0の場合は「利用可能数量」として表示 | allocated > 0 |
| BR-15-04 | 数量調整制限 | シリアル化されている場合は追加/削除/カウントボタンを非表示 | isSerialized() == true |
| BR-15-05 | テスト結果表示 | trackable==trueまたはテスト結果がある場合に表示 | stockShowTests && (part.isTrackable \|\| testResultCount > 0) |
| BR-15-06 | 履歴表示 | stockShowHistory設定とtrackingItemCount > 0の場合に表示 | stockShowHistory && trackingItemCount > 0 |
| BR-15-07 | 有効期限表示 | STOCK_ENABLE_EXPIRY設定が有効で有効期限がある場合に表示 | expiryEnabled && expiryDate != null |

### 計算ロジック

- available = quantity - allocated
- quantityString = allocated > 0 ? "${available} / ${quantity}" : "${quantity}"

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 在庫アイテム取得 | StockItem | SELECT | 在庫アイテム詳細を取得 |
| パーツ取得 | Part | SELECT | 関連パーツ情報を取得 |
| ロケーション取得 | StockLocation | SELECT | デフォルトロケーション情報を取得 |
| テスト結果取得 | StockItemTestResult | SELECT | テスト結果一覧を取得 |
| 添付ファイル数取得 | Attachment | SELECT | 添付ファイル数を取得 |

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

#### StockItem（InvenTree API経由）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | 全項目 | pk指定 | 詳細取得 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | 認証エラー | トークン無効または期限切れ | 再ログインを促す |
| 403 | 権限エラー | 必要なロールが不足 | 権限確認メッセージを表示 |
| 404 | Not Found | 在庫アイテムが存在しない（削除済み等） | 前画面に戻る、itemDeletedメッセージ表示 |
| 500 | サーバーエラー | サーバー内部エラー | リトライまたはエラー表示 |

### リトライ仕様

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

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

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

## パフォーマンス要件

- 関連データは非同期で取得し、取得完了後にsetState()で画面更新
- 画像はキャッシュを使用（CachedNetworkImage）

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

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

## 備考

- テスト結果・履歴の表示は設定で切り替え可能
- 有効期限機能はサーバー設定で有効化が必要
- サプライヤーパーツ、受注、顧客への遷移が可能

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | stock.dart | `lib/inventree/stock.dart` | InvenTreeStockItemクラスの定義、全プロパティを理解する |

**読解のコツ**:
- **134-574行目**: InvenTreeStockItemクラスの完全な定義
- **259-261行目**: isInStock, packaging, batchプロパティ
- **265-282行目**: purchasePrice関連プロパティ
- **288-310行目**: salesOrderId, customerId, stale, expired, expiryDate
- **409-426行目**: quantity, allocated, available, quantityString
- **434行目**: isSerialized()でシリアル化判定

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | stock_detail.dart | `lib/widget/stock/stock_detail.dart` | StockDetailWidgetクラスのエントリーポイント |

**主要処理フロー**:
1. **32-39行目**: StockDetailWidgetの定義、InvenTreeStockItemを受け取る
2. **41-86行目**: appBarActions()で編集・位置特定ボタンを構築
3. **88-157行目**: actionButtons()で数量調整、移動、印刷、削除ボタンを構築
4. **159-192行目**: barcodeButtons()でバーコード関連ボタンを構築

#### Step 3: データ取得処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | stock_detail.dart | `lib/widget/stock/stock_detail.dart` | request()メソッドによる関連データ取得 |

**主要処理フロー**:
- **206-317行目**: request()で各種データを取得
- **207-213行目**: StockStatus.load(), 設定値読み込み
- **215-221行目**: item.reload()とエラーハンドリング
- **223行目**: expiryEnabled設定の取得
- **226-245行目**: パーツ情報、デフォルトロケーションの取得
- **248-256行目**: テスト結果の取得
- **259-267行目**: 添付ファイル数の取得
- **270-311行目**: 受注・顧客情報の取得

#### Step 4: 詳細タイル表示を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | stock_detail.dart | `lib/widget/stock/stock_detail.dart` | getTiles()による詳細情報タイルの構築 |

**主要処理フロー**:
- **516-855行目**: getTiles()で各情報タイルを構築
- **521行目**: headerTile()でパーツ情報ヘッダー
- **529-559行目**: ロケーション情報タイル
- **577-595行目**: シリアル番号または数量タイル
- **614-623行目**: ステータスタイル
- **626-645行目**: サプライヤーパーツタイル
- **769-787行目**: テスト結果タイル
- **806-824行目**: 履歴タイル

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

```
StockDetailWidget
    │
    ├─ RefreshableState (基底クラス)
    │      └─ request()
    │              ├─ item.reload()
    │              ├─ InvenTreePart().get()
    │              ├─ InvenTreeStockLocation().get()
    │              ├─ item.getTestResults()
    │              ├─ InvenTreeAttachment().countAttachments()
    │              ├─ InvenTreeSalesOrder().get()
    │              └─ InvenTreeCompany().get()
    │
    ├─ getTiles()
    │      ├─ headerTile()
    │      ├─ ロケーション/数量/ステータス等のListTile
    │      ├─ テスト結果/履歴へのナビゲーション
    │      └─ ノート/添付ファイルタイル
    │
    ├─ actionButtons()
    │      ├─ _countStockDialog()
    │      ├─ _removeStockDialog()
    │      ├─ _addStockDialog()
    │      ├─ _transferStockDialog()
    │      ├─ selectAndPrintLabel()
    │      └─ _deleteItem()
    │
    └─ barcodeButtons()
           ├─ StockItemScanIntoLocationHandler
           └─ customBarcodeAction
```

### データフロー図

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

StockItem ──────▶ request() ──────▶ getTiles()
    │                 │                   │
    │                 ▼                   ▼
    │         item.reload()        List<Widget>
    │                 │                   │
    │                 ▼                   ▼
    │         API: stock/{pk}/     ListTileリスト表示
    │                 │
    │                 ▼
    │         関連データ取得
    │         ├─ Part
    │         ├─ StockLocation
    │         ├─ TestResults
    │         ├─ Attachments
    │         ├─ SalesOrder
    │         └─ Company
    │
    └─────────────▶ 画面表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| stock_detail.dart | `lib/widget/stock/stock_detail.dart` | ソース | 在庫アイテム詳細表示ウィジェット |
| stock.dart | `lib/inventree/stock.dart` | ソース | InvenTreeStockItem/StockItemTestResultモデル |
| part.dart | `lib/inventree/part.dart` | ソース | InvenTreePartモデル |
| company.dart | `lib/inventree/company.dart` | ソース | InvenTreeCompanyモデル |
| sales_order.dart | `lib/inventree/sales_order.dart` | ソース | InvenTreeSalesOrderモデル |
| attachment.dart | `lib/inventree/attachment.dart` | ソース | InvenTreeAttachmentモデル |
| model.dart | `lib/inventree/model.dart` | ソース | 基底モデルクラス |
| refreshable_state.dart | `lib/widget/refreshable_state.dart` | ソース | リフレッシュ可能な状態管理基底クラス |
| api.dart | `lib/api.dart` | ソース | InvenTree API通信クラス |
| stock_item_history.dart | `lib/widget/stock/stock_item_history.dart` | ソース | 在庫履歴表示ウィジェット |
| stock_item_test_results.dart | `lib/widget/stock/stock_item_test_results.dart` | ソース | テスト結果表示ウィジェット |
| attachment_widget.dart | `lib/widget/attachment_widget.dart` | ソース | 添付ファイル表示ウィジェット |
| notes_widget.dart | `lib/widget/notes_widget.dart` | ソース | ノート表示ウィジェット |
| labels.dart | `lib/labels.dart` | ソース | ラベル印刷機能 |
