# 機能設計書 12-発注書管理

## 概要

本ドキュメントは、InvenTreeシステムにおける発注書管理機能の設計内容を記述した機能設計書である。サプライヤーへの発注書の作成・編集・発行・受領・完了までのライフサイクル管理について詳細を定義する。

### 本機能の処理概要

**業務上の目的・背景**：
製造業や在庫管理において、部品や原材料の調達は重要な業務プロセスである。サプライヤーへの発注を効率的に管理し、発注状況の追跡、受領処理、支払管理との連携を実現することで、調達業務の効率化と在庫の適正化を支援する。本機能は、発注書のライフサイクル全体を一元管理するための中核機能である。

**機能の利用シーン**：
- 在庫不足時にサプライヤーへ発注書を作成する場面
- 発注書を承認・発行してサプライヤーに送付する場面
- サプライヤーからの納品を受領し在庫に登録する場面
- 発注書の進捗状況を確認・追跡する場面
- 発注履歴を分析して調達計画を立てる場面

**主要な処理内容**：
1. 発注書の作成・編集・削除処理
2. 発注書のステータス遷移管理（PENDING→PLACED→COMPLETE/CANCELLED）
3. 発注書の発行処理と通知
4. 発注品の受領処理と在庫登録
5. 発注書の自動完了判定
6. 発注合計金額の計算

**関連システム・外部連携**：
- サプライヤー部品管理との連携
- 在庫管理との連携（受領時の在庫登録）
- 通知システムとの連携（発注通知、受領通知）
- プラグインシステム（イベントトリガー）

**権限による制御**：
- 発注書の作成・編集は購買担当者権限が必要
- 発注書の発行・完了は承認権限が必要
- 発注履歴の閲覧は購買関連の参照権限で可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 19 | 購買インデックス画面 | 主機能 | 発注書の一覧表示（テーブル/カレンダー/パラメトリック） |
| 20 | 発注書詳細画面 | 主機能 | 発注書の詳細表示・編集・発行・完了処理 |
| 25 | 会社詳細画面 | 遷移先機能 | サプライヤーの発注書一覧表示 |
| 26 | サプライヤー詳細画面 | 遷移先機能 | 発注書一覧の表示 |
| 14 | 部品詳細画面 | 遷移先機能 | 発注書一覧の表示・発注詳細への遷移 |
| 18 | 製造オーダー詳細画面 | 遷移先機能 | 外部発注書一覧の表示 |

## 機能種別

CRUD操作 / ステータス管理 / 計算処理 / イベント通知

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| reference | CharField | Yes | 発注書番号（最大64文字、一意） | パターン検証、重複チェック |
| supplier | ForeignKey | Yes | サプライヤー（Company） | is_supplier=Trueの会社のみ |
| description | CharField | No | 発注書説明（最大250文字） | - |
| supplier_reference | CharField | No | サプライヤー発注番号（最大64文字） | - |
| target_date | DateField | No | 納品予定日 | start_dateより後 |
| start_date | DateField | No | 開始日 | target_dateより前 |
| destination | ForeignKey | No | 受入先ロケーション | StockLocation |
| contact | ForeignKey | No | 担当連絡先 | サプライヤーに属するContact |
| address | ForeignKey | No | 配送先住所 | サプライヤーに属するAddress |
| responsible | ForeignKey | No | 責任者 | Owner（ユーザーまたはグループ） |
| project_code | ForeignKey | No | プロジェクトコード | ProjectCode |

### 入力データソース

- 画面入力（発注書作成・編集フォーム）
- APIリクエスト
- 外部システムからのインポート

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| reference | str | 発注書番号 |
| status | int | ステータスコード |
| status_text | str | ステータス表示テキスト |
| total_price | Money | 発注合計金額 |
| line_count | int | 明細行数 |
| pending_line_count | int | 未完了明細行数 |
| completed_line_count | int | 完了明細行数 |
| is_overdue | bool | 期限超過フラグ |

### 出力先

- order_purchaseorderテーブル
- 通知システム（発注イベント）
- レポート・帳票出力

## 処理フロー

### 処理シーケンス

```
1. 発注書の作成
   └─ reference自動採番
   └─ サプライヤー選択
   └─ バリデーション実行

2. 発注明細の追加
   └─ サプライヤー部品の選択
   └─ 数量・単価の入力
   └─ 合計金額の再計算

3. 発注書の発行（PENDING→PLACED）
   └─ ステータス変更
   └─ 発行日の記録
   └─ 関係者への通知

4. 発注品の受領
   └─ 明細ごとの受領処理
   └─ 在庫アイテムの作成
   └─ 受領数量の記録

5. 発注書の完了（PLACED→COMPLETE）
   └─ 全明細の受領完了チェック
   └─ ステータス変更
   └─ 完了日の記録
```

### フローチャート

```mermaid
flowchart TD
    A[発注書作成] --> B{バリデーションOK?}
    B -->|No| C[エラー表示]
    B -->|Yes| D[PENDING状態で保存]
    D --> E[明細追加]
    E --> F{発行可能?}
    F -->|No| E
    F -->|Yes| G[発行処理]
    G --> H[PLACED状態]
    H --> I[受領処理]
    I --> J{全明細完了?}
    J -->|No| I
    J -->|Yes| K{自動完了設定?}
    K -->|Yes| L[COMPLETE状態]
    K -->|No| M[手動完了待ち]
    M --> L

    H --> N[キャンセル]
    N --> O[CANCELLED状態]
    D --> N
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-12-01 | 参照番号一意性 | 発注書番号（reference）はシステム全体で一意 | 発注書作成・編集時 |
| BR-12-02 | サプライヤー制約 | サプライヤーはis_supplier=Trueの会社のみ | サプライヤー選択時 |
| BR-12-03 | ステータス遷移 | PENDING→PLACED→COMPLETE、またはOPEN状態からCANCELLED | ステータス変更時 |
| BR-12-04 | 発行要件 | PENDINGまたはON_HOLD状態からのみ発行可能 | 発行処理時 |
| BR-12-05 | 完了要件 | PLACED状態からのみ完了可能 | 完了処理時 |
| BR-12-06 | 自動完了 | 全明細受領完了時に自動でCOMPLETE（設定依存） | 受領処理後 |
| BR-12-07 | 責任者必須 | 設定により責任者を必須化可能 | PURCHASEORDER_REQUIRE_RESPONSIBLE設定時 |
| BR-12-08 | ロック制御 | 完了済み発注書の編集可否は設定で制御 | PURCHASEORDER_EDIT_COMPLETED_ORDERS設定 |

### 計算ロジック

**発注合計金額（total_price）**:
```python
def calculate_total_price(target_currency):
    total = Money(0, target_currency)
    for line in self.lines.all():
        if line.price:
            total += line.quantity * convert_money(line.price, target_currency)
    for line in self.extra_lines.all():
        if line.price:
            total += line.quantity * convert_money(line.price, target_currency)
    return total
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 発注書作成 | order_purchaseorder | INSERT | 新規発注書レコードの作成 |
| 発注書編集 | order_purchaseorder | UPDATE | 発注書情報の更新 |
| 発注書発行 | order_purchaseorder | UPDATE | status→PLACED、issue_date設定 |
| 発注書完了 | order_purchaseorder | UPDATE | status→COMPLETE、complete_date設定 |
| 発注書キャンセル | order_purchaseorder | UPDATE | status→CANCELLED |
| 合計金額更新 | order_purchaseorder | UPDATE | total_priceフィールドの更新 |

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

#### order_purchaseorder

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | reference | 発注書番号 | UNIQUE制約 |
| INSERT/UPDATE | supplier_id | サプライヤーID | FK: company_company |
| UPDATE | status | ステータスコード | 10:PENDING, 20:PLACED, 25:ON_HOLD, 30:COMPLETE, 40:CANCELLED |
| UPDATE | issue_date | 発行日 | 発行処理時に設定 |
| UPDATE | complete_date | 完了日 | 完了処理時に設定 |
| UPDATE | total_price | 合計金額 | 保存時に再計算 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| reference_duplicate | ValidationError | 発注書番号が重複 | 別の番号を指定 |
| supplier_invalid | ValidationError | サプライヤーがis_supplier=False | 正しいサプライヤーを選択 |
| contact_mismatch | ValidationError | 連絡先がサプライヤーに属さない | サプライヤーの連絡先を選択 |
| address_mismatch | ValidationError | 住所がサプライヤーに属さない | サプライヤーの住所を選択 |
| status_transition_invalid | ValidationError | 不正なステータス遷移 | 正しい遷移パスを確認 |
| order_locked | ValidationError | 完了済み発注書の編集試行 | 設定を確認するか新規作成 |

### リトライ仕様

- データベース一時エラー：トランザクション単位でリトライ
- 通知送信エラー：バックグラウンドタスクで自動リトライ

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

- 発注書の作成・編集はトランザクション内で実行
- ステータス遷移処理はトランザクション内で完結
- 受領処理は@transaction.atomicで保護
- 合計金額の再計算は保存時に自動実行

## パフォーマンス要件

- 発注書一覧表示：1秒以内（100件まで）
- 発注書詳細表示：500ms以内
- ステータス変更：500ms以内
- 合計金額計算：明細100件で1秒以内

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

- 発注書の変更は監査ログに記録
- ステータス遷移はイベントトリガーで追跡
- 完了済み発注書のロック機能で改ざん防止
- 責任者設定による承認フロー実装可能

## 備考

- 発注書番号のパターンはPURCHASEORDER_REFERENCE_PATTERN設定でカスタマイズ可能
- 外部ビルドオーダーとの連携により、外注製造の管理も可能

---

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

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

### 推奨読解順序

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

まず、発注書に関連するデータモデルの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | models.py | `src/backend/InvenTree/order/models.py` | PurchaseOrderクラスの定義 |
| 1-2 | status_codes.py | `src/backend/InvenTree/order/status_codes.py` | PurchaseOrderStatusの定義 |

**読解のコツ**:
- **536-682行目**: PurchaseOrderクラスの定義（OrderクラスとTotalPriceMixinを継承）
- **612-620行目**: referenceフィールドの定義（unique=True、バリデーター付き）
- **622-628行目**: statusフィールドの定義（InvenTreeCustomStatusModelField）
- **635-643行目**: supplierフィールドの定義（is_supplier制約）

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

ステータス遷移の処理フローを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | models.py | `src/backend/InvenTree/order/models.py` | place_order(), complete_order()メソッド |
| 2-2 | models.py | `src/backend/InvenTree/order/models.py` | _action_place(), _action_complete()メソッド |

**主要処理フロー**:
1. **820-825行目**: place_order()でhandle_transitionを呼び出し
2. **762-781行目**: _action_place()でステータス変更と通知処理
3. **827-832行目**: complete_order()でhandle_transitionを呼び出し
4. **783-805行目**: _action_complete()でステータス変更と価格更新処理

#### Step 3: 受領処理を理解する

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

**主要処理フロー**:
- **933-1200行目**: receive_line_items()で一括受領処理
- **958-961行目**: PLACEDステータスのチェック
- **1107-1133行目**: StockItemの作成処理
- **1180-1186行目**: 自動完了判定

#### Step 4: 合計金額計算を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | models.py | `src/backend/InvenTree/order/models.py` | TotalPriceMixin.calculate_total_price() |

**主要処理フロー**:
- **128-179行目**: calculate_total_price()で明細・追加明細の合計を計算
- **147-158行目**: 明細行の価格を通貨変換して合計
- **161-174行目**: 追加明細行の価格を通貨変換して合計

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

```
PurchaseOrder.place_order()
    │
    └─ handle_transition()
           └─ _action_place()
                  ├─ status = PLACED
                  ├─ issue_date = current_date()
                  ├─ trigger_event(PLACED)
                  └─ notify_responsible()

PurchaseOrder.receive_line_items()
    │
    ├─ バリデーション
    │      └─ status == PLACED チェック
    │
    ├─ StockItem作成
    │      ├─ _create_serial_numbers() [シリアル化の場合]
    │      └─ bulk_create() [非シリアル化の場合]
    │
    ├─ 追跡履歴記録
    │      └─ StockItemTracking.bulk_create()
    │
    └─ 自動完了判定
           └─ complete_order() [設定有効時]
```

### データフロー図

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

発注書情報       ───▶ バリデーション          ───▶ order_purchaseorder
  (フォーム)            │                           テーブル
                        │
                        ▼
サプライヤー部品 ───▶ 明細追加               ───▶ order_purchaseorderlineitem
  (選択)                │                           テーブル
                        │
                        ▼
                  合計金額計算             ───▶ total_price
                        │                         フィールド
                        │
                        ▼
発行コマンド     ───▶ ステータス遷移          ───▶ status = PLACED
                        │
                        ▼
                  通知処理                 ───▶ 通知システム
                        │
                        ▼
受領情報         ───▶ 受領処理               ───▶ stock_stockitem
  (数量等)              │                           テーブル
                        │
                        ▼
                  自動完了判定             ───▶ status = COMPLETE
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| models.py | `src/backend/InvenTree/order/models.py` | ソース | PurchaseOrderモデル定義 |
| status_codes.py | `src/backend/InvenTree/order/status_codes.py` | ソース | PurchaseOrderStatusの定義 |
| api.py | `src/backend/InvenTree/order/api.py` | ソース | 発注書APIエンドポイント |
| serializers.py | `src/backend/InvenTree/order/serializers.py` | ソース | 発注書シリアライザー |
| validators.py | `src/backend/InvenTree/order/validators.py` | ソース | 参照番号のバリデーション・生成 |
| events.py | `src/backend/InvenTree/order/events.py` | ソース | PurchaseOrderEventsの定義 |
| tasks.py | `src/backend/InvenTree/order/tasks.py` | ソース | バックグラウンドタスク |
