# 機能設計書 13-発注明細管理

## 概要

本ドキュメントは、InvenTreeシステムにおける発注明細管理機能の設計内容を記述した機能設計書である。発注書に紐づく個々の発注品目（明細行）の管理について詳細を定義する。

### 本機能の処理概要

**業務上の目的・背景**：
発注書は複数のサプライヤー部品から構成される。各部品の数量、単価、受領状況を個別に管理することで、発注進捗の詳細な追跡と正確な在庫登録を実現する。本機能は、発注書の明細レベルでの管理を提供し、部分受領や明細ごとの納期管理を可能にする。

**機能の利用シーン**：
- 発注書に発注品目を追加する場面
- 各明細の受領状況を確認する場面
- 明細ごとの納期を設定・管理する場面
- 発注単価を設定・更新する場面
- 外部製造オーダーと発注明細を紐づける場面

**主要な処理内容**：
1. 発注明細の追加・編集・削除処理
2. サプライヤー部品との紐づけ管理
3. 受領数量の追跡
4. 発注単価の設定と更新
5. 明細ごとの納期管理
6. 外部ビルドオーダーとの連携

**関連システム・外部連携**：
- 発注書管理機能（親レコード）
- サプライヤー部品管理機能
- 製造オーダー管理機能（外部製造）
- 在庫ロケーション管理機能

**権限による制御**：
- 発注明細の作成・編集は購買担当者権限が必要
- 発注書がロック状態の場合は編集不可

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 20 | 発注書詳細画面 | 主機能 | 発注明細の一覧表示・追加・編集・削除 |
| 26 | サプライヤー詳細画面 | 参照画面 | サプライヤー部品選択時の参照 |
| 14 | 部品詳細画面 | 参照画面 | 関連発注書一覧からの遷移 |
| 18 | 製造オーダー詳細画面 | 関連画面 | 外部発注明細との紐づけ表示 |

## 機能種別

CRUD操作 / 計算処理 / 関連データ管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| order | ForeignKey | Yes | 親発注書 | PurchaseOrder |
| part | ForeignKey | Yes | サプライヤー部品 | SupplierPart、サプライヤー一致必須 |
| quantity | DecimalField | Yes | 発注数量 | 正の数値 |
| purchase_price | MoneyField | No | 発注単価 | 金額 |
| reference | CharField | No | 参照番号（最大100文字） | - |
| notes | CharField | No | メモ（最大500文字） | - |
| target_date | DateField | No | 納期 | 発注書のtarget_date継承可 |
| destination | ForeignKey | No | 受入先ロケーション | StockLocation |
| build_order | ForeignKey | No | 外部製造オーダー | Build（external=True） |

### 入力データソース

- 画面入力（発注明細追加フォーム）
- APIリクエスト
- add_line_item()メソッドによる自動追加

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| quantity | Decimal | 発注数量 |
| received | Decimal | 受領済み数量 |
| remaining | Decimal | 未受領数量（計算値） |
| is_completed | bool | 完了フラグ（received >= quantity） |
| total_line_price | Money | 明細合計金額（quantity * price） |
| get_destination | StockLocation | 有効な受入先ロケーション |

### 出力先

- order_purchaseorderlineitemテーブル
- 発注書の合計金額（親レコード更新）

## 処理フロー

### 処理シーケンス

```
1. 発注明細の追加
   └─ サプライヤー部品選択
   └─ 数量・単価入力
   └─ サプライヤー一致検証
   └─ グループ化オプション判定

2. 発注明細の編集
   └─ ロック状態チェック
   └─ バリデーション実行
   └─ 親発注書の合計金額更新

3. 受領処理（発注受領処理機能で実行）
   └─ received数量の更新
   └─ 完了状態の判定

4. 発注明細の削除
   └─ ロック状態チェック
   └─ 親発注書の合計金額更新
```

### フローチャート

```mermaid
flowchart TD
    A[明細追加リクエスト] --> B{発注書ロック?}
    B -->|Yes| C[ValidationError]
    B -->|No| D[サプライヤー部品選択]
    D --> E{サプライヤー一致?}
    E -->|No| F[ValidationError]
    E -->|Yes| G{グループ化?}
    G -->|Yes| H{既存明細あり?}
    H -->|Yes| I[既存明細に数量追加]
    H -->|No| J[新規明細作成]
    G -->|No| J
    I --> K[単価再計算]
    J --> K
    K --> L[親発注書保存]
    L --> M[合計金額更新]
    M --> N[終了]
    C --> N
    F --> N
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-13-01 | サプライヤー一致 | 明細のサプライヤー部品は発注書のサプライヤーと一致必須 | 明細追加・編集時 |
| BR-13-02 | 数量制約 | 発注数量は正の数値 | 明細追加・編集時 |
| BR-13-03 | グループ化 | 同一サプライヤー部品の明細は数量を合算可能（オプション） | add_line_item()実行時 |
| BR-13-04 | 外部製造制約 | 外部ビルドオーダー連携は組立部品のみ | build_order設定時 |
| BR-13-05 | ロック制約 | 親発注書がロック状態の場合は編集不可 | 明細追加・編集・削除時 |
| BR-13-06 | 受入先優先順位 | 明細→発注書→部品デフォルトの順で受入先を決定 | get_destination()呼び出し時 |

### 計算ロジック

**未受領数量（remaining）**:
```python
def remaining(self):
    r = self.quantity - self.received
    return max(r, 0)
```

**明細合計金額（total_line_price）**:
```python
@property
def total_line_price(self):
    if self.price:
        return self.quantity * self.price
```

**受入先ロケーション（get_destination）**:
```python
def get_destination(self):
    if self.destination:
        return self.destination
    if self.order.destination:
        return self.order.destination
    if self.part and self.part.part and self.part.part.default_location:
        return self.part.part.default_location
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 明細追加 | order_purchaseorderlineitem | INSERT | 新規明細レコードの作成 |
| 明細編集 | order_purchaseorderlineitem | UPDATE | 明細情報の更新 |
| 明細削除 | order_purchaseorderlineitem | DELETE | 明細レコードの削除 |
| 受領更新 | order_purchaseorderlineitem | UPDATE | receivedフィールドの更新 |
| 親発注書更新 | order_purchaseorder | UPDATE | total_priceの再計算 |

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

#### order_purchaseorderlineitem

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | order_id | 親発注書ID | FK: order_purchaseorder |
| INSERT/UPDATE | part_id | サプライヤー部品ID | FK: company_supplierpart |
| INSERT/UPDATE | quantity | 発注数量 | DecimalField(15,5) |
| INSERT/UPDATE | received | 受領済み数量 | DecimalField(15,5)、default=0 |
| INSERT/UPDATE | purchase_price | 発注単価 | MoneyField |
| INSERT/UPDATE | destination_id | 受入先ロケーション | FK: stock_stocklocation |
| INSERT/UPDATE | build_order_id | 外部製造オーダー | FK: build_build、external=True |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| supplier_mismatch | ValidationError | サプライヤー部品が発注書のサプライヤーと不一致 | 正しいサプライヤー部品を選択 |
| quantity_invalid | ValidationError | 数量が0以下 | 正の数量を入力 |
| order_locked | ValidationError | 親発注書がロック状態 | 新規発注書を作成 |
| build_order_not_external | ValidationError | 外部製造オーダーでないBuildを指定 | external=TrueのBuildを選択 |
| build_order_part_mismatch | ValidationError | Buildの部品と明細の部品が不一致 | 正しいBuildを選択 |

### リトライ仕様

- データベース一時エラー：トランザクション単位でリトライ
- 親発注書の合計金額更新エラー：明細保存後に再試行

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

- 明細の作成・編集・削除は親発注書の保存と同一トランザクション内で実行
- save()メソッドで親発注書のsave()も呼び出し
- delete()メソッドで親発注書のsave()も呼び出し

## パフォーマンス要件

- 明細一覧表示：500ms以内（50件まで）
- 明細追加：200ms以内
- 合計金額再計算：明細100件で500ms以内

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

- 親発注書のロック状態で編集を制御
- 明細の変更は発注書レベルで監査可能

## 備考

- add_line_item()のgroup=Trueオプションで同一部品の明細を自動集約可能
- サプライヤー部品の単価情報から自動で発注単価を取得可能

---

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

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

### 推奨読解順序

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

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

**読解のコツ**:
- **1849-2027行目**: PurchaseOrderLineItemクラスの定義
- **1712-1810行目**: OrderLineItem抽象クラス（共通フィールド・メソッド）
- **1920-1926行目**: orderフィールド（PurchaseOrderへのFK）
- **1937-1945行目**: partフィールド（SupplierPartへのFK）

#### Step 2: バリデーション処理を理解する

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

**主要処理フロー**:
- **1878-1910行目**: clean()メソッドでバリデーション
- **1888-1891行目**: サプライヤー一致検証
- **1893-1910行目**: 外部製造オーダーの検証

#### Step 3: 明細追加処理を理解する

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

**主要処理フロー**:
- **683-759行目**: add_line_item()メソッド
- **729-746行目**: グループ化処理（既存明細への数量追加）
- **748-759行目**: 新規明細の作成

#### Step 4: 受入先決定ロジックを理解する

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

**主要処理フロー**:
- **1990-2004行目**: get_destination()メソッド
- 優先順位：明細→発注書→部品デフォルト

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

```
PurchaseOrder.add_line_item()
    │
    ├─ バリデーション
    │      ├─ quantity > 0 チェック
    │      └─ supplier一致チェック
    │
    ├─ グループ化処理（group=True）
    │      ├─ 既存明細検索
    │      ├─ 数量加算
    │      └─ 単価再計算
    │
    └─ 新規明細作成
           └─ PurchaseOrderLineItem.save()
                  └─ PurchaseOrder.save()（親発注書更新）

PurchaseOrderLineItem.save()
    │
    ├─ ロック状態チェック
    │      └─ order.check_locked()
    │
    ├─ バリデーション
    │      └─ clean()
    │
    └─ 親発注書更新
           └─ order.save()（合計金額再計算）
```

### データフロー図

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

サプライヤー部品 ───▶ バリデーション          ───▶ ValidationError
  (選択)                │                           (エラー時)
                        │
数量・単価       ───▶ グループ化判定
  (入力)                │
                        ├─ 既存明細更新
                        │      or
                        └─ 新規明細作成      ───▶ order_purchaseorderlineitem
                              │                     テーブル
                              │
                              ▼
                        親発注書保存          ───▶ total_price更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| models.py | `src/backend/InvenTree/order/models.py` | ソース | PurchaseOrderLineItemモデル定義 |
| api.py | `src/backend/InvenTree/order/api.py` | ソース | 発注明細APIエンドポイント |
| serializers.py | `src/backend/InvenTree/order/serializers.py` | ソース | 発注明細シリアライザー |
| models.py | `src/backend/InvenTree/company/models.py` | ソース | SupplierPartモデル定義 |
