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

## 概要

本ドキュメントは、Shopwareストアフロントにおける注文キャンセル機能の設計を記述したものである。本機能は、顧客が自身の注文をキャンセルするための機能を提供する。

### 本機能の処理概要

注文キャンセル機能は、顧客が注文確定後に何らかの理由で注文を取り消す必要がある場合に、注文のステータスを「キャンセル」に変更する機能を提供する。

**業務上の目的・背景**：ECサイトにおいて、顧客が注文後にキャンセルを希望するケースは一定数発生する。注文間違い、重複注文、購入意思の変更など、様々な理由が考えられる。顧客自身でキャンセル操作ができることで、カスタマーサポートへの問い合わせを削減し、顧客体験の向上にも寄与する。ただし、配送開始後や決済完了後など、キャンセルが困難な状況もあるため、適切なステータス管理が重要となる。

**機能の利用シーン**：
- 注文内容を間違えた場合
- 重複して注文してしまった場合
- 購入を取りやめたい場合
- 別の商品を注文し直したい場合

**主要な処理内容**：
1. キャンセル対象の注文IDを受け取る
2. 注文ステータスを「cancel」トランジションで変更
3. ゲスト顧客の場合はdeepLinkCodeで注文詳細に戻る
4. ログイン顧客の場合は注文一覧に戻る

**関連システム・外部連携**：
- AbstractCancelOrderRoute: 注文キャンセルAPI
- CancelOrderRouteRequestEvent: キャンセルリクエストイベント
- StateMachine: 注文ステータス管理

**権限による制御**：
- 注文を行った顧客本人のみがキャンセル可能
- ステートマシンの遷移ルールに従ったキャンセル可否判定

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 21 | 注文履歴ページ | 主画面 | 注文のキャンセル（AccountOrderController::cancelOrder） |
| 41 | 注文一覧（管理画面） | 参照画面 | 注文のキャンセル処理 |

## 機能種別

CRUD操作（Update） / ステータス遷移

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| orderId | string | Yes | キャンセル対象の注文ID | UUID形式 |
| deepLinkCode | string | No | ゲスト注文用ディープリンクコード | 文字列 |

### 入力データソース

- HTTPリクエストパラメータ（POST）
- SalesChannelContext（顧客情報）

## 出力仕様

### 出力データ

出力データはなく、リダイレクトレスポンスのみ返却される。

### 出力先

- リダイレクトレスポンス
  - ゲスト顧客: `frontend.account.order.single.page`（注文詳細）
  - ログイン顧客: `frontend.account.order.page`（注文一覧）

## 処理フロー

### 処理シーケンス

```
1. cancelOrder（POST /account/order/cancel）
   └─ orderId, deepLinkCodeをリクエストから取得
   └─ キャンセルリクエストデータを構築
      └─ orderId: 対象注文ID
      └─ transition: 'cancel'
   └─ CancelOrderRouteRequestEventを発火
   └─ AbstractCancelOrderRoute.cancelを呼び出し
   └─ 顧客タイプに応じてリダイレクト先を決定
   └─ リダイレクトレスポンスを返却
```

### フローチャート

```mermaid
flowchart TD
    A[注文キャンセルリクエスト] --> B[orderIdを取得]
    B --> C[キャンセルリクエストデータ構築]
    C --> D[CancelOrderRouteRequestEvent発火]
    D --> E[AbstractCancelOrderRoute.cancel]
    E --> F[注文ステータス変更]
    F --> G{顧客タイプ?}
    G -->|ゲスト| H[注文詳細ページへリダイレクト]
    G -->|ログイン顧客| I[注文一覧ページへリダイレクト]
    H --> J[リダイレクトレスポンス]
    I --> J
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-24-01 | 所有者確認 | キャンセルできるのは注文を行った顧客本人のみ | 常時 |
| BR-24-02 | ステータス遷移ルール | 注文ステータスが「open」「in_progress」の場合のみキャンセル可能 | StateMachineの遷移ルールに従う |
| BR-24-03 | トランジション指定 | 固定値「cancel」でステータス遷移を実行 | 常時 |
| BR-24-04 | ゲスト判定 | ゲスト顧客（guest=true）の場合は注文詳細へ、それ以外は注文一覧へリダイレクト | 常時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 注文ステータス更新 | order | UPDATE | stateIdを「cancelled」に変更 |
| ステータス履歴追加 | state_machine_history | INSERT | ステータス遷移履歴を記録 |

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

#### order

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | stateId | cancelled状態のID | StateMachineにより制御 |
| UPDATE | updatedAt | 現在日時 | 自動更新 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ORDER_NOT_FOUND | 注文不在 | 指定されたorderIdが存在しない | エラーページ表示 |
| ILLEGAL_STATE_TRANSITION | 遷移不可 | 現在のステータスからキャンセルへの遷移が許可されていない | エラーメッセージ表示 |
| UNAUTHORIZED | 権限エラー | 注文の所有者でない | エラーページ表示 |

### リトライ仕様

リトライ処理は行わない。エラー発生時はエラーメッセージを表示し、ユーザーに状況を通知する。

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

注文ステータスの更新とステータス履歴の追加は、DAL経由で単一トランザクションとして実行される。ロールバックはエラー発生時に自動的に行われる。

## パフォーマンス要件

- キャンセル処理: 2秒以内
- ステートマシン遷移は軽量な処理

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

- 注文所有者の検証（他顧客の注文はキャンセル不可）
- CSRF保護（POSTメソッドのみ受付）
- ゲスト顧客はdeepLinkCode経由でのみアクセス可能

## 備考

- キャンセル後の返金処理は別途決済プロバイダとの連携が必要（本機能の範囲外）
- キャンセル可能なステータスはStateMachineの設定に依存
- キャンセル後の在庫戻し処理はShopwareのStock管理機能により自動実行

---

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

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

### 推奨読解順序

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

注文ステータス遷移に関わるデータ構造を理解。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | OrderEntity.php | `src/Core/Checkout/Order/OrderEntity.php` | stateId, stateMachineStateプロパティ |
| 1-2 | OrderStates.php | `src/Core/Checkout/Order/OrderStates.php` | 注文ステータス定数定義 |

**読解のコツ**: ShopwareのステートマシンはStateMachineStateEntityで管理され、各ステータスには「technicalName」（STATE_OPEN, STATE_CANCELLED等）が割り当てられている。

#### Step 2: エントリーポイントを理解する

処理の起点となるコントローラーを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | AccountOrderController.php | `src/Storefront/Controller/AccountOrderController.php` | cancelOrderメソッド |

**主要処理フロー**:
1. **104-132行目**: `cancelOrder`メソッド - 注文キャンセル処理
2. **111-114行目**: キャンセルリクエストデータ構築
3. **116行目**: リクエストの複製
4. **117-118行目**: CancelOrderRouteRequestEventの発火
5. **120行目**: AbstractCancelOrderRoute.cancel呼び出し
6. **122-129行目**: ゲスト判定とリダイレクト先決定

#### Step 3: Store-APIルートを理解する

キャンセル処理のAPIルートを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | AbstractCancelOrderRoute.php | `src/Core/Checkout/Order/SalesChannel/AbstractCancelOrderRoute.php` | キャンセルAPIの抽象クラス |

**主要処理フロー**:
- **17行目**: `cancel`メソッド - Request, SalesChannelContextを受け取りCancelOrderRouteResponseを返却

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

```
AccountOrderController
    │
    └─ cancelOrder (POST /account/order/cancel)
           │
           ├─ RequestParamHelper.get('orderId')
           │
           ├─ cancelOrderRequestData構築
           │      ├─ orderId: 対象注文ID
           │      └─ transition: 'cancel'
           │
           ├─ CancelOrderRouteRequestEvent dispatch
           │
           ├─ AbstractCancelOrderRoute.cancel
           │      └─ StateMachine遷移処理
           │             └─ order.stateId更新
           │             └─ state_machine_history挿入
           │
           └─ リダイレクト
                  ├─ ゲスト → frontend.account.order.single.page
                  └─ 通常 → frontend.account.order.page
```

### データフロー図

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

POSTリクエスト ─────────────────┬──────────────────────────────────► リダイレクト
(orderId, deepLinkCode)        │                                    レスポンス
                               ▼
                    ┌─────────────────────┐
                    │ AccountOrder        │
                    │ Controller          │
                    │ ::cancelOrder       │
                    └─────────┬───────────┘
                              │
                              ▼
                    ┌─────────────────────┐
                    │ CancelOrderRoute    │
                    │ RequestEvent        │
                    └─────────┬───────────┘
                              │
                              ▼
                    ┌─────────────────────┐
                    │ AbstractCancel      │
                    │ OrderRoute.cancel   │
                    └─────────┬───────────┘
                              │
                              ▼
                    ┌─────────────────────┐
                    │ StateMachine        │─────────────────────────► order (UPDATE stateId)
                    │ 遷移処理            │                          state_machine_history (INSERT)
                    └─────────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| AccountOrderController.php | `src/Storefront/Controller/AccountOrderController.php` | ソース | コントローラー（エントリーポイント） |
| AbstractCancelOrderRoute.php | `src/Core/Checkout/Order/SalesChannel/AbstractCancelOrderRoute.php` | ソース | キャンセルAPI抽象クラス |
| CancelOrderRoute.php | `src/Core/Checkout/Order/SalesChannel/CancelOrderRoute.php` | ソース | キャンセルAPI実装クラス |
| CancelOrderRouteRequestEvent.php | `src/Storefront/Event/RouteRequest/CancelOrderRouteRequestEvent.php` | ソース | キャンセルリクエストイベント |
| OrderEntity.php | `src/Core/Checkout/Order/OrderEntity.php` | ソース | 注文エンティティ |
| OrderStates.php | `src/Core/Checkout/Order/OrderStates.php` | ソース | 注文ステータス定数 |
