# 機能設計書 15-販売注文管理

## 概要

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

### 本機能の処理概要

**業務上の目的・背景**：
製造業や販売業において、顧客からの注文を効率的に管理し、在庫引当・出荷処理との連携を実現することは、顧客満足度と業務効率の向上に不可欠である。本機能は、販売注文のライフサイクル全体を一元管理し、在庫管理・出荷管理との統合を提供する。

**機能の利用シーン**：
- 顧客から注文を受け付ける場面
- 販売注文を発行して処理を開始する場面
- 在庫を販売注文に引き当てる場面
- 販売注文の出荷を完了する場面
- 販売履歴を分析する場面

**主要な処理内容**：
1. 販売注文の作成・編集・削除処理
2. 販売注文のステータス遷移管理（PENDING→IN_PROGRESS→SHIPPED→COMPLETE/CANCELLED）
3. 販売注文の発行処理と通知
4. 在庫引当の管理
5. 出荷処理との連携
6. 販売合計金額の計算

**関連システム・外部連携**：
- 顧客管理との連携
- 在庫管理との連携（在庫引当）
- 出荷管理との連携
- 通知システムとの連携
- プラグインシステム（イベントトリガー）

**権限による制御**：
- 販売注文の作成・編集は販売担当者権限が必要
- 販売注文の発行・完了は承認権限が必要

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 21 | 販売インデックス画面 | 主機能 | 販売注文の一覧表示（テーブル/カレンダー/パラメトリック） |
| 22 | 販売注文詳細画面 | 主機能 | 販売注文の詳細表示・編集・発行・出荷・完了処理 |
| 25 | 会社詳細画面 | 遷移先機能 | 顧客の販売注文一覧表示 |
| 14 | 部品詳細画面 | 遷移先機能 | 販売注文一覧の表示・販売詳細への遷移 |

## 機能種別

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

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| reference | CharField | Yes | 販売注文番号（最大64文字、一意） | パターン検証、重複チェック |
| customer | ForeignKey | Yes | 顧客（Company） | is_customer=Trueの会社のみ |
| description | CharField | No | 販売注文説明（最大250文字） | - |
| customer_reference | CharField | No | 顧客発注番号（最大64文字） | - |
| target_date | DateField | No | 出荷予定日 | start_dateより後 |
| start_date | DateField | No | 開始日 | target_dateより前 |
| 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 | 完了明細行数 |
| shipment_count | int | 出荷件数 |
| is_overdue | bool | 期限超過フラグ |
| is_fully_allocated | bool | 完全引当フラグ |

### 出力先

- order_salesorderテーブル
- 通知システム（販売イベント）
- レポート・帳票出力

## 処理フロー

### 処理シーケンス

```
1. 販売注文の作成
   └─ reference自動採番
   └─ 顧客選択
   └─ バリデーション実行
   └─ デフォルト出荷の自動作成（設定依存）

2. 販売明細の追加
   └─ 部品の選択
   └─ 数量・単価の入力
   └─ 合計金額の再計算

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

4. 在庫引当
   └─ 在庫アイテムの割り当て
   └─ 引当状態の管理

5. 出荷処理
   └─ 出荷の作成・実行
   └─ 在庫の顧客への割り当て

6. 販売注文の完了（IN_PROGRESS/SHIPPED→COMPLETE）
   └─ 全明細の出荷完了チェック
   └─ ステータス変更
   └─ 完了日の記録
```

### フローチャート

```mermaid
flowchart TD
    A[販売注文作成] --> B{バリデーションOK?}
    B -->|No| C[エラー表示]
    B -->|Yes| D[PENDING状態で保存]
    D --> E{デフォルト出荷?}
    E -->|Yes| F[デフォルト出荷作成]
    E -->|No| G[明細追加]
    F --> G
    G --> H{発行可能?}
    H -->|No| G
    H -->|Yes| I[発行処理]
    I --> J[IN_PROGRESS状態]
    J --> K[在庫引当]
    K --> L[出荷処理]
    L --> M{全明細出荷?}
    M -->|No| L
    M -->|Yes| N[SHIPPED状態]
    N --> O[COMPLETE状態]

    J --> P[キャンセル]
    P --> Q[CANCELLED状態]
    D --> P
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-15-01 | 参照番号一意性 | 販売注文番号（reference）はシステム全体で一意 | 販売注文作成・編集時 |
| BR-15-02 | 顧客制約 | 顧客はis_customer=Trueの会社のみ | 顧客選択時 |
| BR-15-03 | ステータス遷移 | PENDING→IN_PROGRESS→SHIPPED→COMPLETE、またはOPEN状態からCANCELLED | ステータス変更時 |
| BR-15-04 | 発行要件 | PENDINGまたはON_HOLD状態からのみ発行可能 | 発行処理時 |
| BR-15-05 | 完了要件 | 未出荷の出荷・未完了の引当がないことが必要 | 完了処理時 |
| BR-15-06 | 出荷経由完了 | SALESORDER_SHIP_COMPLETE設定により、SHIPPEDをスキップ可能 | 完了処理時 |
| BR-15-07 | 責任者必須 | 設定により責任者を必須化可能 | SALESORDER_REQUIRE_RESPONSIBLE設定時 |
| BR-15-08 | ロック制御 | 完了済み販売注文の編集可否は設定で制御 | SALESORDER_EDIT_COMPLETED_ORDERS設定 |
| BR-15-09 | デフォルト出荷 | SALESORDER_DEFAULT_SHIPMENT設定時に自動作成 | 販売注文作成時 |

### 計算ロジック

**販売合計金額（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_salesorder | INSERT | 新規販売注文レコードの作成 |
| 販売注文編集 | order_salesorder | UPDATE | 販売注文情報の更新 |
| 販売注文発行 | order_salesorder | UPDATE | status→IN_PROGRESS、issue_date設定 |
| 販売注文出荷 | order_salesorder | UPDATE | status→SHIPPED、shipment_date設定 |
| 販売注文完了 | order_salesorder | UPDATE | status→COMPLETE |
| 販売注文キャンセル | order_salesorder | UPDATE | status→CANCELLED、引当削除 |
| 合計金額更新 | order_salesorder | UPDATE | total_priceフィールドの更新 |

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

#### order_salesorder

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | reference | 販売注文番号 | UNIQUE制約 |
| INSERT/UPDATE | customer_id | 顧客ID | FK: company_company |
| UPDATE | status | ステータスコード | 10:PENDING, 15:IN_PROGRESS, 20:SHIPPED, 25:ON_HOLD, 30:COMPLETE, 40:CANCELLED |
| UPDATE | issue_date | 発行日 | 発行処理時に設定 |
| UPDATE | shipment_date | 出荷日 | 出荷処理時に設定 |
| UPDATE | total_price | 合計金額 | 保存時に再計算 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| reference_duplicate | ValidationError | 販売注文番号が重複 | 別の番号を指定 |
| customer_invalid | ValidationError | 顧客がis_customer=False | 正しい顧客を選択 |
| contact_mismatch | ValidationError | 連絡先が顧客に属さない | 顧客の連絡先を選択 |
| address_mismatch | ValidationError | 住所が顧客に属さない | 顧客の住所を選択 |
| status_transition_invalid | ValidationError | 不正なステータス遷移 | 正しい遷移パスを確認 |
| incomplete_shipments | ValidationError | 未完了出荷がある状態で完了試行 | 出荷を完了するか削除 |
| incomplete_allocations | ValidationError | 未完了引当がある状態で完了試行 | 引当を解除 |
| order_locked | ValidationError | 完了済み販売注文の編集試行 | 設定を確認するか新規作成 |

### リトライ仕様

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

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

- 販売注文の作成・編集はトランザクション内で実行
- ステータス遷移処理はトランザクション内で完結
- キャンセル処理時の引当削除は@transaction.atomicで保護
- 合計金額の再計算は保存時に自動実行

## パフォーマンス要件

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

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

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

## 備考

- 販売注文番号のパターンはSALESORDER_REFERENCE_PATTERN設定でカスタマイズ可能
- バーチャル部品は出荷処理をスキップ可能

---

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

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

### 推奨読解順序

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

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

**読解のコツ**:
- **1242-1688行目**: SalesOrderクラスの定義（OrderクラスとTotalPriceMixinを継承）
- **1309-1317行目**: referenceフィールドの定義
- **1334-1340行目**: statusフィールドの定義
- **1319-1327行目**: customerフィールドの定義（is_customer制約）

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

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

**主要処理フロー**:
1. **1571-1576行目**: issue_order()でhandle_transitionを呼び出し
2. **1465-1481行目**: _action_place()でステータス変更と通知処理
3. **1499-1534行目**: _action_complete()で完了処理

#### Step 3: 引当管理を理解する

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

**主要処理フロー**:
- **1377-1382行目**: stock_allocationsプロパティ
- **1384-1386行目**: is_fully_allocated()メソッド

#### Step 4: キャンセル処理を理解する

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

**主要処理フロー**:
- **1541-1569行目**: _action_cancel()で引当削除と通知

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

```
SalesOrder.issue_order()
    │
    └─ handle_transition()
           └─ _action_place()
                  ├─ status = IN_PROGRESS
                  ├─ issue_date = current_date()
                  ├─ trigger_event(ISSUED)
                  └─ notify_responsible()

SalesOrder.complete_order()
    │
    └─ handle_transition()
           └─ _action_complete()
                  ├─ can_complete()チェック
                  ├─ バーチャル部品の出荷更新
                  ├─ status = SHIPPED or COMPLETE
                  └─ trigger_event(COMPLETED)

SalesOrder.cancel_order()
    │
    └─ handle_transition()
           └─ _action_cancel()
                  ├─ status = CANCELLED
                  ├─ 全引当の削除
                  └─ notify_responsible()
```

### データフロー図

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

販売注文情報     ───▶ バリデーション          ───▶ order_salesorder
  (フォーム)            │                           テーブル
                        │
                        ▼
部品             ───▶ 明細追加               ───▶ order_salesorderlineitem
  (選択)                │                           テーブル
                        │
                        ▼
                  合計金額計算             ───▶ total_price
                        │                         フィールド
                        │
                        ▼
発行コマンド     ───▶ ステータス遷移          ───▶ status = IN_PROGRESS
                        │
                        ▼
                  通知処理                 ───▶ 通知システム
                        │
                        ▼
在庫アイテム     ───▶ 引当処理               ───▶ order_salesorderallocation
  (選択)                │                           テーブル
                        │
                        ▼
出荷処理         ───▶ 出荷完了               ───▶ stock_stockitem
                        │                           (顧客割当)
                        │
                        ▼
                  完了判定                 ───▶ status = COMPLETE
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| models.py | `src/backend/InvenTree/order/models.py` | ソース | SalesOrderモデル定義 |
| status_codes.py | `src/backend/InvenTree/order/status_codes.py` | ソース | SalesOrderStatusの定義 |
| 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` | ソース | SalesOrderEventsの定義 |
| tasks.py | `src/backend/InvenTree/order/tasks.py` | ソース | バックグラウンドタスク |
