# 画面設計書 12-商品価格編集画面

## 概要

本ドキュメントは、Medusa Admin Dashboard における商品価格編集画面の設計仕様を定義するものである。

### 本画面の処理概要

商品価格編集画面は、商品バリアントの価格情報を一括で編集するための画面である。複数の通貨およびリージョン別の価格設定を効率的に管理することができる。

**業務上の目的・背景**：グローバルECにおいて、各国・地域ごとに異なる価格戦略を適用する必要がある。本画面は、複数通貨・リージョンに対応した価格設定を一元管理し、価格変更作業を効率化するために必要である。為替変動や地域別の価格競争力に応じた柔軟な価格設定を実現する。

**画面へのアクセス方法**：商品詳細画面の価格セクションから「Edit Prices」ボタンをクリックすることでモーダル形式で表示される。URLパスは `/products/:id/prices` となる。特定のバリアントのみを編集する場合は `/products/:id/prices/:variant_id` の形式でアクセスする。

**主要な操作・処理内容**：
1. バリアントごとの通貨別価格の設定・編集
2. リージョン別価格の設定・編集（リージョンIDをキーとした価格ルール）
3. 複数バリアントの価格一括編集
4. 価格のクリア（空欄設定）による削除

**画面遷移**：
- 遷移元：商品詳細画面（No.7）
- 遷移先：商品詳細画面（保存/キャンセル後）

**権限による表示制御**：本画面へのアクセスには商品管理権限が必要。権限がない場合は画面にアクセスできない。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 52 | 価格設定管理 | 主機能 | 商品価格の編集 |
| 54 | 価格計算 | 補助機能 | 通貨・地域に応じた価格表示 |

## 画面種別

編集（モーダル形式・フルスクリーン）

## URL/ルーティング

```
/products/:id/prices
/products/:id/prices/:variant_id
```

パラメータ：
- `id`：商品ID（必須）
- `variant_id`：バリアントID（オプション、指定時は特定バリアントのみ表示）

## 入出力項目

### 入力項目

| 項目名 | 項目ID | 型 | 必須 | バリデーション | 備考 |
|--------|--------|-----|------|---------------|------|
| バリアント価格 | variants[].prices | Record<string, string \| number> | - | 数値または空文字 | 通貨コードまたはリージョンIDをキーとする |

### 出力項目

| 項目名 | 項目ID | 型 | 備考 |
|--------|--------|-----|------|
| バリアント一覧 | variants | Variant[] | 各バリアントの価格情報を含む |

## 表示項目

### 価格編集グリッド

| 項目名 | 表示形式 | 備考 |
|--------|---------|------|
| バリアントタイトル | テキスト | 行ヘッダーとして表示 |
| 通貨別価格 | 数値入力 | 通貨コード（USD, JPY等）をカラムヘッダーに表示 |
| リージョン別価格 | 数値入力 | リージョン名をカラムヘッダーに表示 |

## イベント仕様

### 1-価格入力

**トリガー**：価格セルへの入力

**処理フロー**：
1. `VariantPricingForm`コンポーネントで価格グリッドを表示
2. セル編集時にreact-hook-formの状態を更新
3. 通貨コードまたはリージョンIDをキーとして価格を格納

**バリデーション**：数値のみ許可、空文字は削除として扱う

### 2-保存

**トリガー**：「Save」ボタン押下

**処理フロー**：
1. フォームデータを取得
2. 各バリアントの価格エントリーを処理
   - 空文字・undefinedのエントリーはスキップ
   - リージョンIDで始まるキー（reg_）はリージョン価格として処理
   - それ以外は通貨コードとして処理
3. 既存の価格IDを検索し、存在すればupdate、なければcreate
4. `useUpdateProductVariantsBatch`ミューテーションで一括更新
5. 成功時に親画面（商品詳細）へ遷移

**価格変換処理**：
- `castNumber`関数で文字列を数値に変換
- リージョン価格の場合、リージョンに紐づく通貨コードを`regionsCurrencyMap`から取得

### 3-キャンセル

**トリガー**：「Cancel」ボタン押下またはモーダル外クリック

**処理フロー**：変更を破棄し、商品詳細画面に戻る

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 保存ボタン押下 | product_variant_price | INSERT/UPDATE | バリアント価格の作成・更新 |

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

#### product_variant_price

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | currency_code | 通貨コード（例：USD, JPY） | 必須 |
| INSERT/UPDATE | amount | 価格金額（数値） | castNumber関数で変換 |
| INSERT/UPDATE | rules | リージョンIDを含むルールオブジェクト | リージョン価格の場合のみ |

## メッセージ仕様

| 種別 | メッセージキー | 表示条件 |
|------|--------------|---------|
| 成功 | (なし - モーダル遷移) | 保存成功時 |
| エラー | (API error message) | API呼び出し失敗時 |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| 商品が見つからない | エラーをスロー（親コンポーネントでハンドリング） |
| バリアントが存在しない | 空のフォームを表示 |
| API呼び出し失敗 | エラーハンドリング（RouteFocusModalで処理） |

## 備考

- 本画面ではDataGridコンポーネント相当の`VariantPricingForm`を使用
- リージョン情報は`useRegions`フックで取得（limit: 9999で全件取得）
- 価格の単位は最小通貨単位（例：セント）で保存される
- 特定バリアントIDが指定された場合、そのバリアントのみをフィルタリングして表示

---

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

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

### 推奨読解順序

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

まず、価格データの構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | pricing-edit.tsx | `packages/admin/dashboard/src/routes/products/product-prices/pricing-edit.tsx` | UpdateVariantPricesSchemaの定義（16-24行目） |

**読解のコツ**: 価格はRecord<string, string | number>形式で、キーが通貨コードまたはリージョンIDとなる。リージョンIDは`reg_`プレフィックスで判別される。

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

処理の起点となるメインコンポーネントを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | product-prices.tsx | `packages/admin/dashboard/src/routes/products/product-prices/product-prices.tsx` | ルートコンポーネント、商品データのフェッチ |

**主要処理フロー**:
1. **8行目**: useParamsでid, variant_idを取得
2. **10-13行目**: useProductで商品データを取得
3. **19-25行目**: RouteFocusModal内でPricingEditをレンダリング

#### Step 3: 価格編集フォームを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | pricing-edit.tsx | `packages/admin/dashboard/src/routes/products/product-prices/pricing-edit.tsx` | 価格編集のメインロジック |

**主要処理フロー**:
- **41-51行目**: リージョンと通貨のマッピングを作成
- **53-55行目**: variant_id指定時のフィルタリング
- **57-73行目**: フォーム初期値の設定（既存価格のマッピング）
- **75-120行目**: handleSubmit - 価格データの変換と保存

**価格変換ロジック**（82-112行目）:
```
1. 空文字・undefinedをスキップ
2. reg_で始まるキーはリージョン価格として処理
3. リージョン価格の場合、regionsCurrencyMapから通貨コードを取得
4. 既存価格IDを検索してupdate/createを判別
5. amountはcastNumber関数で数値に変換
```

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

```
ProductPrices (product-prices.tsx)
    │
    ├─ useParams
    │      └─ id, variant_id を取得
    │
    ├─ useProduct (hooks/api/products.tsx)
    │      └─ sdk.admin.product.retrieve
    │
    └─ PricingEdit (pricing-edit.tsx)
           │
           ├─ useRegions (hooks/api/regions.tsx)
           │      └─ sdk.admin.region.list
           │
           ├─ useForm (react-hook-form)
           │      └─ zodResolver(UpdateVariantPricesSchema)
           │
           ├─ VariantPricingForm (common/variant-pricing-form)
           │      └─ 価格グリッドUI
           │
           └─ useUpdateProductVariantsBatch
                  └─ sdk.admin.product.batchVariants
```

### データフロー図

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

既存価格データ ─────────▶ defaultValues生成 ─────────▶ フォーム初期状態
(variant.prices)              │
                              ├─ currency_code → prices[currency_code]
                              └─ region_id → prices[region_id]

価格セル編集 ─────────▶ react-hook-form ─────────────▶ フォーム状態更新
                              │
保存ボタン ──────────▶ handleSubmit ─────────────────▶ API更新
                              │
                              ├─ 空値フィルタリング
                              ├─ リージョン/通貨判定
                              ├─ 既存価格ID検索
                              ├─ castNumber変換
                              │
                              └─ useUpdateProductVariantsBatch
                                     └─ sdk.admin.product.batchVariants
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| product-prices.tsx | `packages/admin/dashboard/src/routes/products/product-prices/product-prices.tsx` | ソース | ルートコンポーネント |
| pricing-edit.tsx | `packages/admin/dashboard/src/routes/products/product-prices/pricing-edit.tsx` | ソース | 価格編集フォーム |
| index.ts | `packages/admin/dashboard/src/routes/products/product-prices/index.ts` | ソース | エクスポート定義 |
| products.tsx | `packages/admin/dashboard/src/hooks/api/products.tsx` | ソース | 商品関連APIフック（useUpdateProductVariantsBatch含む） |
| regions.tsx | `packages/admin/dashboard/src/hooks/api/regions.tsx` | ソース | リージョン関連APIフック |
| cast-number.ts | `packages/admin/dashboard/src/lib/cast-number.ts` | ソース | 数値変換ユーティリティ |
| variant-pricing-form | `packages/admin/dashboard/src/routes/products/common/variant-pricing-form` | ソース | 価格グリッドUIコンポーネント |
