# 機能設計書 43-注文キャンセル

## 概要

本ドキュメントは、ECモバイルアプリケーションにおける注文キャンセル機能の設計仕様を定義する。ユーザーが未処理（pending）状態の注文をキャンセルできる機能である。

### 本機能の処理概要

この機能は、注文詳細画面からユーザーが特定の注文をキャンセルするための処理を提供する。誤発注や購入意思の変更に対応し、ユーザーの購買体験を向上させる。

**業務上の目的・背景**：ECサイトにおいて、注文後の心変わりや誤発注は避けられない。ユーザーが自己解決でキャンセルできる機能を提供することで、カスタマーサポートへの問い合わせを削減し、ユーザー満足度を向上させる。また、出荷前の段階でキャンセルを受け付けることで、物流コストの削減にも貢献する。

**機能の利用シーン**：ユーザーが注文詳細画面で「キャンセル」ボタンをタップした際に利用される。誤って注文した場合、より良い商品を見つけた場合、予算の都合で購入を取りやめる場合などに使用される。

**主要な処理内容**：
1. 注文ステータスが「pending」かどうかの判定
2. キャンセル確認ダイアログの表示
3. ユーザー確認後、GraphQL APIでキャンセルリクエスト送信
4. キャンセル結果の通知とUI更新
5. キャンセル成功時の画面遷移（注文一覧へ戻る）

**関連システム・外部連携**：バックエンドのBagisto GraphQL APIと連携し、注文キャンセル処理を実行する。キャンセル処理はサーバーサイドで注文ステータスの更新、在庫の戻し処理などを行う。

**権限による制御**：ログイン済みユーザーのみがアクセス可能。ユーザーは自身の注文のみをキャンセルできる。注文ステータスが「pending」の場合のみキャンセルボタンが表示される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 18 | 注文詳細画面 | 主画面 | キャンセルボタン押下、確認ダイアログ表示、キャンセル処理実行 |
| 17 | 注文履歴一覧画面 | 遷移先画面 | キャンセル成功後に戻る |

## 機能種別

データ更新 / 確認ダイアログ / API連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | int | Yes | 注文の内部ID | 正の整数 |
| message | String | No | キャンセル理由（現在未使用） | - |

### 入力データソース

- 画面データ：OrderDetailモデルから取得したorderId
- ユーザーセッション：認証トークン（SharedPreferencesから取得）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| status | bool | 処理成功フラグ |
| message | String | 処理結果メッセージ |
| graphqlErrors | String | エラー詳細（失敗時） |

### 出力先

- 画面表示：成功/失敗通知メッセージ
- 画面遷移：成功時は注文詳細画面を閉じて注文一覧に戻る

## 処理フロー

### 処理シーケンス

```
1. キャンセルボタン表示判定
   └─ 注文ステータスが「pending」の場合のみ表示
2. キャンセルボタン押下
   └─ 確認ダイアログを表示
3. ユーザー確認
   └─ 「OK」押下で処理続行、「Cancel」で中断
4. ローディング表示開始
   └─ OnClickOrderLoadingEvent発行（isReqToShowLoader: true）
5. キャンセルAPI呼び出し
   └─ CancelOrderEvent発行、OrderDetailRepositoryで処理
6. レスポンス処理
   └─ BaseModelにデシリアライズ
7. 結果通知
   └─ 成功時：successNotification、失敗時：errorNotification
8. 画面遷移
   └─ 成功時：2秒後にNavigator.pop()で戻る
```

### フローチャート

```mermaid
flowchart TD
    A[注文詳細画面表示] --> B{status == pending?}
    B -->|Yes| C[キャンセルボタン表示]
    B -->|No| D[キャンセルボタン非表示]
    C --> E[キャンセルボタン押下]
    E --> F[確認ダイアログ表示]
    F --> G{ユーザー確認}
    G -->|Cancel| H[ダイアログ閉じる]
    G -->|OK| I[ダイアログ閉じる]
    I --> J[ローディング表示開始]
    J --> K[CancelOrderEvent発行]
    K --> L[cancelOrder API呼び出し]
    L --> M{API成功?}
    M -->|Yes| N{status == true?}
    M -->|No| O[エラー通知表示]
    N -->|Yes| P[成功通知表示]
    N -->|No| Q[エラー通知表示]
    P --> R[2秒待機]
    R --> S[Navigator.pop]
    S --> T[注文一覧画面]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-43-01 | キャンセル可能条件 | ステータスが「pending」の注文のみキャンセル可能 | 常時 |
| BR-43-02 | 確認必須 | キャンセル実行前に確認ダイアログを表示する | 常時 |
| BR-43-03 | 自己注文のみ | ユーザーは自身の注文のみキャンセル可能 | 常時 |
| BR-43-04 | 一括キャンセル | 注文全体をキャンセル（部分キャンセル不可） | 常時 |
| BR-43-05 | 遷移タイミング | キャンセル成功後2秒待機してから画面遷移 | 成功時 |

### 計算ロジック

キャンセル可否判定：
```dart
orderDetailModel?.status?.toLowerCase() == StringConstants.pending.localized().toLowerCase()
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 注文キャンセル | orders | UPDATE | ステータスを「canceled」に更新 |
| 在庫戻し | product_inventories | UPDATE | キャンセルされた商品の在庫を戻す |

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

#### orders（GraphQL API経由）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | status | 'canceled' | id = 指定されたorderId |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | API通信エラー | ネットワーク接続不可 | エラー通知表示 |
| - | 認証エラー | セッション切れ | エラー通知表示 |
| - | ビジネスエラー | キャンセル不可状態 | graphqlErrorsの内容を表示 |
| - | 処理失敗 | サーバー内部エラー | "Something went wrong"表示 |

### リトライ仕様

- 自動リトライなし
- ユーザーが再度キャンセルボタンを押下することで再試行可能

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

- フロントエンドからは単一API呼び出し
- サーバーサイドでトランザクション管理（注文ステータス更新と在庫戻しの整合性保証）

## パフォーマンス要件

- キャンセル処理完了：3秒以内
- 結果通知表示後の画面遷移：2秒の待機後

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

- 認証トークンを使用したAPI呼び出し
- 注文IDによるアクセス制御（サーバーサイドで所有者確認）
- 確認ダイアログによる誤操作防止

## 備考

- キャンセル理由（message）パラメータは現在未使用だが、将来的な拡張のためにEventに含まれている
- ローディング状態はOnClickLoadingStateで管理され、処理中はUI上でインジケーター表示

---

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

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

### 推奨読解順序

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

APIレスポンスの構造を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | graphql_base_model.dart | `lib/data_model/graphql_base_model.dart` | BaseModelの構造（status, message, graphqlErrors） |

**読解のコツ**: キャンセルAPIはBaseModelを返す。status（bool）で成功/失敗を判定。

#### Step 2: イベント定義を理解する

キャンセル処理に関連するイベントを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | order_detail_event.dart | `lib/screens/order_detail/bloc/order_detail_event.dart` | CancelOrderEventとOnClickOrderLoadingEvent |

**主要処理フロー**:
- **24-32行目**: CancelOrderEventの定義（id, messageパラメータ）
- **34-41行目**: OnClickOrderLoadingEventの定義（ローディング制御用）

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

キャンセル処理のビジネスロジックを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | order_detail_bloc.dart | `lib/screens/order_detail/bloc/order_detail_bloc.dart` | CancelOrderEventのハンドリング |

**主要処理フロー**:
- **41-54行目**: CancelOrderEventの処理
  - **43行目**: repository.cancelOrder(event.id)でAPI呼び出し
  - **44-47行目**: status==trueならCancelOrderState.success
  - **49行目**: status!=trueならCancelOrderState.fail

#### Step 4: リポジトリ層を理解する

API呼び出しの実装を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | order_detail_repository.dart | `lib/screens/order_detail/bloc/order_detail_repository.dart` | cancelOrderメソッド |

**主要処理フロー**:
- **39-49行目**: cancelOrderでApiClient.cancelOrder呼び出し

#### Step 5: UI実装を理解する

キャンセルボタンと確認ダイアログの実装を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | order_detail_tile.dart | `lib/screens/order_detail/widgets/order_detail_tile.dart` | キャンセルボタン表示と確認ダイアログ |

**主要処理フロー**:
- **74-77行目**: ステータス判定（pending時のみ表示）
- **78-185行目**: ElevatedButtonとAlertDialog実装
- **151-163行目**: OKボタン押下時の処理（OnClickOrderLoadingEvent + CancelOrderEvent発行）

#### Step 6: 結果処理を理解する

キャンセル結果のハンドリングを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | order_detail.dart | `lib/screens/order_detail/view/order_detail.dart` | BlocConsumerのlistenerでCancelOrderState処理 |

**主要処理フロー**:
- **61-70行目**: CancelOrderStateのハンドリング
  - **62-63行目**: 失敗時エラー通知
  - **64-69行目**: 成功時通知＋2秒後Navigator.pop

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

```
OrderDetailTile (Widget)
    │
    ├─ ElevatedButton [キャンセル]
    │      │
    │      └─ showDialog (AlertDialog)
    │             │
    │             └─ [OK] ボタン
    │                    │
    │                    ├─ OnClickOrderLoadingEvent
    │                    │
    │                    └─ CancelOrderEvent
    │
    └─ OrderDetailBloc
           │
           ├─ OnClickLoadingState → ローディングUI
           │
           └─ CancelOrderState
                  │
                  └─ OrderDetailRepositoryImp.cancelOrder()
                         │
                         └─ ApiClient.cancelOrder()
```

### データフロー図

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

キャンセルボタン押下
       │
       ▼
確認ダイアログ
       │
       ▼ [OK]
OnClickOrderLoadingEvent ───▶ ローディング表示
       │
       ▼
CancelOrderEvent ───▶ OrderDetailBloc
                              │
                              ▼
                    OrderDetailRepository.cancelOrder()
                              │
                              ▼
                    ApiClient (GraphQL mutation)
                              │
                              ▼
                         BaseModel
                              │
                              ▼
                    CancelOrderState ───▶ 成功/失敗通知
                                              │
                                              ▼ [成功時]
                                          Navigator.pop
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| order_detail.dart | `lib/screens/order_detail/view/order_detail.dart` | ソース | 結果ハンドリング（listener） |
| order_detail_bloc.dart | `lib/screens/order_detail/bloc/order_detail_bloc.dart` | ソース | キャンセル処理のBLoC |
| order_detail_event.dart | `lib/screens/order_detail/bloc/order_detail_event.dart` | ソース | CancelOrderEvent定義 |
| order_detail_state.dart | `lib/screens/order_detail/bloc/order_detail_state.dart` | ソース | CancelOrderState定義 |
| order_detail_repository.dart | `lib/screens/order_detail/bloc/order_detail_repository.dart` | ソース | cancelOrderメソッド |
| order_detail_tile.dart | `lib/screens/order_detail/widgets/order_detail_tile.dart` | ソース | キャンセルボタンUI |
| graphql_base_model.dart | `lib/data_model/graphql_base_model.dart` | ソース | APIレスポンスモデル |
