# 機能設計書 17-出荷管理

## 概要

本ドキュメントは、InvenTreeシステムにおける出荷管理機能の設計内容を記述した機能設計書である。販売注文に対する出荷（Shipment）の作成・管理・完了処理について詳細を定義する。

### 本機能の処理概要

**業務上の目的・背景**：
販売注文の履行において、実際の物品の出荷は重要なマイルストーンである。一つの販売注文を複数回に分けて出荷（分割出荷）することや、出荷ごとに追跡番号を管理することが必要である。本機能は、出荷の作成から完了までを管理し、在庫からの払い出しと顧客への割り当てを実行する。

**機能の利用シーン**：
- 販売注文の出荷を準備する場面
- 在庫を出荷に引き当てる場面
- 出荷を完了して在庫を顧客に割り当てる場面
- 出荷追跡番号を登録・管理する場面
- 出荷状況を確認する場面

**主要な処理内容**：
1. 出荷の作成・編集・削除処理
2. 出荷への在庫引当
3. 出荷完了処理（在庫の顧客割り当て）
4. 出荷確認処理
5. 追跡番号・請求書番号の管理
6. 配送日の管理

**関連システム・外部連携**：
- 販売注文管理機能（親レコード）
- 在庫引当機能（SalesOrderAllocation）
- 在庫管理機能（在庫の顧客割り当て）
- 通知システム
- プラグインシステム（イベントトリガー）

**権限による制御**：
- 出荷の作成・編集は販売担当者権限が必要
- 出荷完了は承認権限が必要（設定による）
- 出荷確認は検品担当者権限が必要

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 22 | 販売注文詳細画面 | 主機能 | 出荷の一覧表示・追加・完了処理 |
| 16 | 在庫アイテム詳細画面 | 関連画面 | 出荷に引き当てられた在庫の詳細 |

## 機能種別

CRUD操作 / トランザクション処理 / イベント通知

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| order | ForeignKey | Yes | 親販売注文 | SalesOrder |
| reference | CharField | Yes | 出荷参照番号（最大100文字） | 販売注文内で一意 |
| shipment_address | ForeignKey | No | 配送先住所 | 顧客に属するAddress |
| tracking_number | CharField | No | 追跡番号（最大100文字） | - |
| invoice_number | CharField | No | 請求書番号（最大100文字） | - |
| shipment_date | DateField | No | 出荷日 | 完了処理時に設定 |
| delivery_date | DateField | No | 配送日 | - |
| link | URLField | No | 外部リンク | URL形式 |

### 入力データソース

- 画面入力（出荷作成・編集フォーム）
- APIリクエスト
- デフォルト出荷の自動作成（販売注文作成時）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| reference | str | 出荷参照番号 |
| shipment_date | Date | 出荷日 |
| delivery_date | Date | 配送日 |
| is_complete | bool | 完了フラグ（shipment_date != None） |
| is_delivered | bool | 配送完了フラグ（delivery_date != None） |
| is_checked | bool | 確認済みフラグ（checked_by != None） |
| address | Address | 有効な配送先住所 |
| allocations | QuerySet | 引当一覧 |

### 出力先

- order_salesordershipmentテーブル
- order_salesorderallocationテーブル（引当情報）
- stock_stockitemテーブル（顧客割り当て）
- 通知システム

## 処理フロー

### 処理シーケンス

```
1. 出荷の作成
   └─ 参照番号の設定
   └─ 配送先住所の選択（任意）
   └─ バリデーション実行

2. 在庫引当
   └─ 在庫アイテムの選択
   └─ 出荷への割り当て
   └─ SalesOrderAllocationの作成

3. 出荷確認（任意）
   └─ 確認者の記録
   └─ SALESORDER_SHIPMENT_REQUIRES_CHECK設定時は必須

4. 出荷完了
   └─ 完了可能チェック
   └─ 引当の完了処理（在庫の顧客割り当て）
   └─ 出荷日の記録
   └─ イベント発火
```

### フローチャート

```mermaid
flowchart TD
    A[出荷作成] --> B{バリデーションOK?}
    B -->|No| C[ValidationError]
    B -->|Yes| D[出荷保存]
    D --> E[在庫引当]
    E --> F{確認必須設定?}
    F -->|Yes| G[出荷確認]
    F -->|No| H{完了可能?}
    G --> H
    H -->|No| I[エラー]
    H -->|Yes| J[出荷完了処理]
    J --> K[引当完了処理]
    K --> L[在庫の顧客割り当て]
    L --> M[出荷日記録]
    M --> N[イベント発火]
    N --> O[終了]
    C --> O
    I --> O
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-17-01 | 参照番号一意性 | 出荷参照番号は販売注文内で一意 | 出荷作成・編集時 |
| BR-17-02 | 配送先制約 | 配送先住所は顧客に属するものに限定 | 配送先設定時 |
| BR-17-03 | 完了要件 | 完了には最低1件の引当が必要 | 完了処理時 |
| BR-17-04 | 確認必須 | SALESORDER_SHIPMENT_REQUIRES_CHECK設定時、確認済みでないと完了不可 | 完了処理時 |
| BR-17-05 | 再完了禁止 | 既に完了した出荷は再度完了できない | 完了処理時 |

### 計算ロジック

**有効な配送先住所（address）**:
```python
@property
def address(self) -> Address:
    return self.shipment_address or self.order.address
```

**完了可能判定（check_can_complete）**:
```python
def check_can_complete(self, raise_error=True) -> bool:
    if self.shipment_date:
        raise ValidationError('Shipment has already been sent')
    if self.allocations.count() == 0:
        raise ValidationError('Shipment has no allocated stock items')
    if get_global_setting('SALESORDER_SHIPMENT_REQUIRES_CHECK') and not self.is_checked():
        raise ValidationError('Shipment must be checked before it can be completed')
    return True
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 出荷作成 | order_salesordershipment | INSERT | 新規出荷レコードの作成 |
| 出荷編集 | order_salesordershipment | UPDATE | 出荷情報の更新 |
| 出荷完了 | order_salesordershipment | UPDATE | shipment_date設定 |
| 引当完了 | order_salesorderallocation | UPDATE | 引当情報の更新 |
| 在庫更新 | stock_stockitem | UPDATE | 顧客割り当て |
| 追跡記録 | stock_stockitemtracking | INSERT | 出荷履歴 |

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

#### order_salesordershipment

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | order_id | 親販売注文ID | FK: order_salesorder |
| INSERT/UPDATE | reference | 出荷参照番号 | order+referenceでUNIQUE |
| UPDATE | shipment_date | 出荷日 | 完了時に設定 |
| UPDATE | checked_by_id | 確認者ID | 確認時に設定 |
| UPDATE | tracking_number | 追跡番号 | 任意 |
| UPDATE | invoice_number | 請求書番号 | 任意 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| reference_duplicate | ValidationError | 同一販売注文内で参照番号重複 | 別の参照番号を指定 |
| address_mismatch | ValidationError | 配送先が顧客に属さない | 顧客の住所を選択 |
| already_shipped | ValidationError | 既に出荷済み | - |
| no_allocations | ValidationError | 引当がない状態で完了試行 | 引当を追加 |
| not_checked | ValidationError | 未確認状態で完了試行（設定時） | 出荷確認を実行 |

### リトライ仕様

- 出荷完了処理失敗時：トランザクション全体をロールバック

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

- complete_shipment()は@transaction.atomicで保護
- 引当完了処理はバックグラウンドタスクにオフロード
- 各引当の完了は個別トランザクション内で実行

## パフォーマンス要件

- 出荷一覧表示：500ms以内（20件まで）
- 出荷完了処理：引当10件で2秒以内
- 引当完了処理はバックグラウンドで非同期実行

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

- 出荷完了者がshipment_byとして記録
- 出荷確認者がchecked_byとして記録
- 出荷完了イベントがトリガーで通知
- 在庫の顧客割り当ては追跡履歴に記録

## 備考

- デフォルト出荷はSALESORDER_DEFAULT_SHIPMENT設定で自動作成
- 出荷完了の実処理（引当完了）はバックグラウンドタスクで実行

---

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

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

### 推奨読解順序

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

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

**読解のコツ**:
- **2208-2398行目**: SalesOrderShipmentクラスの定義
- **2274-2282行目**: orderフィールド（SalesOrderへのFK）
- **2318-2324行目**: referenceフィールド
- **2483-2631行目**: SalesOrderAllocationクラスの定義

#### Step 2: 出荷完了処理を理解する

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

**主要処理フロー**:
- **2400-2454行目**: complete_shipment()メソッド
- **2369-2397行目**: check_can_complete()メソッド

#### Step 3: 引当完了処理を理解する

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

**主要処理フロー**:
- **2610-2630行目**: complete_allocation()メソッド
- 在庫の顧客割り当て（allocateToCustomer）を呼び出し

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

```
SalesOrderShipment.complete_shipment()
    │
    ├─ check_can_complete()
    │      ├─ shipment_date チェック
    │      ├─ allocations.count() > 0 チェック
    │      └─ is_checked() チェック（設定時）
    │
    ├─ shipment_date 設定
    ├─ tracking_number 設定
    ├─ invoice_number 設定
    ├─ save()
    │
    ├─ offload_task(complete_sales_order_shipment)
    │      └─ [バックグラウンド]
    │             ├─ allocation.complete_allocation() × n
    │             │      └─ StockItem.allocateToCustomer()
    │             │             ├─ splitStock() [必要時]
    │             │             ├─ customer 設定
    │             │             └─ add_tracking_entry()
    │             └─ line.shipped 更新
    │
    └─ trigger_event(SHIPMENT_COMPLETE)
```

### データフロー図

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

出荷情報         ───▶ バリデーション          ───▶ order_salesordershipment
  (フォーム)            │                           テーブル
                        │
                        ▼
在庫アイテム     ───▶ 引当処理               ───▶ order_salesorderallocation
  (選択)                │                           テーブル
                        │
                        ▼
完了コマンド     ───▶ 完了可能チェック
                        │
                        ▼
                  出荷完了処理
                        │
                        ├─ shipment_date設定    ───▶ order_salesordershipment
                        │
                        └─ [バックグラウンド]
                               │
                               ▼
                         引当完了処理          ───▶ stock_stockitem
                               │                     (customer設定)
                               │
                               ▼
                         追跡履歴記録          ───▶ stock_stockitemtracking
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| models.py | `src/backend/InvenTree/order/models.py` | ソース | SalesOrderShipment、SalesOrderAllocationモデル定義 |
| models.py | `src/backend/InvenTree/stock/models.py` | ソース | StockItem.allocateToCustomer()メソッド |
| api.py | `src/backend/InvenTree/order/api.py` | ソース | 出荷APIエンドポイント |
| serializers.py | `src/backend/InvenTree/order/serializers.py` | ソース | 出荷シリアライザー |
| tasks.py | `src/backend/InvenTree/order/tasks.py` | ソース | complete_sales_order_shipment()タスク |
| events.py | `src/backend/InvenTree/order/events.py` | ソース | SHIPMENT_COMPLETEイベント |
