# 画面設計書 49-交換作成画面

## 概要

本ドキュメントは、Medusa管理画面における交換作成画面の設計を記載する。本画面は、注文に対する交換（商品の返品と新しい商品の発送）を作成するためのモーダル形式の画面であり、返送商品（Inbound）の選択と新規発送商品（Outbound）の追加、それぞれの配送設定を行う。

### 本画面の処理概要

**業務上の目的・背景**：ECサイト運営において、顧客が購入した商品を別の商品に交換したい場合、交換処理が必要となる。本画面は、元の商品を返送してもらい、代わりに別の商品を発送する「交換」を作成する機能を提供する。クレームとは異なり、交換は顧客の希望による商品変更（サイズ変更等）に使用される。

**画面へのアクセス方法**：注文詳細画面（/orders/{id}）のサマリーセクションから「Create Exchange」アクションを選択することでアクセス。画面表示時に自動的に交換リクエストが初期化される。

**主要な操作・処理内容**：
1. 返送商品（Inbound）の追加：返送対象の商品を選択し、数量・理由を設定
2. 返送先ロケーションの選択：商品を受け取る倉庫・店舗を選択
3. 返送用配送オプションの設定：返送用の配送方法と送料を設定
4. 発送商品（Outbound）の追加：発送する新商品を選択し、数量を設定
5. 発送用配送オプションの設定：新商品の配送方法と送料を設定
6. プロモーション引継ぎ設定：元注文のプロモーションを新商品に適用するか設定
7. 交換リクエストの確定：設定内容で交換を確定

**画面遷移**：
- 遷移元：注文詳細画面（/orders/{id}）
- 遷移先：注文詳細画面（交換作成完了またはキャンセル後）

**権限による表示制御**：管理者権限を持つユーザーのみがアクセス可能。他のアクティブな変更がある場合はエラー表示されリダイレクト。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| F-001 | 交換作成 | 主機能 | 交換リクエストの初期化と確定 |
| F-002 | 返送商品追加 | 補助機能 | 返送対象商品の追加 |
| F-003 | 返送商品更新 | 補助機能 | 返送数量・理由の更新 |
| F-004 | 返送商品削除 | 補助機能 | 返送対象商品の削除 |
| F-005 | 発送商品追加 | 補助機能 | 発送商品の追加 |
| F-006 | 発送商品更新 | 補助機能 | 発送商品数量の更新 |
| F-007 | 発送商品削除 | 補助機能 | 発送商品の削除 |
| F-008 | 返送送料設定 | 補助機能 | 返送用配送オプションの設定 |
| F-009 | 発送送料設定 | 補助機能 | 発送商品配送オプションの設定 |
| F-010 | プロモーション引継ぎ | 補助機能 | プロモーションの引継ぎ設定 |

## 画面種別

モーダル（RouteFocusModal）

## URL/ルーティング

```
/orders/:id/exchanges
```

## 入出力項目

| 項目名 | 入出力 | 型 | 必須 | 説明 |
|--------|--------|-----|------|------|
| id | 入力 | string | ○ | 注文ID（URLパラメータ） |
| inbound_items | 入力 | array | × | 返送対象商品配列 |
| inbound_items[].item_id | 入力 | string | ○ | 商品ID |
| inbound_items[].quantity | 入力 | number | ○ | 返送数量 |
| inbound_items[].reason_id | 入力 | string | × | 返送理由ID |
| inbound_items[].note | 入力 | string | × | 内部メモ |
| outbound_items | 入力 | array | × | 発送商品配列 |
| outbound_items[].variant_id | 入力 | string | ○ | バリアントID |
| outbound_items[].quantity | 入力 | number | ○ | 発送数量 |
| location_id | 入力 | string | × | 返送先ロケーションID |
| inbound_option_id | 入力 | string | × | 返送配送オプションID |
| outbound_option_id | 入力 | string | × | 発送配送オプションID |
| carry_over_promotions | 入力 | boolean | × | プロモーション引継ぎフラグ |
| send_notification | 入力 | boolean | × | 顧客への通知送信フラグ |

## 表示項目

| 項目名 | 表示名 | 型 | 説明 |
|--------|--------|-----|------|
| inbound_items | Inbound Items | list | 返送対象商品一覧 |
| inbound_items[].thumbnail | - | image | 商品サムネイル |
| inbound_items[].title | Title | string | 商品名 |
| inbound_items[].quantity | Quantity | number | 返送数量（入力） |
| inbound_items[].reason | Reason | select | 返送理由選択 |
| inbound_items[].note | Note | input | 内部メモ |
| outbound_items | Outbound Items | list | 発送商品一覧 |
| outbound_items[].thumbnail | - | image | 商品サムネイル |
| outbound_items[].title | Title | string | 商品名 |
| outbound_items[].quantity | Quantity | number | 発送数量（入力） |
| location_id | Location | select | 返送先ロケーション選択 |
| inbound_option_id | Inbound Shipping | select | 返送配送オプション選択 |
| outbound_option_id | Outbound Shipping | select | 発送配送オプション選択 |
| inbound_total | Inbound Total | number | 返送商品合計金額 |
| outbound_total | Outbound Total | number | 発送商品合計金額 |
| inbound_shipping | Inbound Shipping | number | 返送送料 |
| outbound_shipping | Outbound Shipping | number | 発送送料 |
| refund_amount | Refund Amount | number | 返金/追加請求金額 |
| carry_over_promotions | Carry Over Promotion | switch | プロモーション引継ぎスイッチ |
| send_notification | Send Notification | switch | 通知送信スイッチ |

## イベント仕様

### 1-返送商品追加

「Add Items」リンク（Inboundセクション）をクリックすると、商品選択モーダルが表示される。商品を選択して「Save」をクリックすると、返送対象商品が追加される。

- API呼び出し: `POST /admin/exchanges/{exchange_id}/inbound/items`
- リクエストボディ:
  ```json
  {
    "items": [{ "id": "item_xxx", "quantity": 1 }]
  }
  ```

### 2-返送商品更新

返送商品の数量や理由を変更すると、交換リクエストが更新される。

- API呼び出し: `POST /admin/exchanges/{exchange_id}/inbound/items/{action_id}`
- リクエストボディ:
  ```json
  {
    "quantity": 2,
    "reason_id": "reason_xxx",
    "internal_note": "note"
  }
  ```

### 3-返送商品削除

返送商品の削除ボタンをクリックすると、返送対象から除外される。

- API呼び出し: `DELETE /admin/exchanges/{exchange_id}/inbound/items/{action_id}`

### 4-ロケーション変更

ロケーションを変更すると、関連する返品リクエストのlocation_idが更新される。

- API呼び出し: `POST /admin/returns/{return_id}`
- 連動: 返送用配送オプションがロケーションでフィルタリングされる

### 5-返送配送オプション変更

返送配送オプションを変更すると、既存の返送送料が削除され、新しい配送オプションが設定される。

- API呼び出し（削除）: `DELETE /admin/exchanges/{exchange_id}/inbound/shipping-method/{action_id}`
- API呼び出し（追加）: `POST /admin/exchanges/{exchange_id}/inbound/shipping-method`

### 6-返送送料カスタマイズ

返送送料の編集アイコンをクリックすると、カスタム送料を入力できる。

- API呼び出し: `POST /admin/exchanges/{exchange_id}/inbound/shipping-method/{action_id}`
- リクエストボディ:
  ```json
  {
    "custom_amount": 500
  }
  ```

### 7-発送商品追加

「Add Items」リンク（Outboundセクション）をクリックすると、商品選択モーダルが表示される。バリアントを選択して「Save」をクリックすると、発送商品が追加される。

- API呼び出し: `POST /admin/exchanges/{exchange_id}/outbound/items`
- リクエストボディ:
  ```json
  {
    "items": [{ "variant_id": "variant_xxx", "quantity": 1 }]
  }
  ```

### 8-発送商品更新

発送商品の数量を変更すると、交換リクエストが更新される。

- API呼び出し: `POST /admin/exchanges/{exchange_id}/outbound/items/{action_id}`

### 9-発送商品削除

発送商品の削除ボタンをクリックすると、発送対象から除外される。

- API呼び出し: `DELETE /admin/exchanges/{exchange_id}/outbound/items/{action_id}`

### 10-発送配送オプション変更

発送配送オプションを変更すると、既存の発送送料が削除され、新しい配送オプションが設定される。

- API呼び出し（削除）: `DELETE /admin/exchanges/{exchange_id}/outbound/shipping-method/{action_id}`
- API呼び出し（追加）: `POST /admin/exchanges/{exchange_id}/outbound/shipping-method`

### 11-発送送料カスタマイズ

発送送料の編集アイコンをクリックすると、カスタム送料を入力できる。

- API呼び出し: `POST /admin/exchanges/{exchange_id}/outbound/shipping-method/{action_id}`
- リクエストボディ:
  ```json
  {
    "custom_amount": 500
  }
  ```

### 12-プロモーション引継ぎ変更

プロモーション引継ぎスイッチを変更すると、order_changeのcarry_over_promotionsが更新される。

- API呼び出し: `POST /admin/order-changes/{order_change_id}`
- リクエストボディ:
  ```json
  {
    "carry_over_promotions": true
  }
  ```

### 13-交換確定

「Confirm」ボタンをクリックすると、確認ダイアログ表示後、交換が確定される。

- 確認ダイアログ: 「Are you sure?」メッセージを表示
- API呼び出し: `POST /admin/exchanges/{exchange_id}/request`
- 成功時: モーダルを閉じて注文詳細画面へ遷移
- エラー時: トースト通知でエラーメッセージを表示

### 14-キャンセル

「Cancel」ボタンをクリックまたはモーダルを閉じると、交換リクエストがキャンセルされる。

- API呼び出し: `POST /admin/exchanges/{exchange_id}/cancel`
- 成功時: 成功トーストを表示

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示 | order | SELECT | 注文データの取得 |
| 交換初期化 | exchange | INSERT | 交換の作成 |
| 交換初期化 | return | INSERT | 返品リクエストの作成 |
| 交換初期化 | order_change | INSERT | 注文変更の作成 |
| 返送商品追加 | order_change_action | INSERT | 返送アクションの追加 |
| 返送商品更新 | order_change_action | UPDATE | 返送アクションの更新 |
| 返送商品削除 | order_change_action | DELETE | 返送アクションの削除 |
| 発送商品追加 | order_change_action | INSERT | 発送アクションの追加 |
| 発送商品更新 | order_change_action | UPDATE | 発送アクションの更新 |
| 発送商品削除 | order_change_action | DELETE | 発送アクションの削除 |
| プロモーション引継ぎ | order_change | UPDATE | carry_over_promotionsの更新 |
| 交換確定 | exchange | UPDATE | ステータス更新 |
| 交換確定 | order_change | UPDATE | 変更の確定 |
| キャンセル | exchange | UPDATE | ステータス更新 |
| キャンセル | order_change | DELETE | 変更の削除 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|----------|
| orders.exchanges.confirmText | 確認 | Are you sure you want to confirm this exchange? | 交換確定前 |
| orders.exchanges.activeChangeError | エラー | There is an active change on this order | 他の変更がアクティブな場合 |
| orders.exchanges.actions.cancelExchange.successToast | 成功 | Exchange cancelled | キャンセル成功時 |
| orders.returns.noInventoryLevel | 警告 | No inventory level | 在庫レベルが設定されていない場合 |
| orders.exchanges.carryOverPromotion | ラベル | Carry Over Promotion | プロモーション引継ぎスイッチ |
| orders.exchanges.carryOverPromotionHint | ヒント | Apply the promotion from the original order | プロモーション引継ぎ説明 |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| 他のアクティブな変更がある | エラートースト表示し、注文詳細画面へリダイレクト |
| 交換初期化失敗 | エラートースト表示し、注文詳細画面へリダイレクト |
| API呼び出しエラー | トースト通知でエラーメッセージを表示 |
| モーダルクローズ | 未確定の場合、交換リクエストをキャンセル |

## 備考

- 画面表示時に自動的に交換リクエストが初期化される（createExchange API呼び出し）
- 既存のアクティブな交換変更がある場合は、その変更を継続して編集できる
- 交換には関連する返品リクエスト（return）が自動的に作成される
- 在庫レベルが設定されていない商品には警告が表示される
- カスタム送料は通貨に応じた小数点桁数で設定される
- 返金/追加請求金額は、返送商品合計と発送商品合計の差額で計算される
- 元注文にプロモーションが適用されている場合、プロモーション引継ぎオプションが表示される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | exchange-create.tsx | `packages/admin/dashboard/src/routes/orders/order-create-exchange/exchange-create.tsx` | ページコンポーネントの構成を確認 |

**主要処理フロー**:
1. **行16**: `useParams`で注文ID取得
2. **行20-22**: `useOrder`で注文データ取得
3. **行24**: `useOrderPreview`で注文プレビュー取得
4. **行26**: `useCreateExchange`で交換作成フック取得
5. **行28-30**: `useExchange`で交換データ取得
6. **行32-34**: `useReturn`で関連する返品データ取得
7. **行36-70**: `useEffect`で交換リクエストの初期化処理

#### Step 2: フォームコンポーネントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | exchange-create-form.tsx | `packages/admin/dashboard/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-create-form.tsx` | フォームの実装詳細 |

**主要処理フロー**:
- **行81-82**: `useExchangeConfirmRequest`で交換確定フック取得
- **行84-85**: `useCancelExchangeRequest`でキャンセルフック取得
- **行87-95**: 配送更新フック取得
- **行97-104**: `useUpdateOrderChange`でプロモーション引継ぎ更新
- **行217-239**: `handleSubmit`で確認ダイアログと確定処理
- **行253-273**: アンマウント時のキャンセル処理

#### Step 3: Inbound/Outboundセクションを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | exchange-inbound-section.tsx | `packages/admin/dashboard/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-inbound-section.tsx` | 返送商品セクションの実装 |
| 3-2 | exchange-outbound-section.tsx | `packages/admin/dashboard/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-outbound-section.tsx` | 発送商品セクションの実装 |

**Inboundセクション主要処理フロー**:
- **行68-94**: 返送商品・配送操作フック取得
- **行212-243**: `onItemsSelected`で返送商品追加処理
- **行245-247**: `onLocationChange`でロケーション変更処理
- **行249-280**: `onShippingOptionChange`で配送オプション変更処理

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

```
ExchangeCreate (exchange-create.tsx)
    │
    ├─ useParams (id)
    ├─ useOrder (注文データ)
    ├─ useOrderPreview (プレビュー)
    ├─ useCreateExchange (交換作成)
    ├─ useExchange (交換データ)
    ├─ useReturn (返品データ)
    │
    └─ ExchangeCreateForm
           │
           ├─ useExchangeConfirmRequest
           ├─ useCancelExchangeRequest
           ├─ useUpdateExchangeInboundShipping
           ├─ useUpdateExchangeOutboundShipping
           ├─ useUpdateOrderChange
           │
           ├─ ExchangeInboundSection
           │      │
           │      ├─ useUpdateReturn
           │      ├─ useAddExchangeInboundShipping
           │      ├─ useDeleteExchangeInboundShipping
           │      ├─ useAddExchangeInboundItems
           │      ├─ useUpdateExchangeInboundItem
           │      ├─ useRemoveExchangeInboundItem
           │      ├─ useStockLocations
           │      ├─ useShippingOptions
           │      │
           │      └─ ExchangeInboundItem (返送商品)
           │
           └─ ExchangeOutboundSection
                  │
                  ├─ useAddExchangeOutboundItems
                  ├─ useUpdateExchangeOutboundItems
                  ├─ useRemoveExchangeOutboundItem
                  ├─ useAddExchangeOutboundShipping
                  ├─ useDeleteExchangeOutboundShipping
                  │
                  └─ ExchangeOutboundItem (発送商品)
```

### データフロー図

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

URLパラメータ ───▶ useParams ──────────────▶ orderId
                          │
                          ▼
画面表示 ────────▶ createExchange ──────────▶ 交換作成
                          │
                          ▼
操作（Inbound）──▶ 返送商品API ─────────────▶ 交換更新
                          │
                          ▼
操作（Outbound）─▶ 発送商品API ─────────────▶ 交換更新
                          │
                          ▼
プロモーション ──▶ updateOrderChange ───────▶ 引継ぎ設定
                          │
                          ▼
確定ボタン ──────▶ confirmExchangeRequest ──▶ 交換確定
                          │
                          ▼
キャンセル ──────▶ cancelExchangeRequest ───▶ 交換取消
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| exchange-create.tsx | `packages/admin/dashboard/src/routes/orders/order-create-exchange/exchange-create.tsx` | ソース | ページコンポーネント |
| exchange-create-form.tsx | `packages/admin/dashboard/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-create-form.tsx` | ソース | フォームコンポーネント |
| exchange-inbound-section.tsx | `packages/admin/dashboard/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-inbound-section.tsx` | ソース | 返送商品セクション |
| exchange-outbound-section.tsx | `packages/admin/dashboard/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-outbound-section.tsx` | ソース | 発送商品セクション |
| exchange-inbound-item.tsx | `packages/admin/dashboard/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-inbound-item.tsx` | ソース | 返送商品コンポーネント |
| exchange-outbound-item.tsx | `packages/admin/dashboard/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-outbound-item.tsx` | ソース | 発送商品コンポーネント |
| add-exchange-inbound-items-table.tsx | `packages/admin/dashboard/src/routes/orders/order-create-exchange/components/add-exchange-inbound-items-table/add-exchange-inbound-items-table.tsx` | ソース | 返送商品選択テーブル |
| add-exchange-outbound-items-table.tsx | `packages/admin/dashboard/src/routes/orders/order-create-exchange/components/add-exchange-outbound-items-table/add-exchange-outbound-items-table.tsx` | ソース | 発送商品選択テーブル |
| schema.ts | `packages/admin/dashboard/src/routes/orders/order-create-exchange/components/exchange-create-form/schema.ts` | ソース | バリデーションスキーマ |
| exchanges.tsx | `packages/admin/dashboard/src/hooks/api/exchanges.tsx` | ソース | API呼び出しフック |
