# 機能設計書：製造部品割当

## 1. 機能概要

### 1.1 機能の目的
製造部品割当（Build Item Allocation）は、製造オーダー（Build Order）を実行するために必要な在庫部品を割り当て、管理する機能である。BOM（部品表）に基づいて必要な部品を在庫から引当し、製造プロセスを通じて部品の消費を追跡する。

### 1.2 ビジネス上の位置づけ
製造プロセスにおいて、正確な部品割当は以下の点で重要である：
- 製造に必要な部品の可用性を事前に確認
- 在庫の二重引当を防止
- 製造完了時の部品消費を正確に追跡
- トラッキング対象部品の製造出力への組み込み管理

### 1.3 関連画面
| 画面No | 画面名 | 関連機能 |
|--------|--------|----------|
| 27 | 製造インデックス | 製造オーダー一覧での割当状況確認 |
| 28 | 製造オーダー詳細 | 部品割当の実行・管理 |

## 2. データモデル

### 2.1 主要エンティティ

#### BuildLine（製造明細）
```
BuildLine
├── build: FK → Build（製造オーダー）
├── bom_item: FK → BomItem（BOM品目）
├── quantity: Decimal（必要数量）
└── consumed: Decimal（消費済み数量）

制約:
- unique_together: ['build', 'bom_item']
```

#### BuildItem（製造部品割当）
```
BuildItem
├── build_line: FK → BuildLine（製造明細）
├── stock_item: FK → StockItem（在庫品目）
├── quantity: Decimal（割当数量）
└── install_into: FK → StockItem（組み込み先、nullable）

制約:
- unique_together: ['build_line', 'stock_item', 'install_into']
```

### 2.2 フィールド仕様

#### BuildLineフィールド
| フィールド名 | 型 | 制約 | 説明 |
|-------------|-----|------|------|
| build | ForeignKey | NOT NULL, CASCADE | 所属する製造オーダー |
| bom_item | ForeignKey | NOT NULL, CASCADE | 対応するBOM品目 |
| quantity | DecimalField | min=0, max_digits=15, decimal_places=5 | 製造に必要な数量 |
| consumed | DecimalField | min=0, default=0 | 消費済み数量 |

#### BuildItemフィールド
| フィールド名 | 型 | 制約 | 説明 |
|-------------|-----|------|------|
| build_line | ForeignKey | NULL可, CASCADE | 所属する製造明細 |
| stock_item | ForeignKey | NOT NULL, CASCADE | 割り当てる在庫品目 |
| quantity | DecimalField | min=0, default=1 | 割当数量 |
| install_into | ForeignKey | NULL可, SET_NULL | トラッキング対象部品の組み込み先 |

### 2.3 ER図
```
+----------------+       +----------------+       +----------------+
|     Build      |       |   BuildLine    |       |   BuildItem    |
+----------------+       +----------------+       +----------------+
| PK: id         |<──────| FK: build      |<──────| FK: build_line |
| reference      |       | FK: bom_item   |       | FK: stock_item |
| part           |       | quantity       |       | quantity       |
| quantity       |       | consumed       |       | FK: install_into|
| status         |       +----------------+       +----------------+
+----------------+              │                        │
                               │                        │
                               ▼                        ▼
                        +----------------+       +----------------+
                        |    BomItem     |       |   StockItem    |
                        +----------------+       +----------------+
                        | part           |       | part           |
                        | sub_part       |       | quantity       |
                        | quantity       |       | serial         |
                        +----------------+       +----------------+
```

## 3. 機能詳細

### 3.1 BuildLine管理

#### 3.1.1 自動生成
製造オーダー作成時、関連するBOM品目ごとにBuildLineが自動生成される。

```python
# Build.create_build_line_items()で実装
# 各BomItemに対応するBuildLineを作成
# quantity = bom_item.quantity * build.quantity
```

#### 3.1.2 割当数量計算
| メソッド | 説明 | 計算式 |
|---------|------|--------|
| allocated_quantity() | 割当済み数量 | SUM(allocations.quantity) |
| unallocated_quantity() | 未割当数量 | MAX(quantity - consumed - allocated_quantity, 0) |
| is_fully_allocated() | 完全割当判定 | allocated_quantity >= (quantity - consumed) |
| is_fully_consumed() | 完全消費判定 | consumed >= quantity |

#### 3.1.3 消耗品の扱い
消耗品（consumable=True）のBOM品目は、割当チェックをスキップする：
```python
def is_fully_allocated(self) -> bool:
    if self.bom_item.consumable:
        return True  # 消耗品は常に割当済みと見なす
```

### 3.2 BuildItem管理

#### 3.2.1 割当の作成
```
入力: build_line, stock_item, quantity, install_into(optional)
処理:
  1. BomItem検証（stock_item.partがBOMに含まれるか）
  2. 割当数量検証（利用可能数量を超えないか）
  3. トラッキング対象の場合、install_into必須チェック
  4. BuildItem作成・保存
出力: 作成されたBuildItem
```

#### 3.2.2 バリデーション
```python
def clean(self, raise_error: bool = True):
    # 1. トラッキング対象部品のinstall_into必須チェック
    if self.stock_item.part.trackable and not self.install_into:
        raise ValidationError('Build item must specify a build output...')

    # 2. 割当数量チェック
    self.check_allocated_quantity(raise_error=raise_error)

    # 3. BomItem検証（stock_itemがBOMに対応しているか）
    # - 直接一致
    # - バリアント許可時のバリアント部品
    # - 代替部品
```

#### 3.2.3 割当数量検証（check_allocated_quantity）
```python
def check_allocated_quantity(self, raise_error: bool = False):
    # 1. 数量が正の値であること
    if self.quantity <= 0:
        error = 'Allocated quantity must be greater than zero'

    # 2. シリアル化在庫は数量=1のみ
    if self.stock_item.serialized and self.quantity != 1:
        self.quantity = 1
        raise ValidationError('Quantity must be 1 for serialized stock')

    # 3. 在庫数量を超えないこと
    if self.quantity > self.stock_item.quantity:
        error = 'Allocated quantity must not exceed available stock quantity'

    # 4. 過剰割当チェック（他の割当との合計）
    total_allocation = build_allocation + sales_allocation + quantity
    if total_allocation > available:
        error = 'Stock item is over-allocated'
```

### 3.3 自動割当（auto_allocate_stock）

#### 3.3.1 処理フロー
```
入力: location, exclude_location, interchangeable, substitutes, optional_items
処理:
  1. 未トラッキング（untracked）の明細のみを対象
  2. 各BuildLineに対して:
     a. 消耗品・オプション品目のスキップ判定
     b. 未割当数量の確認
     c. 有効な部品の特定（バリアント・代替含む）
     d. 利用可能な在庫の検索・フィルタリング
     e. 優先順位でソート（直接一致 > バリアント > 代替）
     f. BuildItemの作成
  3. 一括作成（bulk_create）
出力: なし（BuildItemが作成される）
```

#### 3.3.2 優先順位
```python
def stock_sort(item, bom_item, variant_parts):
    if item.part == bom_item.sub_part:
        return 1  # 直接一致：最優先
    elif item.part in variant_parts:
        return 2  # バリアント部品
    return 3      # 代替部品
```

#### 3.3.3 制約条件
- 単一在庫品目のみ、またはinterchangeable=Trueの場合のみ自動割当
- シリアル化在庫は対象外
- locationでの在庫フィルタリング対応
- exclude_locationで特定場所の除外対応

### 3.4 割当解除（deallocate_stock）

```python
def deallocate_stock(self, build_line=None, output=None):
    """
    Args:
        build_line: 特定のBuildLineの割当のみ解除
        output: 特定の製造出力の割当のみ解除
    """
    allocations = self.allocated_stock.filter(install_into=output)
    if build_line:
        allocations = allocations.filter(build_line=build_line)
    allocations.delete()
```

### 3.5 割当完了（complete_allocation）

#### 3.5.1 処理フロー
```
入力: quantity, notes, user
処理:
  1. 数量未指定時は全量を使用
  2. 利用可能数量を超える場合は調整
  3. 在庫分割（必要な場合）
  4. トラッキング対象/非対象で処理分岐:
     【トラッキング対象】
     - install_into.installStockItem()で組み込み
     【非トラッキング対象】
     - consumed_byを設定
     - locationをNullに
     - 追跡履歴（BUILD_CONSUMED）を記録
  5. BuildLine.consumedを更新
  6. BuildItem.quantityを減算
  7. 数量0以下ならBuildItem削除
出力: なし
```

#### 3.5.2 トラッキング対象部品の処理
```python
if item.part.trackable:
    # 製造出力への組み込み
    self.install_into.installStockItem(
        item, quantity, user, notes, build=self.build
    )
else:
    # 消費として処理
    item.consumed_by = self.build
    item.location = None
    item.save(add_note=False)

    item.add_tracking_entry(
        StockHistoryCode.BUILD_CONSUMED,
        user,
        notes=notes,
        deltas={'buildorder': self.build.pk, 'quantity': float(item.quantity)}
    )
```

### 3.6 過剰割当の調整（trim_allocated_stock）

製造オーダーの数量変更時に、過剰割当を自動調整する。

```python
def trim_allocated_stock(self):
    # 未トラッキング明細のみ対象
    # 消耗品は除外
    for build_line in lines:
        reduce_by = allocated - quantity
        if reduce_by <= 0:
            continue

        # BuildItemを順次削減
        for item in BuildItem.objects.filter(build_line=build_line):
            if item.quantity > reduce_by:
                item.quantity -= reduce_by
                # 保存
                break
            else:
                reduce_by -= item.quantity
                # 削除対象
```

## 4. API仕様

### 4.1 BuildLine API

#### エンドポイント
| メソッド | パス | 説明 |
|---------|------|------|
| GET | /api/build/line/ | 製造明細一覧取得 |
| GET | /api/build/line/{id}/ | 製造明細詳細取得 |
| PATCH | /api/build/line/{id}/ | 製造明細更新 |
| DELETE | /api/build/line/{id}/ | 製造明細削除 |

### 4.2 BuildItem API

#### エンドポイント
| メソッド | パス | 説明 |
|---------|------|------|
| GET | /api/build/item/ | 割当一覧取得 |
| POST | /api/build/item/ | 割当作成 |
| GET | /api/build/item/{id}/ | 割当詳細取得 |
| PATCH | /api/build/item/{id}/ | 割当更新 |
| DELETE | /api/build/item/{id}/ | 割当削除 |

### 4.3 自動割当API

#### エンドポイント
| メソッド | パス | 説明 |
|---------|------|------|
| POST | /api/build/{id}/allocate/ | 手動割当 |
| POST | /api/build/{id}/auto-allocate/ | 自動割当 |
| POST | /api/build/{id}/unallocate/ | 割当解除 |

#### 自動割当パラメータ
```json
{
    "location": 123,           // 在庫場所フィルタ（オプション）
    "exclude_location": 456,   // 除外する在庫場所（オプション）
    "interchangeable": true,   // 複数在庫からの割当許可
    "substitutes": true,       // 代替部品の使用許可
    "optional_items": false    // オプション品目の割当
}
```

## 5. 業務ルール

### 5.1 割当ルール
| ルールID | ルール | 実装箇所 |
|---------|--------|----------|
| ALLOC-001 | 在庫数量を超える割当は不可 | BuildItem.check_allocated_quantity() |
| ALLOC-002 | シリアル化在庫は数量1のみ | BuildItem.check_allocated_quantity() |
| ALLOC-003 | トラッキング対象部品はinstall_into必須 | BuildItem.clean() |
| ALLOC-004 | 消耗品は割当チェック対象外 | BuildLine.is_fully_allocated() |
| ALLOC-005 | 割当済み在庫の二重割当は不可 | BuildItem.check_allocated_quantity() |

### 5.2 BOM検証ルール
| ルールID | ルール | 実装箇所 |
|---------|--------|----------|
| BOM-001 | 割当部品はBOMに含まれる必要あり | BuildItem.clean() |
| BOM-002 | バリアント許可時はバリアント部品も有効 | BuildItem.clean() |
| BOM-003 | 継承BOMは子部品でも有効 | BuildItem.clean() |

### 5.3 完了ルール
| ルールID | ルール | 実装箇所 |
|---------|--------|----------|
| COMP-001 | 完了時に割当在庫を消費 | BuildItem.complete_allocation() |
| COMP-002 | トラッキング対象は製造出力に組み込み | BuildItem.complete_allocation() |
| COMP-003 | 非トラッキング対象はconsumed_byを設定 | BuildItem.complete_allocation() |

## 6. 設定オプション

| 設定キー | デフォルト | 説明 |
|---------|----------|------|
| BUILDORDER_REQUIRE_VALID_BOM | True | 有効なBOM必須 |
| BUILDORDER_REQUIRE_ACTIVE_PART | False | アクティブ部品必須 |
| BUILDORDER_REQUIRE_LOCKED_PART | False | ロック済み部品必須 |

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

### 7.1 推奨される読み解き順序

1. **データ構造の理解**
   - `BuildLine`クラス（build/models.py: 1607-1721行目）
   - `BuildItem`クラス（build/models.py: 1723-1999行目）

2. **割当処理の流れ**
   - `auto_allocate_stock`メソッド（build/models.py: 1280-1412行目）
   - `BuildItem.clean`メソッド（build/models.py: 1751-1835行目）
   - `check_allocated_quantity`メソッド（build/models.py: 1837-1889行目）

3. **完了処理の流れ**
   - `complete_allocation`メソッド（build/models.py: 1901-1965行目）
   - `deallocate_stock`メソッド（build/models.py: 862-874行目）
   - `trim_allocated_stock`メソッド（build/models.py: 1057-1096行目）

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

```
Build Order Allocation Flow
│
├── auto_allocate_stock() [build/models.py:1280]
│   ├── untracked_line_items [プロパティ]
│   ├── bom_item.get_valid_parts_for_allocation()
│   ├── StockItem.objects.filter(IN_STOCK_FILTER)
│   └── BuildItem.objects.bulk_create()
│
├── BuildItem.save() [build/models.py:1746]
│   └── clean() [build/models.py:1751]
│       ├── check_allocated_quantity() [build/models.py:1837]
│       └── BomItem.is_stock_item_valid()
│
├── complete_allocation() [build/models.py:1901]
│   ├── stock_item.splitStock()
│   ├── [trackable] install_into.installStockItem()
│   └── [non-trackable] item.add_tracking_entry(BUILD_CONSUMED)
│
└── deallocate_stock() [build/models.py:862]
    └── allocations.delete()
```

### 7.3 データフロー図

```
[在庫品目]                    [製造オーダー]
    │                              │
    │ 割当                         │ 作成
    ▼                              ▼
┌─────────────────────────────────────────────┐
│              BuildLine                       │
│  (BomItemと製造オーダーをリンク)              │
│  quantity: 必要数量                          │
│  consumed: 消費済み数量                       │
└─────────────────────────────────────────────┘
                    │
                    │ 割当
                    ▼
┌─────────────────────────────────────────────┐
│              BuildItem                       │
│  (在庫品目とBuildLineをリンク)                │
│  quantity: 割当数量                          │
│  install_into: 組み込み先（トラッキング対象）  │
└─────────────────────────────────────────────┘
                    │
        ┌──────────┴──────────┐
        │                     │
   [トラッキング対象]      [非トラッキング対象]
        │                     │
        ▼                     ▼
   製造出力に組み込み      consumed_byに設定
   (installStockItem)     (在庫消費記録)
```

### 7.4 関連ファイル一覧

| ファイル | 主な内容 |
|---------|---------|
| src/backend/InvenTree/build/models.py | BuildLine, BuildItemモデル定義 |
| src/backend/InvenTree/build/serializers.py | API用シリアライザ |
| src/backend/InvenTree/build/api.py | REST APIエンドポイント |
| src/backend/InvenTree/stock/models.py | StockItemモデル（割当対象） |
| src/backend/InvenTree/part/models.py | BomItemモデル（BOM定義） |

## 8. 非機能要件

### 8.1 パフォーマンス
- 自動割当はbulk_createで一括処理
- クエリ最適化（prefetch_related, annotate使用）

### 8.2 データ整合性
- トランザクション管理（@transaction.atomic）
- 一意制約による重複防止
- カスケード削除による孤立データ防止

### 8.3 監査・追跡
- 在庫消費時の追跡履歴記録（StockHistoryCode.BUILD_CONSUMED）
- 製造オーダーとの紐付け維持

## 9. 変更履歴

| 版 | 日付 | 変更内容 | 作成者 |
|----|------|----------|--------|
| 1.0 | 2026-01-29 | 初版作成 | AI |
