# 機能設計書 5-部品テスト管理

## 概要

本ドキュメントは、InvenTreeシステムにおける部品テスト管理機能の設計を記述する。部品テスト管理は、品質管理や製造工程において、部品や在庫アイテムに対するテスト要件とテスト結果を体系的に管理する。

### 本機能の処理概要

**業務上の目的・背景**：
製造業において、部品や完成品の品質保証は極めて重要である。部品テスト管理機能は、特定の部品に対して必要なテスト項目（テストテンプレート）を定義し、在庫アイテム単位でテスト結果を記録・追跡する。これにより、出荷前の品質確認、不良品の特定、テスト履歴の追跡が可能となる。テストテンプレートはバリアント部品に継承されるため、製品ラインナップ全体で一貫したテスト管理が実現できる。

**機能の利用シーン**：
- 部品の品質要件として必須テスト項目を定義する場面
- 製造工程で在庫アイテムに対しテスト結果を記録する場面
- 出荷前に全テスト合格を確認する場面
- テスト不合格品を特定し対処する場面
- テスト履歴を参照し品質分析を行う場面

**主要な処理内容**：
1. テストテンプレートの作成・編集・削除
2. 在庫アイテムへのテスト結果記録
3. テスト合否判定
4. 添付ファイル・測定値の記録
5. バリアント部品へのテンプレート継承
6. テスト結果の検索・フィルタリング

**関連システム・外部連携**：
- Part（部品）との連携（テストテンプレートの保持）
- StockItem（在庫アイテム）との連携（テスト結果の保持）
- 添付ファイルシステムとの連携

**権限による制御**：
- テストテンプレートの閲覧は一般ユーザーでも可能
- テストテンプレートの作成・編集にはpart.change_parttesttemplate権限が必要
- テスト結果の記録にはstock.change_stockitemtestresult権限が必要

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 14 | 部品詳細画面 | 主機能 | テストテンプレートタブでのテンプレート管理 |
| 16 | 在庫アイテム詳細画面 | 主機能 | テスト結果の記録・表示 |

## 機能種別

CRUD操作 / 品質管理 / 階層継承

## 入力仕様

### テストテンプレート

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| part | integer | Yes | 部品ID | testable=Trueの部品のみ |
| test_name | string | Yes | テスト名 | 最大100文字、英数字を1文字以上含む |
| description | string | No | テスト説明 | 最大100文字 |
| required | boolean | No | 必須フラグ | デフォルトTrue |
| requires_value | boolean | No | 値必須フラグ | デフォルトFalse |
| requires_attachment | boolean | No | 添付必須フラグ | デフォルトFalse |
| choices | string | No | 選択肢（カンマ区切り） | 最大5000文字 |
| enabled | boolean | No | 有効フラグ | デフォルトTrue |

### テスト結果

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| stock_item | integer | Yes | 在庫アイテムID | |
| template | integer | No | テストテンプレートID | |
| test | string | Yes | テスト名 | 最大100文字 |
| result | boolean | Yes | 合否結果 | |
| value | string | No | 測定値 | 最大500文字、requires_valueの場合は必須 |
| attachment | file | No | 添付ファイル | requires_attachmentの場合は必須 |
| notes | string | No | 備考 | |
| started_datetime | datetime | No | テスト開始日時 | |
| finished_datetime | datetime | No | テスト終了日時 | |

### 入力データソース

- 画面入力（テストテンプレート作成・編集フォーム、テスト結果記録フォーム）
- REST API（api-part-test-template-list, api-stock-test-result-list）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| pk | integer | テストテンプレート/結果の主キー |
| test_name | string | テスト名 |
| key | string | テストキー（正規化された名前） |
| description | string | 説明 |
| required | boolean | 必須フラグ |
| result | boolean | 合否結果（結果のみ） |
| value | string | 測定値 |
| user | integer | 記録者ID |
| date | datetime | 記録日時 |

### 出力先

- 画面表示（テスト一覧、テスト結果一覧）
- REST API レスポンス
- レポート出力

## 処理フロー

### 処理シーケンス

```
1. テストテンプレート作成
   └─ testable部品チェック → テスト名バリデーション → キー生成 → 一意性チェック → DB保存

2. テスト結果記録
   └─ 在庫アイテム検証 → テンプレート検証（任意） → 値/添付バリデーション → 選択肢チェック → DB保存

3. テスト合否判定
   └─ 必須テスト取得 → 各テスト結果確認 → 全テスト合格判定

4. バリアント継承
   └─ 親部品のテンプレート取得 → 子部品にも適用
```

### フローチャート

```mermaid
flowchart TD
    A[テスト操作開始] --> B{操作種別}
    B -->|テンプレート作成| C{testable部品?}
    C -->|No| D[ValidationError]
    C -->|Yes| E[テスト名バリデーション]
    E --> F[キー生成]
    F --> G{キー一意?}
    G -->|No| D
    G -->|Yes| H[選択肢バリデーション]
    H --> I[DB保存]

    B -->|結果記録| J[在庫アイテム検証]
    J --> K{テンプレート指定?}
    K -->|Yes| L[テンプレート検証]
    L --> M{requires_value?}
    K -->|No| M
    M -->|Yes| N{値あり?}
    N -->|No| D
    N -->|Yes| O{requires_attachment?}
    M -->|No| O
    O -->|Yes| P{添付あり?}
    P -->|No| D
    P -->|Yes| Q[選択肢チェック]
    O -->|No| Q
    Q --> I

    I --> R[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | testable部品制約 | テストテンプレートはtestable=Trueの部品のみ定義可能 | part.testable |
| BR-002 | テストキー一意性 | テストキーは部品ツリー内で一意 | 親バリアントを含む |
| BR-003 | 値必須制約 | requires_value=Trueの場合、測定値は必須 | テンプレート設定 |
| BR-004 | 添付必須制約 | requires_attachment=Trueの場合、添付ファイルは必須 | テンプレート設定 |
| BR-005 | 選択肢制約 | 選択肢が定義されている場合、値はその中から選択 | テンプレート設定 |
| BR-006 | バリアント継承 | テストテンプレートはバリアント部品に継承される | include_parent=True |
| BR-007 | 必須テスト完了 | 全必須テストが合格でないと出荷不可 | required=True |

### 計算ロジック

```python
# テストキーの生成（正規化）
def generateTestKey(test_name: str) -> str:
    # 空白を削除、小文字化、非英数字を削除
    key = test_name.strip().lower()
    key = re.sub(r'[^a-z0-9]', '', key)
    return key

# 全テスト合格判定
def passedAllRequiredTests(self, include_installed=True):
    for template in self.part.getRequiredTests(include_parent=True, enabled=True):
        result = self.testResultMap().get(template.key, None)
        if not result or not result.result:
            return False
    return True
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| テンプレート作成 | part_parttesttemplate | INSERT | 新規テンプレートレコード挿入 |
| テンプレート編集 | part_parttesttemplate | UPDATE | テンプレート情報更新 |
| テンプレート削除 | part_parttesttemplate | DELETE | テンプレートレコード削除 |
| 結果記録 | stock_stockitemtestresult | INSERT | テスト結果レコード挿入 |
| 結果編集 | stock_stockitemtestresult | UPDATE | テスト結果更新 |
| 結果削除 | stock_stockitemtestresult | DELETE | テスト結果レコード削除 |

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

#### part_parttesttemplate

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | part_id, test_name, key, description, required, requires_value, requires_attachment, choices, enabled | 入力値、生成キー | キー一意性チェック後 |
| UPDATE | 上記カラム | 入力値 | |
| DELETE | - | pk指定 | |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ValidationError | バリデーションエラー | testable=Falseの部品にテンプレート作成 | testable=Trueに変更 |
| ValidationError | バリデーションエラー | テストキーが重複 | 別のテスト名を使用 |
| ValidationError | バリデーションエラー | requires_valueだが値なし | 測定値を入力 |
| ValidationError | バリデーションエラー | requires_attachmentだが添付なし | ファイルを添付 |
| ValidationError | バリデーションエラー | 選択肢に含まれない値 | 有効な選択肢から選択 |

### リトライ仕様

特になし

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

- テストテンプレート・結果の作成・更新・削除は単一トランザクション内で実行

## パフォーマンス要件

- テスト一覧取得: 500件で1秒以内
- テスト合否判定: 100ms以内

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

- ユーザー権限に基づくアクセス制御
- 添付ファイルのサイズ・形式制限
- APIエンドポイントは認証必須

## 備考

- テストキーは自動生成（test_nameから正規化）
- enabled=Falseのテンプレートは合否判定から除外
- テスト結果はテンプレートなしでも記録可能（アドホックテスト）

---

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

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

### 推奨読解順序

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

PartTestTemplateとStockItemTestResultモデルの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | models.py | `src/backend/InvenTree/part/models.py` | PartTestTemplateクラス（3625-3786行目） |
| 1-2 | models.py | `src/backend/InvenTree/stock/models.py` | StockItemTestResultクラス |

**読解のコツ**:
- PartTestTemplateはpartとtest_nameを持ち、keyは自動生成
- limit_choices_to={'testable': True}でtestable部品のみ許可
- validate_unique()でバリアント階層内の一意性をチェック

**主要処理フロー**:
- **3625-3636行目**: クラス定義とドキュメント
- **3652-3656行目**: saveメソッド（clean呼び出し）
- **3658-3693行目**: cleanメソッド（キー生成、選択肢バリデーション）
- **3695-3717行目**: validate_uniqueメソッド（testable確認、キー一意性）
- **3780-3785行目**: get_choicesメソッド

#### Step 2: テスト結果モデルを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | models.py | `src/backend/InvenTree/stock/models.py` | StockItemTestResultクラス |

#### Step 3: 部品側のテストメソッドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | models.py | `src/backend/InvenTree/part/models.py` | Part.getTestTemplates, getRequiredTestsメソッド（2522-2571行目） |

**主要処理フロー**:
- **2522-2551行目**: getTestTemplatesメソッド（バリアント継承対応）
- **2562-2571行目**: getRequiredTestsメソッド

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

```
REST API Request
    │
    ├─ PartTestTemplateList (api.py)
    │      ├─ PartTestTemplateSerializer
    │      │      └─ PartTestTemplate.save()
    │      │             ├─ clean()
    │      │             │      ├─ キー生成
    │      │             │      └─ 選択肢バリデーション
    │      │             └─ validate_unique()
    │      │                    ├─ testableチェック
    │      │                    └─ キー一意性チェック
    │      │
    │      └─ PartTestTemplate.delete()
    │
    └─ StockItemTestResultList (api.py)
           └─ StockItemTestResultSerializer
                  └─ StockItemTestResult.save()
                         ├─ clean()
                         │      ├─ 値必須チェック
                         │      ├─ 添付必須チェック
                         │      └─ 選択肢チェック
                         └─ super().save()
```

### データフロー図

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

フォーム入力 ───▶ PartTestTemplateSerializer ───▶ JSONレスポンス
                        │
                        ▼
                 PartTestTemplate.clean()
                        │
                        ├─ helpers.generateTestKey()
                        │
                        └─ 選択肢バリデーション
                                │
                                ▼
                        PartTestTemplate.validate_unique()
                                │
                                ├─ testableチェック
                                │
                                └─ キー一意性チェック
                                        │
                                        ▼
                                part_parttesttemplate テーブル
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| models.py | `src/backend/InvenTree/part/models.py` | ソース | PartTestTemplateモデル定義 |
| models.py | `src/backend/InvenTree/stock/models.py` | ソース | StockItemTestResultモデル定義 |
| api.py | `src/backend/InvenTree/part/api.py` | ソース | テストテンプレートAPIビュー定義 |
| api.py | `src/backend/InvenTree/stock/api.py` | ソース | テスト結果APIビュー定義 |
| serializers.py | `src/backend/InvenTree/part/serializers.py` | ソース | テストテンプレートシリアライザ定義 |
| serializers.py | `src/backend/InvenTree/stock/serializers.py` | ソース | テスト結果シリアライザ定義 |
| helpers.py | `src/backend/InvenTree/part/helpers.py` | ソース | generateTestKey関数 |
