# 機能設計書 10-在庫追跡・履歴

## 概要

本ドキュメントは、InvenTreeシステムにおける在庫追跡・履歴機能の設計を記述する。在庫追跡機能は、在庫アイテムに対する全ての操作（作成、移動、数量変更、ステータス変更、顧客割当等）を履歴として記録し、完全なトレーサビリティを提供する。

### 本機能の処理概要

**業務上の目的・背景**：
製造業・物流業において、在庫の移動や変更の追跡は品質管理、監査対応、不具合発生時の原因究明において極めて重要である。在庫追跡・履歴機能は、各在庫アイテムに対するあらゆる操作を時系列で記録し、「いつ」「誰が」「何を」「どのように」変更したかを完全に追跡可能とする。これにより、製品リコール時の影響範囲特定、品質問題の原因追跡、監査対応のエビデンス提供が可能となる。

**機能の利用シーン**：
- 在庫アイテムの変更履歴を確認する場面
- 品質問題発生時に原因を追跡する場面
- 監査対応で在庫操作のエビデンスを提示する場面
- 製品リコール時に影響範囲を特定する場面
- 在庫の入出庫履歴を分析する場面
- 特定ユーザーの操作履歴を確認する場面

**主要な処理内容**：
1. 在庫操作時の自動履歴記録
2. 履歴エントリの一覧表示・検索
3. 変更内容（delta）の詳細表示
4. 関連オブジェクト（発注、受注、製造等）のリンク表示
5. ユーザー別・期間別フィルタリング

**関連システム・外部連携**：
- StockItem（在庫アイテム）との連携
- PurchaseOrder（発注）との連携
- SalesOrder（受注）との連携
- ReturnOrder（返品オーダー）との連携
- Build（製造オーダー）との連携
- Company（顧客）との連携

**権限による制御**：
- 在庫追跡情報の閲覧は在庫閲覧権限を持つユーザーに許可
- 追跡エントリの手動作成にはstock.add_stockitemtracking権限が必要
- 既存の追跡エントリは読み取り専用（改ざん防止）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 16 | 在庫アイテム詳細画面 | 主機能 | 在庫追跡履歴の表示 |
| 14 | 部品詳細画面 | 参照画面 | 部品の在庫履歴の間接参照 |
| 15 | 在庫ロケーション一覧・詳細画面 | 参照画面 | ロケーション関連履歴の参照 |

## 機能種別

履歴管理 / 参照機能 / トレーサビリティ

## 入力仕様

### 入力パラメータ（履歴記録時）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| item | integer | Yes | 対象在庫アイテムID | 存在する在庫アイテム |
| tracking_type | integer | Yes | 履歴種別コード | StockHistoryCodeの有効値 |
| notes | string | No | メモ・備考 | 最大512文字 |
| deltas | JSON | No | 変更内容 | JSON形式 |
| user | integer | No | 操作ユーザーID | 自動設定 |
| date | datetime | No | 操作日時 | 自動設定 |

### 入力パラメータ（履歴照会時）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| item | integer | No | 在庫アイテムIDでフィルタ | |
| user | integer | No | ユーザーIDでフィルタ | |
| ordering | string | No | ソート順（デフォルト: -date） | |
| search | string | No | ノート検索 | |

### 入力データソース

- 内部システム（在庫操作時に自動記録）
- REST API（api-stock-tracking-list）：手動エントリ追加
- 発注受領処理
- 製造完了処理
- 販売出荷処理

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| pk | integer | 追跡エントリの主キー |
| item | integer | 在庫アイテムID |
| item_detail | object | 在庫アイテム詳細情報 |
| date | datetime | 記録日時 |
| tracking_type | integer | 履歴種別コード |
| label | string | 履歴種別のラベル（翻訳済み） |
| notes | string | メモ・備考 |
| user | integer | ユーザーID |
| user_detail | object | ユーザー詳細情報 |
| deltas | JSON | 変更内容の詳細 |

### deltas（変更内容）の構造

| キー | 型 | 説明 |
|------|-----|------|
| quantity | decimal | 変更後数量 |
| added | decimal | 追加数量 |
| removed | decimal | 削除数量 |
| location | integer | 移動先ロケーションID |
| location_detail | object | ロケーション詳細（API応答時に付加） |
| status | integer | 変更後ステータス |
| old_status | integer | 変更前ステータス |
| part | integer | 関連部品ID |
| part_detail | object | 部品詳細（API応答時に付加） |
| customer | integer | 顧客ID |
| customer_detail | object | 顧客詳細（API応答時に付加） |
| purchaseorder | integer | 発注ID |
| purchaseorder_detail | object | 発注詳細（API応答時に付加） |
| salesorder | integer | 受注ID |
| salesorder_detail | object | 受注詳細（API応答時に付加） |
| returnorder | integer | 返品オーダーID |
| returnorder_detail | object | 返品オーダー詳細（API応答時に付加） |
| buildorder | integer | 製造オーダーID |
| buildorder_detail | object | 製造オーダー詳細（API応答時に付加） |
| stockitem | integer | 関連在庫アイテムID |
| stockitem_detail | object | 関連在庫詳細（API応答時に付加） |

### 出力先

- 画面表示（在庫追跡タブ）
- REST API レスポンス
- CSVエクスポート

## 処理フロー

### 処理シーケンス

```
1. 履歴自動記録（在庫操作時）
   └─ 在庫操作実行 → add_tracking_entry呼び出し → deltas生成 → DB挿入

2. 履歴一覧取得
   └─ APIリクエスト → クエリ実行 → deltas詳細解決 → レスポンス返却

3. 履歴詳細表示
   └─ 履歴エントリ取得 → deltas解析 → 関連オブジェクト取得 → 表示
```

### フローチャート

```mermaid
flowchart TD
    subgraph 履歴記録フロー
        A[在庫操作実行] --> B[add_tracking_entry呼び出し]
        B --> C[tracking_type設定]
        C --> D[deltas生成]
        D --> E[StockItemTracking作成]
        E --> F[DB保存]
    end

    subgraph 履歴照会フロー
        G[API Request] --> H[StockTrackingList]
        H --> I[クエリ実行]
        I --> J[deltas解析]
        J --> K[関連オブジェクト取得]
        K --> L{キャッシュあり?}
        L -->|Yes| M[キャッシュから取得]
        L -->|No| N[DB照会]
        M --> O[詳細付加]
        N --> O
        O --> P[レスポンス返却]
    end
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 自動記録 | 在庫操作時は自動的に履歴エントリを作成 | 全ての在庫変更操作 |
| BR-002 | 読み取り専用 | 過去の履歴エントリは編集・削除不可 | 全ての履歴エントリ |
| BR-003 | 空エントリ防止 | deltasが空でnotesもない場合はSTOCK_UPDATEエントリを作成しない | STOCK_UPDATE時 |
| BR-004 | ユーザー追跡 | 操作ユーザーを必ず記録 | システム操作を除く |
| BR-005 | 時系列順 | 履歴はデフォルトで日時降順で表示 | 履歴一覧表示時 |

### 履歴種別（StockHistoryCode）

| コード | 値 | 説明 |
|--------|-----|------|
| LEGACY | 0 | レガシー追跡エントリ |
| CREATED | 1 | 在庫アイテム作成 |
| EDITED | 5 | 在庫アイテム編集 |
| ASSIGNED_SERIAL | 6 | シリアル番号割り当て |
| STOCK_COUNT | 10 | 棚卸 |
| STOCK_ADD | 11 | 数量手動追加 |
| STOCK_REMOVE | 12 | 数量手動削除 |
| STOCK_SERIALIZED | 13 | シリアル化 |
| RETURNED_TO_STOCK | 15 | 在庫返却 |
| STOCK_MOVE | 20 | ロケーション移動 |
| STOCK_UPDATE | 25 | 在庫更新 |
| INSTALLED_INTO_ASSEMBLY | 30 | アセンブリへ組込 |
| REMOVED_FROM_ASSEMBLY | 31 | アセンブリから取外 |
| INSTALLED_CHILD_ITEM | 35 | 子アイテム組込 |
| REMOVED_CHILD_ITEM | 36 | 子アイテム取外 |
| SPLIT_FROM_PARENT | 40 | 親アイテムから分割 |
| SPLIT_CHILD_ITEM | 42 | 子アイテム分割 |
| MERGED_STOCK_ITEMS | 45 | 在庫統合 |
| CONVERTED_TO_VARIANT | 48 | バリアント変換 |
| BUILD_OUTPUT_CREATED | 50 | 製造アウトプット作成 |
| BUILD_OUTPUT_COMPLETED | 55 | 製造アウトプット完了 |
| BUILD_OUTPUT_REJECTED | 56 | 製造アウトプット却下 |
| BUILD_CONSUMED | 57 | 製造消費 |
| SHIPPED_AGAINST_SALES_ORDER | 60 | 販売注文出荷 |
| RECEIVED_AGAINST_PURCHASE_ORDER | 70 | 発注受領 |
| RETURNED_AGAINST_RETURN_ORDER | 80 | 返品オーダー処理 |
| SENT_TO_CUSTOMER | 100 | 顧客送付 |
| RETURNED_FROM_CUSTOMER | 105 | 顧客返品 |

### 計算ロジック

```python
# 履歴エントリ作成
def add_tracking_entry(
    self,
    entry_type: StockHistoryCode,
    user: User | None,
    deltas: dict | None = None,
    notes: str = '',
    **kwargs
) -> StockItemTracking | None:
    if deltas is None:
        deltas = {}

    # 空エントリ防止
    if entry_type == StockHistoryCode.STOCK_UPDATE and len(deltas) == 0 and not notes:
        return None

    # ロケーション情報の追加
    location = kwargs.get('location')
    if location:
        deltas['location'] = location.id

    # 数量情報の追加
    quantity = kwargs.get('quantity')
    if quantity:
        deltas['quantity'] = float(quantity)

    entry = StockItemTracking(
        item=self,
        tracking_type=entry_type.value,
        user=user,
        date=current_time(),
        notes=notes,
        deltas=deltas,
    )
    entry.save()
    return entry

# ラベル取得
def label(self):
    if self.tracking_type in StockHistoryCode.keys():
        return StockHistoryCode.label(self.tracking_type)
    return getattr(self, 'title', '')
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 履歴記録 | stock_stockitemtracking | INSERT | 追跡エントリ挿入 |
| 履歴一覧 | stock_stockitemtracking | SELECT | 追跡エントリ取得 |
| 一括履歴記録 | stock_stockitemtracking | BULK INSERT | 複数エントリの一括挿入 |

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

#### stock_stockitemtracking

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | item_id, tracking_type, user_id, date, notes, deltas | 在庫操作時の情報 | 自動記録 |
| SELECT | 全カラム | item, user, 日付範囲でフィルタ | ソート: -date |

### 関連テーブル参照

履歴照会時にdeltasに含まれるIDから関連オブジェクトを取得する。

| deltaキー | 参照テーブル | 用途 |
|-----------|-------------|------|
| location | stock_stocklocation | ロケーション詳細 |
| part | part_part | 部品詳細 |
| customer | company_company | 顧客詳細 |
| purchaseorder | order_purchaseorder | 発注詳細 |
| salesorder | order_salesorder | 受注詳細 |
| returnorder | order_returnorder | 返品オーダー詳細 |
| buildorder | build_build | 製造オーダー詳細 |
| stockitem | stock_stockitem | 関連在庫詳細 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ValidationError | バリデーションエラー | 存在しない在庫アイテムID | 正しい在庫アイテムIDを指定 |
| ValidationError | バリデーションエラー | 無効なtracking_type | StockHistoryCodeの有効値を使用 |
| PermissionDenied | 権限エラー | 手動エントリ追加権限なし | 権限を付与 |

### リトライ仕様

特になし（履歴記録は在庫操作と同一トランザクション内で実行）

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

- 履歴エントリの作成は在庫操作と同一トランザクション内で実行
- 一括記録時はbulk_createを使用して効率化
- 履歴の改ざん防止のため、更新・削除操作は基本的に許可しない

## パフォーマンス要件

- 履歴一覧取得: 1000件で1秒以内
- 履歴エントリ作成: 在庫操作のオーバーヘッドとして10ms以内
- 関連オブジェクト詳細取得: キャッシュによる最適化

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

- 履歴の改ざん防止（読み取り専用）
- 操作ユーザーの自動記録
- ユーザー権限に基づくアクセス制御
- 監査対応のための完全な操作ログ

## 備考

- StockItemTrackingはStockItemのrelated_name='tracking_info'で関連付け
- deltasはJSONフィールドで柔軟な変更内容を格納
- APIはデフォルトで-date（日時降順）ソート
- フロントエンドはStockTrackingTableコンポーネントで表示

---

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

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

### 推奨読解順序

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

StockItemTrackingモデルの構造と履歴種別コードを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | models.py | `src/backend/InvenTree/stock/models.py` | StockItemTrackingクラス（2869-2928行目） |
| 1-2 | status_codes.py | `src/backend/InvenTree/stock/status_codes.py` | StockHistoryCode列挙型（41-98行目） |

**読解のコツ**:
- StockItemTrackingのフィールド構造（item, tracking_type, date, notes, user, deltas）を理解
- tracking_typeはStockHistoryCodeの値を格納
- deltasはJSONフィールドで変更内容を柔軟に格納

**主要処理フロー**:
- **2869-2887行目**: クラス定義とドキュメント（属性一覧）
- **2894-2897行目**: get_api_url（APIエンドポイント）
- **2903-2908行目**: labelプロパティ（履歴種別ラベル取得）
- **2910-2928行目**: フィールド定義（tracking_type, item, date, notes, user, deltas）

#### Step 2: 履歴記録処理を理解する

StockItemモデルのadd_tracking_entryメソッドを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | models.py | `src/backend/InvenTree/stock/models.py` | add_tracking_entryメソッド（1794-1848行目） |

**主要処理フロー**:
- **1794-1812行目**: メソッド定義とドキュメント
- **1813-1822行目**: 空エントリ防止ロジック
- **1824-1834行目**: deltasへのlocation, quantity追加
- **1836-1848行目**: StockItemTrackingオブジェクト作成・保存

#### Step 3: API処理を理解する

履歴一覧・詳細取得のAPI処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | api.py | `src/backend/InvenTree/stock/api.py` | StockTrackingList, StockTrackingDetail（1471-1601行目） |
| 3-2 | serializers.py | `src/backend/InvenTree/stock/serializers.py` | StockTrackingSerializer（1236-1274行目） |

**主要処理フロー**:
- **1471-1475行目**: StockTrackingDetail（単一エントリ取得）
- **1487-1601行目**: StockTrackingList（一覧取得）
- **1502-1518行目**: get_delta_model_map（関連オブジェクトのマッピング定義）
- **1520-1565行目**: listメソッド（関連オブジェクト詳細の付加処理）
- **1567-1591行目**: createメソッド（手動エントリ作成）

#### Step 4: フロントエンド処理を理解する

履歴表示のUIコンポーネントを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | StockTrackingTable.tsx | `src/frontend/src/tables/stock/StockTrackingTable.tsx` | 履歴テーブルコンポーネント（1-260行目） |
| 4-2 | StockDetail.tsx | `src/frontend/src/pages/stock/StockDetail.tsx` | 在庫詳細画面での履歴タブ表示（89行目, 531-535行目） |

**主要処理フロー**:
- **37-39行目**: StockTrackingTable関数コンポーネント定義
- **42-198行目**: renderDetails（deltas内容の詳細表示）
- **201-209行目**: filters（ユーザーフィルタ定義）
- **211-243行目**: tableColumns（テーブルカラム定義）
- **245-259行目**: InvenTreeTableの描画

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

```
REST API Request (GET /api/stock/track/)
    │
    ├─ StockTrackingList (api.py:1487)
    │      │
    │      ├─ get_queryset()
    │      │      └─ StockItemTracking.objects.all()
    │      │
    │      ├─ filter_queryset()
    │      │      └─ item, user, searchでフィルタ
    │      │
    │      ├─ paginate_queryset()
    │      │
    │      ├─ get_serializer()
    │      │      └─ StockTrackingSerializer
    │      │
    │      └─ list() カスタム処理
    │             ├─ deltas解析
    │             ├─ get_delta_model_map()
    │             │      └─ 関連モデルのマッピング取得
    │             ├─ 関連モデル一括取得
    │             │      └─ Model.objects.filter(pk__in=...)
    │             └─ deltas拡張（*_detail追加）
    │
    └─ Response

在庫操作時の履歴記録
    │
    ├─ StockItem操作メソッド
    │      └─ add_tracking_entry()
    │             │
    │             ├─ 空エントリチェック
    │             │
    │             ├─ deltas構築
    │             │      ├─ location
    │             │      └─ quantity
    │             │
    │             ├─ StockItemTracking()
    │             │
    │             └─ entry.save()
    │
    └─ 一括記録
           └─ StockItemTracking.objects.bulk_create()
```

### データフロー図

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

                      ┌─────────────────────────────────┐
在庫操作             │    add_tracking_entry()         │   stock_stockitemtracking
(移動/追加/削除等) ──▶│                                 │──▶ テーブルへINSERT
                      │  ├─ entry_type設定              │
                      │  ├─ deltas生成                  │
                      │  │   ├─ location               │
                      │  │   ├─ quantity               │
                      │  │   └─ その他変更情報          │
                      │  └─ StockItemTracking保存      │
                      └─────────────────────────────────┘

                      ┌─────────────────────────────────┐
APIリクエスト        │    StockTrackingList            │   JSONレスポンス
(履歴一覧取得) ─────▶│                                 │──▶（deltas詳細付き）
                      │  ├─ フィルタ適用               │
                      │  │   ├─ item                   │
                      │  │   └─ user                   │
                      │  ├─ ページネーション            │
                      │  └─ deltas拡張                 │
                      │      ├─ location_detail        │
                      │      ├─ part_detail            │
                      │      ├─ customer_detail        │
                      │      ├─ purchaseorder_detail   │
                      │      ├─ salesorder_detail      │
                      │      ├─ returnorder_detail     │
                      │      └─ buildorder_detail      │
                      └─────────────────────────────────┘

                      ┌─────────────────────────────────┐
フロントエンド       │    StockTrackingTable           │   画面表示
(履歴タブ表示) ─────▶│                                 │──▶（テーブル形式）
                      │  ├─ useTable()                  │
                      │  ├─ APIコール                   │
                      │  ├─ renderDetails()            │
                      │  │   └─ deltas内容の展開       │
                      │  └─ InvenTreeTable             │
                      └─────────────────────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| models.py | `src/backend/InvenTree/stock/models.py` | ソース | StockItemTracking定義、add_tracking_entry実装 |
| status_codes.py | `src/backend/InvenTree/stock/status_codes.py` | ソース | StockHistoryCode列挙型 |
| api.py | `src/backend/InvenTree/stock/api.py` | ソース | StockTrackingList, StockTrackingDetail |
| serializers.py | `src/backend/InvenTree/stock/serializers.py` | ソース | StockTrackingSerializer |
| StockTrackingTable.tsx | `src/frontend/src/tables/stock/StockTrackingTable.tsx` | ソース | 履歴テーブルコンポーネント |
| StockDetail.tsx | `src/frontend/src/pages/stock/StockDetail.tsx` | ソース | 在庫詳細画面（履歴タブ含む） |
| admin.py | `src/backend/InvenTree/stock/admin.py` | ソース | StockTrackingAdmin（管理画面） |
| tests.py | `src/backend/InvenTree/stock/tests.py` | テスト | 履歴機能のテストケース |
| test_api.py | `src/backend/InvenTree/stock/test_api.py` | テスト | API関連テストケース |
