# 画面設計書 61-プロモーション編集画面

## 概要

本ドキュメントは、Medusa Admin Dashboard のプロモーション編集画面について、画面仕様・機能・データ処理を定義した画面設計書です。

### 本画面の処理概要

この画面では、既存のプロモーションの詳細情報を編集することができます。プロモーションのステータス、適用方法、コード、値タイプ、割引値、配分方法などの設定を変更できます。

**業務上の目的・背景**：プロモーションは顧客獲得・売上向上のための重要なマーケティングツールです。一度作成したプロモーションの条件や設定を市場状況や在庫状況に応じて柔軟に変更できることは、運用効率の向上に直結します。また、プロモーションのステータス（下書き/有効/無効）を切り替えることで、期間限定キャンペーンの開始・終了を管理できます。

**画面へのアクセス方法**：プロモーション詳細画面から「編集」ボタンをクリックすることで、右側からスライドインするドロワー形式でこの画面が表示されます。URL: `/promotions/:id/edit`

**主要な操作・処理内容**：
1. プロモーションのステータス（下書き/有効/無効）を選択
2. 適用方法（コード入力/自動適用）を選択
3. プロモーションコードを編集
4. 税込み設定の切り替え（固定値タイプかつ標準プロモーションの場合）
5. 値タイプ（固定額/割合）を選択
6. 割引値（金額または割合）を入力
7. 配分方法（各アイテム/合計/一度のみ）を選択
8. 最大数量を入力（配分方法が「各アイテム」または「一度のみ」の場合）

**画面遷移**：
- 遷移元：プロモーション詳細画面（`/promotions/:id`）
- 遷移先：保存成功時はプロモーション詳細画面に戻る、キャンセル時も同様

**権限による表示制御**：認証済みの管理者ユーザーのみがアクセス可能です。プロモーション管理権限を持つユーザーが操作できます。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 36 | プロモーションCRUD | 主機能 | プロモーション情報の編集 |

## 画面種別

編集（モーダル/ドロワー形式）

## URL/ルーティング

`/promotions/:id/edit`

## 入出力項目

| 項目名 | 項目ID | 型 | 必須 | 入力/出力 | 説明 |
|--------|--------|-----|------|-----------|------|
| ステータス | status | enum | ○ | 入力 | draft/active/inactive |
| 適用方法 | is_automatic | string | ○ | 入力 | "true"（自動）/ "false"（コード） |
| 税込み | is_tax_inclusive | boolean | - | 入力 | 固定値かつ標準タイプの場合のみ |
| プロモーションコード | code | string | ○ | 入力 | 1文字以上 |
| 値タイプ | value_type | enum | ○ | 入力 | fixed/percentage |
| 割引値 | value | number | ○ | 入力 | 0以上 |
| 配分方法 | allocation | enum | ○ | 入力 | each/across/once |
| 最大数量 | max_quantity | number | - | 入力 | 1以上（配分方法がeach/onceの場合） |

## 表示項目

| 項目名 | 説明 |
|--------|------|
| 画面タイトル | 「プロモーションを編集」 |
| ステータス選択 | RadioGroupで下書き/有効/無効を選択 |
| 適用方法選択 | RadioGroupでコード/自動を選択 |
| 税込みスイッチ | 固定値タイプの場合のみ表示 |
| プロモーションコード | テキスト入力フィールド |
| 値タイプ選択 | RadioGroupで固定額/割合を選択（配送以外のターゲット） |
| 割引値入力 | 通貨入力または割合入力 |
| 配分方法選択 | RadioGroupで各/合計/一度を選択 |
| 最大数量入力 | 数値入力フィールド |
| キャンセルボタン | ドロワーを閉じる |
| 保存ボタン | 変更を保存 |

## イベント仕様

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

**処理フロー**：
1. フォームバリデーションを実行
2. 値が有効な数値かチェック（NaNまたは負数の場合はエラー）
3. `useUpdatePromotion` フックを使用してAPIを呼び出し
4. 成功時：ドロワーを閉じてプロモーション詳細画面に戻る
5. 失敗時：エラーメッセージを表示

**API呼び出し**：
- エンドポイント: `POST /admin/promotions/:id`
- リクエストボディ:
```json
{
  "is_automatic": boolean,
  "code": string,
  "status": "draft" | "active" | "inactive",
  "is_tax_inclusive": boolean,
  "application_method": {
    "value": number,
    "type": "fixed" | "percentage",
    "allocation": "each" | "across" | "once",
    "max_quantity": number | null
  }
}
```

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

**処理フロー**：
1. フォームの変更を破棄
2. ドロワーを閉じてプロモーション詳細画面に戻る

### 3-値タイプ変更

**処理フロー**：
1. 値タイプが固定額以外、または標準タイプ以外の場合、税込み設定をfalseにリセット
2. 入力フィールドの表示を切り替え（通貨入力/割合入力）

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 保存ボタン押下 | promotion | UPDATE | プロモーション基本情報の更新 |
| 保存ボタン押下 | promotion_application_method | UPDATE | 適用方法の更新 |

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

#### promotion

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | is_automatic | フォームの is_automatic === "true" | boolean変換 |
| UPDATE | code | フォームの code | |
| UPDATE | status | フォームの status | |
| UPDATE | is_tax_inclusive | フォームの is_tax_inclusive | |

#### promotion_application_method

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | value | フォームの value（parseFloat） | |
| UPDATE | type | フォームの value_type | |
| UPDATE | allocation | フォームの allocation | |
| UPDATE | max_quantity | フォームの max_quantity | null許可 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| MSG-001 | エラー | 値が無効です | 値が数値でないまたは負数の場合 |
| MSG-002 | エラー | コードは必須です | コードが空の場合 |

## 例外処理

| 例外 | 処理内容 |
|------|---------|
| プロモーションが見つからない | エラーをスローしてエラー画面を表示 |
| API通信エラー | エラーメッセージをtoastで表示 |
| バリデーションエラー | 各フィールドにエラーメッセージを表示 |

## 備考

- 配分方法の選択肢は、元の配分方法によって制限される（eachからacrossへの変更は不可など）
- target_typeが「shipping_methods」の場合、値タイプ・配分方法などの項目は表示されない
- 税込み設定は固定値タイプかつ標準プロモーションの場合のみ表示・編集可能

---

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

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

### 推奨読解順序

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

まず、プロモーションのデータ構造とフォームスキーマを理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | edit-promotion-details-form.tsx | `packages/admin/dashboard/src/routes/promotions/promotion-edit-details/components/edit-promotion-form/edit-promotion-details-form.tsx` | EditPromotionSchema（25-35行目）でフォームの構造を確認 |

**読解のコツ**: zodを使ったスキーマ定義でフォームの型と検証ルールを確認できます。

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | promotion-edit-details.tsx | `packages/admin/dashboard/src/routes/promotions/promotion-edit-details/promotion-edit-details.tsx` | usePromotionフックでデータ取得（13行目） |

**主要処理フロー**:
1. **10行目**: useParamsでURLからプロモーションIDを取得
2. **13行目**: usePromotionフックでプロモーションデータを取得
3. **25-26行目**: ローディング完了後にEditPromotionDetailsFormを表示

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | edit-promotion-details-form.tsx | `packages/admin/dashboard/src/routes/promotions/promotion-edit-details/components/edit-promotion-form/edit-promotion-details-form.tsx` | フォームロジックと送信処理 |

**主要処理フロー**:
- **43-56行目**: useFormでフォーム初期化
- **71行目**: useUpdatePromotionでミューテーション準備
- **73-100行目**: handleSubmitで送信処理

#### Step 4: API層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | promotions.tsx | `packages/admin/dashboard/src/hooks/api/promotions.tsx` | useUpdatePromotionフックの実装（206-223行目） |
| 4-2 | route.ts | `packages/medusa/src/api/admin/promotions/[id]/route.ts` | POST APIエンドポイントの実装（42-69行目） |

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

```
PromotionEditDetails (promotion-edit-details.tsx)
    │
    ├─ usePromotion() - プロモーションデータ取得
    │      └─ sdk.admin.promotion.retrieve()
    │
    └─ EditPromotionDetailsForm
           │
           ├─ useForm() - フォーム状態管理
           │
           ├─ useWatch() - フィールド監視（値タイプ、配分方法）
           │
           └─ useUpdatePromotion() - 更新処理
                  └─ sdk.admin.promotion.update()
                         └─ POST /admin/promotions/:id
                                └─ updatePromotionsWorkflow
```

### データフロー図

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

URLパラメータ ────▶ usePromotion ────▶ プロモーションデータ
     (id)                                      ↓
                                         フォーム初期値
                                              ↓
フォーム入力 ─────▶ useForm ──────▶ フォーム状態
                                              ↓
保存ボタン ──────▶ handleSubmit ───▶ バリデーション
                                              ↓
                  useUpdatePromotion ──▶ API更新
                                              ↓
                                        handleSuccess()
                                              ↓
                                        画面遷移（詳細画面）
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| promotion-edit-details.tsx | `packages/admin/dashboard/src/routes/promotions/promotion-edit-details/promotion-edit-details.tsx` | ソース | エントリーポイント |
| edit-promotion-details-form.tsx | `packages/admin/dashboard/src/routes/promotions/promotion-edit-details/components/edit-promotion-form/edit-promotion-details-form.tsx` | ソース | フォームコンポーネント |
| promotions.tsx | `packages/admin/dashboard/src/hooks/api/promotions.tsx` | ソース | APIフック |
| route.ts | `packages/medusa/src/api/admin/promotions/[id]/route.ts` | ソース | APIエンドポイント |
| RouteDrawer | `packages/admin/dashboard/src/components/modals` | ソース | ドロワーUIコンポーネント |
| Form | `packages/admin/dashboard/src/components/common/form` | ソース | フォームUIコンポーネント |
