# 機能設計書 25-発注編集

## 概要

本ドキュメントは、InvenTreeアプリにおける発注（Purchase Order）編集機能の設計を記載する。既存の発注情報を編集し、発注内容の変更を可能にする機能を提供する。

### 本機能の処理概要

**業務上の目的・背景**：調達業務において、発注後に納期変更、担当者変更、プロジェクト変更などの修正が必要になることは一般的である。本機能により、発注情報の柔軟な変更を可能にし、実際の業務状況に合わせた発注管理を実現する。

**機能の利用シーン**：発注詳細画面から編集ボタンを押下した際に利用される。サプライヤー情報以外の発注属性（リファレンス、説明、日程、担当者等）の変更が可能。

**主要な処理内容**：
1. 発注詳細画面から編集モードを起動
2. 編集可能なフィールドをフォーム形式で表示
3. ユーザー入力をバリデーション
4. APIを通じて発注情報を更新
5. 更新成功後に画面をリフレッシュ

**関連システム・外部連携**：InvenTree REST API（`order/po/{pk}/`エンドポイント）のPATCH/PUTメソッドと連携。

**権限による制御**：`purchase_order`ロールを持ち、かつ対象発注に対するcanEdit権限を持つユーザーのみ編集可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 27 | 購入注文詳細画面 | 主画面 | 編集ボタン押下時に発注情報編集フォーム表示 |

## 機能種別

CRUD操作（更新）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| reference | String | No | 発注リファレンス番号 | - |
| supplier_reference | String | No | サプライヤー参照番号 | - |
| description | String | No | 発注の説明 | - |
| project_code | int | No | プロジェクトコードID | 正の整数（API v109+） |
| destination | int | No | 受入先ロケーションID | 正の整数（API v276+） |
| start_date | String | No | 開始日 | 日付形式（API v306+） |
| target_date | String | No | 目標日 | 日付形式 |
| link | String | No | 外部リンク | URL形式 |
| responsible | int | No | 担当者ID | 正の整数 |
| contact | int | No | 連絡先ID | 正の整数（API v104+） |

### 入力データソース

発注詳細画面で表示中の`InvenTreePurchaseOrder`オブジェクトから現在値を取得し、フォームの初期値として設定。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| success | bool | 更新成功フラグ |
| data | Map | 更新後の発注データ |

### 出力先

APIレスポンス → 画面リフレッシュ → スナックバーで結果通知

## 処理フロー

### 処理シーケンス

```
1. 編集ボタン押下
   └─ editOrder()メソッド呼び出し
2. フォームフィールド準備
   ├─ formFields()から編集可能フィールド取得
   ├─ supplierフィールドを除外
   ├─ API非対応フィールドを除外（contact, project_code, start_date）
   └─ フィールドに現在値を設定
3. フォーム表示
   └─ editForm()でダイアログ表示
4. ユーザー入力
   └─ 各フィールドの値を編集
5. 保存処理
   ├─ APIにPATCHリクエスト送信
   └─ バリデーションエラー時はフォームにエラー表示
6. 更新成功
   ├─ refresh()で画面リフレッシュ
   └─ スナックバーで成功メッセージ表示
```

### フローチャート

```mermaid
flowchart TD
    A[編集ボタン押下] --> B[editOrder呼び出し]
    B --> C[formFields取得]
    C --> D[supplierフィールド除外]
    D --> E{contactModel対応?}
    E -->|No| F[contactフィールド除外]
    E -->|Yes| G[contactフィールド保持]
    F --> H{projectCodes対応?}
    G --> H
    H -->|No| I[project_codeフィールド除外]
    H -->|Yes| J[project_codeフィールド保持]
    I --> K[editForm呼び出し]
    J --> K
    K --> L[フォームダイアログ表示]
    L --> M[ユーザー入力]
    M --> N[保存ボタン押下]
    N --> O[API PATCH送信]
    O --> P{更新成功?}
    P -->|Yes| Q[refresh呼び出し]
    P -->|No| R[エラー表示]
    Q --> S[スナックバー表示]
    S --> T[編集完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-25-01 | サプライヤー変更不可 | 発注作成後はサプライヤーの変更不可 | 常時 |
| BR-25-02 | 編集権限チェック | canEdit権限がないユーザーは編集不可 | canEdit == false |
| BR-25-03 | API機能分岐 | API非対応の機能フィールドは非表示 | APIバージョンによる |
| BR-25-04 | 連絡先フィルタ | 連絡先はサプライヤーに属するものに限定 | contact選択時 |

### 計算ロジック

特になし。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 更新 | purchase_order | UPDATE | 発注情報を更新 |

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

#### purchase_order（APIエンドポイント: order/po/{pk}/）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | reference | :reference | オプション |
| UPDATE | supplier_reference | :supplier_reference | オプション |
| UPDATE | description | :description | オプション |
| UPDATE | project_code | :project_code | オプション（API v109+） |
| UPDATE | destination | :destination | オプション（API v276+） |
| UPDATE | start_date | :start_date | オプション（API v306+） |
| UPDATE | target_date | :target_date | オプション |
| UPDATE | link | :link | オプション |
| UPDATE | responsible | :responsible | オプション |
| UPDATE | contact | :contact | オプション（API v104+） |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | 認証エラー | トークン無効 | 再ログインを促す |
| 403 | 権限エラー | 編集権限なし | 権限不足メッセージ表示 |
| 400 | バリデーションエラー | 入力値不正 | フォームにエラー表示 |
| 404 | Not Found | 発注が存在しない | 一覧に戻る |
| 500 | サーバーエラー | サーバー側障害 | リトライを促す |

### リトライ仕様

ネットワークエラー時はユーザーが再度保存ボタンを押下することでリトライ可能。

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

API側で単一トランザクションとして処理される。

## パフォーマンス要件

- フォーム表示は即座に行う
- 保存処理中はローディングインジケータを表示

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

- トークンベース認証による保護
- canEdit権限チェックによるアクセス制御
- サプライヤー変更を禁止することで不正な発注変更を防止

## 備考

- supplierフィールドはformFieldsから明示的に除外（364-368行目）
- 連絡先フィールドはcontact選択時にサプライヤーでフィルタリング
- 編集成功時のスナックバーメッセージ: `L10().purchaseOrderUpdated`

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | purchase_order.dart | `lib/inventree/purchase_order.dart` | formFieldsメソッド（47-79行目） |

**読解のコツ**: `formFields()`はフォームで使用するフィールド定義を返す。APIバージョンに応じて特定フィールドを除外する分岐がある。

#### Step 2: 編集処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | purchase_order_detail.dart | `lib/widget/order/purchase_order_detail.dart` | editOrderメソッド（364-389行目） |

**主要処理フロー**:
1. **365行目**: order.formFields()でフィールド定義取得
2. **368行目**: supplierフィールドを除外
3. **371-373行目**: contactModel非対応時にcontactフィールド除外
4. **376-378行目**: projectCodes非対応時にproject_codeフィールド除外
5. **380-388行目**: editForm()でフォーム表示

#### Step 3: フォーム処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | model.dart | `lib/inventree/model.dart` | editFormメソッド（基底クラス） |
| 3-2 | api_form.dart | `lib/api_form.dart` | launchApiForm関数 |

**主要処理フロー**:
- editFormは基底クラスInvenTreeModelで定義
- 内部でlaunchApiFormを呼び出しAPI通信とフォーム表示を行う

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

```
PurchaseOrderDetailWidget
    │
    └─ _PurchaseOrderDetailState
           │
           ├─ appBarActions()
           │      └─ IconButton（編集ボタン）
           │             └─ onPressed → editOrder()
           │
           └─ editOrder()
                  │
                  ├─ order.formFields()
                  │      └─ フィールド定義取得
                  │
                  ├─ fields.remove("supplier")
                  │
                  ├─ fields.remove("contact") [API非対応時]
                  │
                  ├─ fields.remove("project_code") [API非対応時]
                  │
                  └─ order.editForm()
                         │
                         └─ launchApiForm()
                                │
                                ├─ APIフォーム生成
                                ├─ ダイアログ表示
                                ├─ API PATCH送信
                                │
                                └─ onSuccess
                                       ├─ refresh()
                                       └─ showSnackIcon()
```

### データフロー図

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

InvenTreePurchaseOrder ────▶ editOrder()
         │                           │
         ▼                           ▼
    formFields() ───────────▶ フィールド定義
                                     │
                            ┌────────┼────────┐
                            ▼        ▼        ▼
                    supplier除外  contact除外  project_code除外
                            │        │        │
                            └────────┼────────┘
                                     ▼
                              editForm()
                                     │
                                     ▼
                          [フォームダイアログ]
                                     │
                                     ▼
                           ユーザー入力
                                     │
                                     ▼
                    API: PATCH order/po/{pk}/
                                     │
                                     ▼
                              更新成功
                                     │
                            ┌────────┴────────┐
                            ▼                 ▼
                       refresh()      showSnackIcon()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| purchase_order_detail.dart | `lib/widget/order/purchase_order_detail.dart` | ソース | 発注詳細・編集ウィジェット |
| purchase_order.dart | `lib/inventree/purchase_order.dart` | ソース | 発注モデル・フォームフィールド定義 |
| model.dart | `lib/inventree/model.dart` | ソース | 基底モデル（editFormメソッド） |
| api_form.dart | `lib/api_form.dart` | ソース | APIフォーム生成・通信処理 |
| api.dart | `lib/api.dart` | ソース | API機能フラグ |
| snacks.dart | `lib/widget/snacks.dart` | ソース | スナックバー表示 |
| l10.dart | `lib/l10.dart` | ソース | 多言語対応 |
