# 機能設計書 30-発注キャンセル

## 概要

本ドキュメントは、InvenTreeアプリにおける発注キャンセル機能の設計を記載する。オープン状態の発注をキャンセル（Cancelled）状態に変更する機能を提供する。

### 本機能の処理概要

**業務上の目的・背景**：発注業務において、サプライヤーからのキャンセル、発注ミス、または業務上の理由により発注を取り消す必要が生じることがある。本機能により、オープン状態の発注をキャンセルし、発注サイクルを中断させる。

**機能の利用シーン**：発注詳細画面でキャンセルボタンを押下した際に利用される。発注が「キャンセル」状態になることで、以降の受入処理や編集はできなくなる。

**主要な処理内容**：
1. 発注詳細画面からキャンセル処理を起動
2. 確認ダイアログを表示
3. ユーザー確認後、APIを通じて発注ステータスを更新
4. 更新成功後に画面をリフレッシュ

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

**権限による制御**：`purchase_order`ロールを持ち、かつcanCreate権限を持つユーザーのみキャンセル可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 27 | 購入注文詳細画面 | 主画面 | キャンセルボタン押下時にキャンセル確認ダイアログ表示 |

## 機能種別

ステータス更新

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| order_pk | int | Yes | 発注ID | 正の整数（URL内で指定） |

### 入力データソース

発注詳細画面で表示中の`InvenTreePurchaseOrder`オブジェクトからPKを取得。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| success | bool | キャンセル成功フラグ |
| status | int | 更新後のステータスコード（CANCELLED） |

### 出力先

APIレスポンス → 画面リフレッシュ

## 処理フロー

### 処理シーケンス

```
1. キャンセルボタン押下
   └─ _cancelOrder()メソッド呼び出し
2. 確認ダイアログ表示
   └─ confirmationDialog()でユーザー確認
3. ユーザー承諾
   └─ onAcceptコールバック実行
4. API呼び出し
   ├─ order.cancelOrder()メソッド実行
   └─ POST order/po/{pk}/cancel/ エンドポイント
5. キャンセル成功
   └─ refresh()で画面リフレッシュ
```

### フローチャート

```mermaid
flowchart TD
    A[キャンセルボタン押下] --> B[_cancelOrder呼び出し]
    B --> C[confirmationDialog表示]
    C --> D{ユーザー承諾?}
    D -->|Yes| E[order.cancelOrder呼び出し]
    D -->|No| F[処理中断]
    E --> G[showLoadingOverlay]
    G --> H[API POST送信]
    H --> I[hideLoadingOverlay]
    I --> J{キャンセル成功?}
    J -->|Yes| K[refresh呼び出し]
    J -->|No| L[エラー表示]
    K --> M[ステータス更新表示]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-30-01 | オープン状態のみキャンセル可 | 発注がオープン状態（Pending/Placed/On Hold）の場合のみキャンセル可能 | isOpen == true |
| BR-30-02 | キャンセル後の状態変化 | キャンセル後は「Cancelled」状態となり、受入処理ができなくなる | - |
| BR-30-03 | キャンセル後の編集不可 | キャンセル後は発注の編集ができなくなる | - |
| BR-30-04 | 確認ダイアログ必須 | キャンセル前に必ず確認ダイアログを表示 | 常時 |
| BR-30-05 | 受入済み品の扱い | 既に受入済みの在庫アイテムはキャンセルに影響しない | - |

### 計算ロジック

特になし。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ステータス更新 | purchase_order | UPDATE | ステータスをCANCELLEDに変更 |

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

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

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | status | CANCELLED | ステータス変更 |

## エラー処理

### エラーケース一覧

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

### リトライ仕様

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

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

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

## パフォーマンス要件

- キャンセル処理中はローディングオーバーレイを表示
- キャンセル完了後は即座に画面をリフレッシュ

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

- トークンベース認証による保護
- canCreate権限チェックによるアクセス制御
- 確認ダイアログによる誤操作防止

## 備考

- キャンセルボタンは`actionButtons`で構築される（142-152行目）
- キャンセルアイコンはTablerIcons.circle_x（赤色）
- 確認ダイアログのacceptTextは`L10().cancel`
- APIレスポンスの期待ステータスコードは201

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | purchase_order.dart | `lib/inventree/purchase_order.dart` | isOpenプロパティ（102-106行目） |
| 1-2 | purchase_order.dart | `lib/inventree/purchase_order.dart` | cancelOrderメソッド（148-156行目） |

**読解のコツ**: `isOpen`は["PENDING", "PLACED", "ON_HOLD"]を含む。`cancelOrder`はPOSTリクエストを送信する単純なメソッド。

#### Step 2: UI処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | purchase_order_detail.dart | `lib/widget/order/purchase_order_detail.dart` | actionButtonsメソッド（142-152行目） |
| 2-2 | purchase_order_detail.dart | `lib/widget/order/purchase_order_detail.dart` | _cancelOrderメソッド（225-238行目） |

**主要処理フロー**:
1. **142行目**: isOpen条件でキャンセルボタン表示
2. **225行目**: `_cancelOrder`メソッド定義
3. **226-237行目**: confirmationDialogで確認後、cancelOrder呼び出し

#### Step 3: API処理を理解する

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

**主要処理フロー**:
- **149-151行目**: isOpen条件チェック
- **153行目**: showLoadingOverlay()でローディング表示
- **154行目**: api.post("${url}cancel/")でAPI呼び出し
- **155行目**: hideLoadingOverlay()でローディング非表示

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

```
PurchaseOrderDetailWidget
    │
    └─ _PurchaseOrderDetailState
           │
           ├─ actionButtons()
           │      │
           │      └─ SpeedDialChild（キャンセルボタン）
           │             │
           │             └─ condition: order.isOpen
           │             └─ onTap → _cancelOrder()
           │
           └─ _cancelOrder()
                  │
                  └─ confirmationDialog()
                         │
                         └─ onAccept
                                │
                                └─ order.cancelOrder()
                                       │
                                       ├─ isOpen check
                                       ├─ showLoadingOverlay()
                                       ├─ api.post("${url}cancel/")
                                       ├─ hideLoadingOverlay()
                                       │
                                       └─ .then()
                                              │
                                              └─ refresh()
```

### データフロー図

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

キャンセルボタン押下 ─────▶ _cancelOrder()
                               │
                               ▼
                      confirmationDialog()
                               │
                               ▼
                        ユーザー承諾
                               │
                               ▼
                      order.cancelOrder()
                               │
                               ▼
               API: POST order/po/{pk}/cancel/
                               │
                               ▼
                        ステータス更新
                 (PENDING/PLACED → CANCELLED)
                               │
                               ▼
                          refresh()
                               │
                               ▼
                        画面更新表示
          (全アクションボタン消去、ステータス変更)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| purchase_order_detail.dart | `lib/widget/order/purchase_order_detail.dart` | ソース | 発注詳細・キャンセル処理ウィジェット |
| purchase_order.dart | `lib/inventree/purchase_order.dart` | ソース | 発注モデル・cancelOrderメソッド |
| dialogs.dart | `lib/widget/dialogs.dart` | ソース | 確認ダイアログ |
| progress.dart | `lib/widget/progress.dart` | ソース | ローディングオーバーレイ |
| api.dart | `lib/api.dart` | ソース | API通信処理 |
| l10.dart | `lib/l10.dart` | ソース | 多言語対応 |
