# 通知設計書 4-購買発注アイテム受領通知

## 概要

本ドキュメントは、InvenTreeシステムにおける「購買発注アイテム受領通知」の設計を定義する。この通知は、購買発注（Purchase Order）のアイテムが受領された際に、担当者および関係者に送信される。

### 本通知の処理概要

本通知は、購買発注の明細アイテムが倉庫に受領された際に、担当者および関係者に対して受領完了を通知する機能を提供する。

**業務上の目的・背景**：購買発注のアイテムが実際に受領されたことを関係者に通知し、在庫の入庫確認、品質検査の実施、後続の製造工程への材料供給、請求書処理などの業務を円滑に進めることが目的である。受領情報の伝達漏れによる業務の遅延を防止する。

**通知の送信タイミング**：購買発注の`receive_line_items()`メソッドが呼び出され、在庫アイテムが正常に受領された時点でトリガーされる。具体的にはメソッドの最後で`notify_responsible()`が呼び出される。

**通知の受信者**：購買発注の`responsible`フィールドに設定されたユーザーまたはグループ、および受領されたパーツの購読者（`part.get_subscribers()`）が受信者となる。受領操作を実行したユーザーは通知から除外される。

**通知内容の概要**：購買発注のアイテムが受領されたことを伝えるメッセージ、発注の詳細ページへのリンクが含まれる。件名は「Items Received」となる。

**期待されるアクション**：受信者は通知を確認後、受領内容の確認、品質検査の実施、在庫配置の確認、製造指示への材料供給、サプライヤーへの受領確認連絡などを行うことが期待される。

## 通知種別

- メール通知
- アプリ内通知（UI通知）
- Slack通知（設定されている場合）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（プラグインベース） |
| 優先度 | 中 |
| リトライ | プラグイン実装に依存 |

### 送信先決定ロジック

1. `PurchaseOrder.responsible` - 発注の責任者（ユーザーまたはグループ）
2. `line.part.part.get_subscribers()` - 受領されたパーツの購読者
3. 上記から受領操作実行ユーザーを除外

```python
# 送信先決定ロジック（order/models.py 行1189-1195）
notify_responsible(
    self,
    PurchaseOrder,
    exclude=user,
    content=InvenTreeNotificationBodies.ItemsReceived,
    extra_users=line.part.part.get_subscribers(),
)
```

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | システム設定に依存 |
| 送信元名称 | InvenTree |
| 件名 | Items Received |
| 形式 | HTML |

### 本文テンプレート

```html
<!-- templates/email/purchase_order_received.html -->
{% extends "email/email.html" %}

{% load i18n %}
{% load inventree_extras %}

{% block title %}
{{ message }}
{% if link %}
<p>{% trans "Click on the following link to view this order" %}: <a href='{{ link }}'>{{ link }}</a></p>
{% endif %}
{% endblock title %}
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| なし | - | - | 本通知に添付ファイルはない |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| message | 通知メッセージ | InvenTreeNotificationBodies.ItemsReceived.message | Yes |
| link | 発注詳細ページURL | construct_absolute_url(instance.get_absolute_url()) | Yes |
| instance | 発注オブジェクト | PurchaseOrderインスタンス | Yes |
| name | 通知名（件名） | "Items Received" | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 画面操作/API | PurchaseOrder.receive_line_items() | ステータスがPLACED | アイテムが受領された時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| データインポート中 | `isImportingData()`がTrueの場合 |
| データリビルド中 | `isRebuildingData()`がTrueの場合 |
| マイグレーション実行中 | `isRunningMigrations()`がTrueの場合 |
| 最近の通知 | 同一カテゴリ・オブジェクトへの通知が1日以内にある場合（check_recent） |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[receive_line_items呼び出し] --> B{status == PLACED?}
    B -->|No| Z[ValidationError]
    B -->|Yes| C[受領アイテム処理]
    C --> D[StockItem作成]
    D --> E[tracking_entry作成]
    E --> F[line.received更新]
    F --> G[trigger_event発火]
    G --> H{auto_complete?}
    H -->|Yes| I[complete_order]
    H -->|No| J[notify_responsible呼び出し]
    I --> J
    J --> K[送信先ユーザー収集]
    K --> L[context構築]
    L --> M[trigger_notification呼び出し]
    M --> N[終了]
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| order_purchaseorder | 発注情報取得 | 主テーブル |
| order_purchaseorderlineitem | 発注明細取得 | 受領対象アイテム |
| company_supplierpart | サプライヤーパーツ情報 | パーツ変換用 |
| part_part | パーツ情報 | 購読者取得用 |
| users_owner | 責任者情報取得 | responsible参照 |
| auth_user | ユーザー情報取得 | 通知先ユーザー |
| common_notificationentry | 重複通知チェック | check_recent用 |

### テーブル別参照項目詳細

#### order_purchaseorder

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| id | 発注識別 | 対象発注 |
| reference | 発注番号 | 通知表示用 |
| status | ステータス | PLACED状態確認 |
| responsible_id | 責任者 | 通知先決定 |

#### order_purchaseorderlineitem

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| id | 明細ID | 受領対象 |
| part_id | サプライヤーパーツ | 在庫作成用 |
| quantity | 発注数量 | 受領数量確認 |
| received | 受領済み数量 | 更新対象 |
| purchase_price | 購入単価 | 在庫価格設定 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| stock_stockitem | INSERT | 受領在庫アイテム作成 |
| stock_stockitemtracking | INSERT | 在庫追跡エントリ作成 |
| order_purchaseorderlineitem | UPDATE | received数量更新 |
| order_purchaseorder | UPDATE | ステータス更新（auto_complete時） |
| common_notificationentry | INSERT | 通知履歴記録 |
| common_notificationmessage | INSERT | UI通知メッセージ保存 |

#### 送信ログテーブル

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| INSERT | category | "purchase_order.items_received" | 通知カテゴリ |
| INSERT | key | PurchaseOrder.pk | 対象オブジェクトID |
| INSERT | updated | 現在日時 | 通知日時 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ValidationError | ステータスがPLACED以外 | エラーメッセージ表示、処理中止 |
| ValidationError | 数量が負数 | エラーメッセージ表示、処理中止 |
| 送信失敗 | プラグインエラー | log_errorで記録、他プラグインは継続 |
| テンプレートエラー | テンプレート不正 | 例外キャッチしてログ記録 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | プラグイン実装依存 |
| リトライ間隔 | プラグイン実装依存 |
| リトライ対象エラー | 一時的な送信エラー |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 同一オブジェクト・カテゴリに対して1回（check_recent=1日） |

### 配信時間帯

制限なし（受領時に即時送信）

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

- 通知受信者は対象オブジェクトへの`view`権限が必要（`check_user_permission`で検証）
- 非アクティブユーザー（is_active=False）は通知対象から除外
- 発注情報へのリンクはシステム設定のベースURLを使用して構築

## 備考

- `PURCHASEORDER_AUTO_COMPLETE`設定がTrueで全明細受領済みの場合、発注は自動完了
- 受領処理は`@transaction.atomic`でトランザクション管理
- シリアル番号付きアイテムは個別のStockItemとして作成
- バルク作成（`bulk_create`）で効率化

---

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

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

### 推奨読解順序

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

通知の基本データ構造である`InvenTreeNotificationBodies.ItemsReceived`を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | notifications.py | `src/backend/InvenTree/common/notifications.py` | ItemsReceivedの定義（行72-77） |

**読解のコツ**: `slug`が`purchase_order.items_received`で固定されている点に注意。

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

購買発注の受領処理がどこから始まるかを特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | models.py | `src/backend/InvenTree/order/models.py` | PurchaseOrder.receive_line_items()メソッド（行934-1200） |

**主要処理フロー**:
1. **行958-961**: ステータスがPLACEDか検証
2. **行964-970**: 在庫アイテム作成準備
3. **行1139-1141**: StockItem一括作成
4. **行1167-1168**: tracking_entry作成
5. **行1171**: LineItem更新
6. **行1180-1186**: auto_complete処理
7. **行1189-1195**: notify_responsible呼び出し

#### Step 3: 通知送信処理を理解する

実際の通知送信処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | helpers_model.py | `src/backend/InvenTree/InvenTree/helpers_model.py` | notify_responsible()関数（行297-326） |
| 3-2 | notifications.py | `src/backend/InvenTree/common/notifications.py` | trigger_notification()関数（行87-218） |

#### Step 4: イベント発火を理解する

受領時のプラグインイベントを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | events.py | `src/backend/InvenTree/order/events.py` | PurchaseOrderEvents.ITEM_RECEIVED（行16） |

#### Step 5: メールテンプレートを理解する

メール本文のテンプレートを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | purchase_order_received.html | `src/backend/InvenTree/templates/email/purchase_order_received.html` | メール本文テンプレート |

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

```
PurchaseOrder.receive_line_items() [order/models.py:934]
    │
    ├─ ステータス検証（PLACED必須）
    │
    ├─ for item in items:
    │      ├─ StockItem作成準備
    │      └─ line.received更新
    │
    ├─ StockItem.objects.bulk_create()
    │
    ├─ StockItemTracking.objects.bulk_create()
    │
    ├─ PurchaseOrderLineItem.objects.bulk_update()
    │
    ├─ trigger_event(PurchaseOrderEvents.ITEM_RECEIVED)
    │
    ├─ [条件] complete_order() (auto_complete時)
    │
    └─ notify_responsible() [InvenTree/helpers_model.py:297]
           │
           └─ notify_users() [InvenTree/helpers_model.py:329]
                  │
                  └─ trigger_notification() [common/notifications.py:87]
                         │
                         └─ plugin.send_notification() [各通知プラグイン]
```

### データフロー図

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

受領リクエスト              receive_line_items()               StockItem作成
  ├─ location        ───▶     │                          ───▶ tracking記録
  ├─ items[]                  ├─ StockItem作成                 通知送信
  │   ├─ line_item            ├─ tracking記録                  (メール/UI/Slack)
  │   ├─ quantity             ├─ LineItem更新
  │   └─ ...                  └─ notify_responsible()
  └─ user
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| models.py | `src/backend/InvenTree/order/models.py` | ソース | PurchaseOrderモデル、receive_line_items()実装 |
| notifications.py | `src/backend/InvenTree/common/notifications.py` | ソース | ItemsReceived定義、trigger_notification() |
| helpers_model.py | `src/backend/InvenTree/InvenTree/helpers_model.py` | ソース | notify_responsible()ヘルパー |
| events.py | `src/backend/InvenTree/order/events.py` | ソース | PurchaseOrderEvents.ITEM_RECEIVEDイベント定義 |
| purchase_order_received.html | `src/backend/InvenTree/templates/email/purchase_order_received.html` | テンプレート | メール本文テンプレート |
| stock/models.py | `src/backend/InvenTree/stock/models.py` | ソース | StockItem, StockItemTrackingモデル |
