# 画面設計書 141-配送オプション料金設定画面

## 概要

本ドキュメントは、Medusa Admin Dashboard における配送オプション料金設定画面の設計仕様を記述したものである。

### 本画面の処理概要

**業務上の目的・背景**：ECサイト運営において、配送料金の設定は重要な業務の一つである。配送オプションごとに通貨別・リージョン別の料金を設定し、さらに注文金額に応じた条件付き料金（例：○○円以上で送料無料）を柔軟に設定できることで、多様なビジネス要件に対応可能となる。本画面は、サービスゾーンに紐づく配送オプションの料金を詳細に設定・編集するためのものである。

**画面へのアクセス方法**：設定 > ロケーション > ロケーション詳細 > サービスゾーン > 配送オプション > 料金設定 の順にナビゲートするか、ロケーション詳細画面から配送オプションの料金設定アイコンをクリックしてアクセスする。URLパスは `/settings/locations/:location_id/shipping-options/:so_id/pricing` である。

**主要な操作・処理内容**：
1. 通貨別の基本配送料金を設定する
2. リージョン別の配送料金を設定する
3. 条件付き料金（注文金額に応じた料金ルール）を追加・編集する
4. DataGridによる一括価格編集を行う
5. 設定内容を保存する

**画面遷移**：
- 遷移元: ロケーション詳細画面（配送オプション一覧から料金設定を選択）
- 遷移先: ロケーション詳細画面（保存・キャンセル後）

**権限による表示制御**：管理者権限を持つユーザーのみアクセス可能。配送設定の編集権限がない場合は表示されない。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 43 | 配送オプション管理 | 主機能 | 配送オプションの料金設定 |
| 54 | 価格計算 | 補助機能 | 通貨別料金の計算 |

## 画面種別

編集（モーダル形式）

## URL/ルーティング

```
/settings/locations/:location_id/shipping-options/:so_id/pricing
```

## 入出力項目

### 入力項目

| 項目名 | 項目ID | データ型 | 必須 | 説明 |
|--------|--------|----------|------|------|
| 通貨別料金 | currency_prices | Record<string, number> | - | 通貨コードをキーとした料金マップ |
| リージョン別料金 | region_prices | Record<string, number> | - | リージョンIDをキーとした料金マップ |
| 条件付き通貨料金 | conditional_currency_prices | Record<string, ConditionalPrice[]> | - | 通貨別の条件付き料金設定 |
| 条件付きリージョン料金 | conditional_region_prices | Record<string, ConditionalPrice[]> | - | リージョン別の条件付き料金設定 |

### 条件付き料金（ConditionalPrice）の構造

| 項目名 | 項目ID | データ型 | 必須 | 説明 |
|--------|--------|----------|------|------|
| ID | id | string | - | 既存価格ルールのID |
| 金額 | amount | number | 必須 | 配送料金 |
| 以上 | gte | number | - | 注文金額が指定値以上 |
| 以下 | lte | number | - | 注文金額が指定値以下 |
| より大きい | gt | number | - | 注文金額が指定値より大きい |
| 未満 | lt | number | - | 注文金額が指定値未満 |
| 等しい | eq | number | - | 注文金額が指定値と等しい |

## 表示項目

| 項目名 | データソース | 説明 |
|--------|-------------|------|
| 配送オプション名 | shippingOption.name | ヘッダーに表示される配送オプションの名称 |
| 通貨一覧 | store.supported_currencies | 設定可能な通貨の一覧 |
| リージョン一覧 | regions | 設定可能なリージョンの一覧（名称、通貨コード含む） |
| 現在の価格設定 | shippingOption.prices | 既存の価格設定と価格ルール |
| DataGrid | - | 通貨・リージョン別料金を編集するグリッド |

## イベント仕様

### 1-保存ボタン押下

**トリガー**：保存ボタンをクリック

**処理フロー**：
1. フォームのバリデーションを実行
2. 通貨別料金データを整形（通貨コード、金額、既存IDの付与）
3. 条件付き通貨料金データを整形（価格ルールの構築）
4. リージョン別料金データを整形（リージョンID、金額）
5. 条件付きリージョン料金データを整形（価格ルールの構築）
6. すべての価格データを統合
7. `useUpdateShippingOptions` フックを使用してAPIを呼び出し
8. 成功時: 成功トーストを表示し、モーダルを閉じてロケーション詳細画面に戻る
9. 失敗時: エラートーストを表示

### 2-キャンセルボタン押下

**トリガー**：キャンセルボタンをクリック

**処理フロー**：
1. モーダルを閉じる
2. ロケーション詳細画面 (`/settings/locations/:location_id`) に遷移

### 3-条件付き料金編集

**トリガー**：DataGridの条件付き料金セルをクリック

**処理フロー**：
1. `onOpenConditionalPricesModal` が呼び出される
2. `StackedFocusModal` が開く
3. `ConditionalPriceForm` コンポーネントで条件付き料金を編集
4. 保存時にフォームの状態を更新

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 保存ボタン押下 | shipping_option_price | UPDATE/INSERT/DELETE | 配送オプションの価格情報を更新 |
| 保存ボタン押下 | shipping_option_price_rule | UPDATE/INSERT/DELETE | 価格ルール（条件付き料金）を更新 |

### テーブル別更新項目詳細

#### shipping_option_price

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | currency_code | フォーム入力値（通貨コード） | 通貨別料金の場合 |
| INSERT/UPDATE | region_id | フォーム入力値（リージョンID） | リージョン別料金の場合 |
| INSERT/UPDATE | amount | フォーム入力値（金額） | 価格金額 |
| INSERT/UPDATE | shipping_option_id | URLパラメータso_id | 親配送オプションのID |

#### shipping_option_price_rule

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | attribute | item_total / region_id | ルール属性 |
| INSERT/UPDATE | operator | gte / lte / gt / lt / eq | 比較演算子 |
| INSERT/UPDATE | value | フォーム入力値 | 条件値 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| general.success | 成功 | 保存に成功しました | 保存処理成功時 |
| - | エラー | {APIエラーメッセージ} | API呼び出し失敗時 |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| 配送オプションIDが未指定 | 404エラーをthrowし、エラーページを表示 |
| 配送オプション取得失敗 | エラーをthrowし、エラーページを表示 |
| ストア情報取得失敗 | エラーをthrowし、エラーページを表示 |
| リージョン情報取得失敗 | エラーをthrowし、エラーページを表示 |
| 保存処理失敗 | エラートーストを表示し、モーダルは閉じない |

## 備考

- DataGridコンポーネントを使用した一括価格編集UIを提供
- 条件付き料金はStackedFocusModalを使用したネストしたモーダルで編集
- 価格ルールは `item_total` 属性を使用して注文金額に基づく条件を設定
- リージョン別料金はAPIの制約により、更新時に再作成される仕様

---

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

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

### 推奨読解順序

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

配送オプションの価格設定に関するデータ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | HttpTypes | `packages/core/types/src/http/shipping-option/` | AdminShippingOption、AdminShippingOptionPrice、AdminShippingOptionPriceRuleの型定義 |
| 1-2 | schema.ts | `packages/admin/dashboard/src/routes/locations/common/schema.ts` | UpdateConditionalPriceSchemaの定義 |
| 1-3 | types.ts | `packages/admin/dashboard/src/routes/locations/common/types.ts` | ConditionalPriceInfoの型定義 |

**読解のコツ**: zodによるスキーマ定義とTypeScriptの型定義の関係を理解する。価格ルール（price_rules）の構造が条件付き料金の中核。

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

画面のルートコンポーネントを特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | location-service-zone-shipping-option-pricing.tsx | `packages/admin/dashboard/src/routes/locations/location-service-zone-shipping-option-pricing/location-service-zone-shipping-option-pricing.tsx` | ルートコンポーネント、useParamsでURLパラメータ取得、useShippingOptionでデータ取得 |

**主要処理フロー**:
1. **7-9行目**: URLパラメータ（so_id, location_id）の取得
2. **10-15行目**: so_idがない場合の404エラー処理
3. **17-23行目**: useShippingOptionフックで配送オプションデータを取得
4. **29-35行目**: RouteFocusModalでフォームを表示

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

価格編集フォームの実装を読み解く。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | edit-shipping-options-pricing-form.tsx | `packages/admin/dashboard/src/routes/locations/location-service-zone-shipping-option-pricing/components/create-shipping-options-form/edit-shipping-options-pricing-form.tsx` | フォーム全体の実装 |

**主要処理フロー**:
- **45-62行目**: EditShippingOptionPricingSchemaの定義（zod）
- **87-90行目**: useFormによるフォーム初期化、getDefaultValuesで既存価格を変換
- **92行目**: useUpdateShippingOptionsフックの取得
- **101-104行目**: ストアの通貨設定を取得
- **106-114行目**: リージョン情報を取得
- **132-219行目**: handleSubmitで価格データを整形してAPI送信
- **254-263行目**: DataGridコンポーネントで価格を一括編集

#### Step 4: APIフックを理解する

データ取得・更新のAPIフック実装を読み解く。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | shipping-options.tsx | `packages/admin/dashboard/src/hooks/api/shipping-options.tsx` | useShippingOption、useUpdateShippingOptionsの実装 |

**主要処理フロー**:
- **21-38行目**: useShippingOptionでの配送オプション取得
- **83-104行目**: useUpdateShippingOptionsでの価格更新、キャッシュ無効化

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

```
LocationServiceZoneShippingOptionPricing
    │
    ├─ useShippingOption (配送オプション取得)
    │      └─ sdk.admin.shippingOption.retrieve
    │
    └─ EditShippingOptionsPricingForm
           │
           ├─ useStore (ストア設定取得)
           │      └─ sdk.admin.store.retrieve
           │
           ├─ useRegions (リージョン一覧取得)
           │      └─ sdk.admin.region.list
           │
           ├─ usePricePreferences (価格設定取得)
           │
           ├─ useShippingOptionPriceColumns (DataGrid列定義)
           │
           ├─ DataGrid (価格編集グリッド)
           │
           ├─ ConditionalPriceForm (条件付き料金編集)
           │
           └─ useUpdateShippingOptions (価格更新)
                  └─ sdk.admin.shippingOption.update
```

### データフロー図

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

URLパラメータ              ───▶ useShippingOption              ───▶ shippingOption
(so_id, location_id)

shippingOption.prices      ───▶ getDefaultValues               ───▶ フォーム初期値
                                (価格データの変換)

store.supported_currencies ───▶ useShippingOptionPriceColumns  ───▶ DataGrid列定義
regions

フォーム入力値             ───▶ handleSubmit                   ───▶ API (update)
                                (価格データの整形)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| location-service-zone-shipping-option-pricing.tsx | `packages/admin/dashboard/src/routes/locations/location-service-zone-shipping-option-pricing/` | ソース | ルートコンポーネント |
| edit-shipping-options-pricing-form.tsx | `packages/admin/dashboard/src/routes/locations/location-service-zone-shipping-option-pricing/components/create-shipping-options-form/` | ソース | 価格編集フォーム |
| shipping-options.tsx | `packages/admin/dashboard/src/hooks/api/` | ソース | 配送オプションAPIフック |
| use-shipping-option-price-columns.tsx | `packages/admin/dashboard/src/routes/locations/common/hooks/` | ソース | DataGrid列定義フック |
| conditional-price-form.tsx | `packages/admin/dashboard/src/routes/locations/common/components/` | ソース | 条件付き料金編集フォーム |
| price-rule-helpers.ts | `packages/admin/dashboard/src/routes/locations/common/utils/` | ソース | 価格ルール構築ヘルパー |
| schema.ts | `packages/admin/dashboard/src/routes/locations/common/` | ソース | バリデーションスキーマ |
| constants.ts | `packages/admin/dashboard/src/routes/locations/common/` | ソース | 定数定義（ITEM_TOTAL_ATTRIBUTE等） |
