# 通知設計書 14-製造指示キャンセル通知

## 概要

本ドキュメントは、InvenTreeシステムにおける「製造指示キャンセル通知」の設計仕様を定義する。製造指示（Build Order）がキャンセルされた際に、担当者へ通知を送信する機能について詳述する。

### 本通知の処理概要

製造指示キャンセル通知は、Build Orderがキャンセルステータスに遷移した際に、責任者および関連するパーツの購読者に自動的に通知を送信する機能である。

**業務上の目的・背景**：製造指示のキャンセルは、計画変更や資材不足、優先度の変更など様々な理由で発生する。キャンセル通知により、関係者は割り当てられた資材の解放、作業スケジュールの再調整、他の製造指示への資源再配分などの対応を迅速に行うことができる。

**通知の送信タイミング**：Build._action_cancel()メソッド内で、製造指示が正常にキャンセル処理された後にトリガーされる。具体的には、ステータスがCANCELLEDに変更され、割当済み在庫の処理が完了した後に送信される。

**通知の受信者**：製造指示の責任者（responsible）、および製造対象パーツの購読者（subscribers）が受信対象となる。製造指示を発行したユーザー（issued_by）は除外される。

**通知内容の概要**：製造指示がキャンセルされたことを通知する。InvenTreeNotificationBodies.OrderCanceledテンプレートを使用し、製造指示の詳細へのリンクを含む。

**期待されるアクション**：受信者は割当済みの資材・作業リソースを確認し、解放または他の製造指示への再割当を行う。関連する販売注文への影響を確認し、必要に応じて顧客への連絡を行う。

## 通知種別

- メール通知（InvenTreeEmailNotifications経由）
- UI通知（InvenTreeUINotifications経由）
- Slack通知（InvenTreeSlackNotifications経由、設定時）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（_action_cancel処理内で直接呼び出し） |
| 優先度 | 中 |
| リトライ | 無（通知送信失敗時はログ記録のみ） |

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

1. `notify_responsible()`関数により送信先を決定
2. Build Orderの`responsible`フィールドに設定されたOwner（ユーザーまたはグループ）
3. `extra_users`として製造対象パーツに購読しているユーザー（`self.part.get_subscribers()`）
4. 製造指示を発行したユーザー（issued_by）は`exclude`パラメータにより除外

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | システム設定による |
| 送信元名称 | [INVENTREE_INSTANCE] |
| 件名 | Build Order canceled（動的に生成） |
| 形式 | HTML |

### 本文テンプレート

```html
{% extends "email/email.html" %}

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

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| なし | - | - | 添付ファイルなし |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| name | 通知名（"{verbose_name} canceled"） | InvenTreeNotificationBodies.OrderCanceled | Yes |
| message | 通知メッセージ | InvenTreeNotificationBodies.OrderCanceled | Yes |
| link | 製造指示詳細ページへのリンク | construct_absolute_url(build.get_absolute_url()) | No |
| slug | 通知カテゴリスラッグ | 'build.canceled_build' | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 画面操作 | Build.cancel_build()呼び出し | 正常にキャンセル処理された場合 | 製造指示キャンセル操作 |
| API | POST /api/build/{id}/cancel/ | 同上 | REST API経由でのキャンセル操作 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| データインポート中 | isImportingData()がTrueの場合は通知をスキップ |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[cancel_build呼び出し] --> B[handle_transition]
    B --> C[_action_cancel実行]
    C --> D{remove_allocated_stock?}
    D -->|Yes| E[complete_build_allocations offload]
    D -->|No| F[allocated_stock.delete]
    E --> G{remove_incomplete_outputs?}
    F --> G
    G -->|Yes| H[build_outputs削除]
    G -->|No| I[ステータス更新]
    H --> I
    I --> J[notify_responsible呼び出し]
    J --> K[trigger_event発火]
    K --> L[終了]
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| build_build | 製造指示情報取得 | 責任者、発行者情報 |
| part_part | パーツ情報 | 購読者取得用 |
| users_owner | 責任者情報 | 通知先決定 |
| auth_user | ユーザー情報 | 送信先ユーザー |

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

#### build_build

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| responsible | 責任者取得 | 通知対象 |
| issued_by | 除外対象ユーザー | 発行者 |
| part | 製造対象パーツ | 購読者取得用 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| common_notificationentry | INSERT/UPDATE | 通知送信記録 |
| common_notificationmessage | INSERT | UI通知メッセージ |

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

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| INSERT/UPDATE | key | 'build.canceled_build' | 通知カテゴリ |
| INSERT/UPDATE | uid | Build.pk | 対象オブジェクトID |
| INSERT/UPDATE | updated | 現在日時 | 最終送信日時 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 送信失敗 | メール送信エラー | ログ記録、処理続行 |
| テンプレートエラー | テンプレート変数不正 | ログ記録、処理続行 |
| 宛先不正 | 宛先ユーザーが無効 | 該当ユーザーをスキップ |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | - |
| リトライ対象エラー | - |

## 配信設定

### レート制限

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

### 配信時間帯

制限なし（リアルタイム送信）

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

- 通知を受信できるのは、該当する権限（view権限）を持つユーザーのみ
- メール本文には機密情報を含まないよう、リンクのみを提供
- NOTIFY_BY_EMAILユーザー設定によりメール通知の受信可否を制御可能

## 備考

- OrderCanceled通知ボディは汎用的なオーダーキャンセル通知として使用
- 動的なスラッグ生成（'{app_label}.canceled_{model_name}'）により、Build Order固有のカテゴリが設定される
- キャンセル時のオプション（remove_allocated_stock、remove_incomplete_outputs）により処理が分岐

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | notifications.py | `src/backend/InvenTree/common/notifications.py` | OrderCanceled定義（行64-70） |
| 1-2 | models.py | `src/backend/InvenTree/build/models.py` | Buildモデルの構造 |

**読解のコツ**: OrderCanceledはf-string形式のテンプレートを使用し、'{verbose_name}'が実行時に"Build Order"に置換される。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | models.py | `src/backend/InvenTree/build/models.py` | Build.cancel_build()（行797-811）、_action_cancel()（行813-859） |

**主要処理フロー**:
1. **行817**: userパラメータ取得
2. **行819-835**: 割当在庫の処理（削除またはオフロード）
3. **行837-841**: 未完了アウトプットの削除（オプション）
4. **行843-848**: ステータス・完了日時更新
5. **行850-857**: notify_responsible呼び出し
6. **行859**: trigger_event(BuildEvents.CANCELLED)発火

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | helpers_model.py | `src/backend/InvenTree/InvenTree/helpers_model.py` | notify_responsible関数の実装 |

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

```
Build.cancel_build() [build/models.py:797]
    │
    └─ handle_transition()
           │
           └─ _action_cancel() [build/models.py:813]
                  │
                  ├─ [remove_allocated_stock] offload_task(complete_build_allocations)
                  │    または
                  ├─ allocated_stock.all().delete()
                  │
                  ├─ [remove_incomplete_outputs] build_outputs.filter().delete()
                  │
                  ├─ self.save()
                  │
                  ├─ notify_responsible() [InvenTree/helpers_model.py]
                  │      │
                  │      ├─ Build.responsible → 責任者取得
                  │      ├─ Build.part.get_subscribers() → 購読者取得
                  │      └─ trigger_notification()
                  │
                  └─ trigger_event(BuildEvents.CANCELLED)
```

### データフロー図

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

cancel_build() ───▶ _action_cancel() ───▶ Build更新(CANCELLED)
                              │                        │
                              ├─ 在庫割当解放 ────▶ BuildItem削除
                              │                        │
                              ├─ notify_responsible ───▶ 通知送信
                              │                        │
                              └─ trigger_event ───▶ BuildEvents.CANCELLED
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| models.py | `src/backend/InvenTree/build/models.py` | ソース | Buildモデル、cancel_build実装 |
| notifications.py | `src/backend/InvenTree/common/notifications.py` | ソース | OrderCanceled定義 |
| helpers_model.py | `src/backend/InvenTree/InvenTree/helpers_model.py` | ソース | notify_responsible関数 |
| canceled_order_assigned.html | `src/backend/InvenTree/templates/email/canceled_order_assigned.html` | テンプレート | メール本文テンプレート |
