# 機能設計書 52-価格設定管理

## 概要

本ドキュメントは、Medusa ECプラットフォームにおける価格設定管理機能の設計を記述する。商品・バリアントの価格設定機能の詳細仕様を定義する。

### 本機能の処理概要

価格設定管理機能は、商品やバリアントに対する価格の作成・更新・削除を行う機能である。価格はPriceSetという単位で管理され、複数の通貨や条件に応じた価格（Price）を持つことができる。さらに、価格ルール（PriceRule）を設定することで、地域や顧客グループごとの条件付き価格設定が可能である。

**業務上の目的・背景**：ECサイトでは、同じ商品でも国や地域、通貨、顧客セグメントによって異なる価格を設定する必要がある。また、卸売価格と小売価格、会員価格と一般価格など、多様な価格戦略を実現するための柔軟な価格管理基盤が求められる。

**機能の利用シーン**：
1. 商品作成時に基本価格を設定
2. バリアントごとに異なる価格を設定
3. 特定の通貨・地域向けの価格を追加
4. 数量割引（min_quantity/max_quantity）の設定
5. 価格ルールによる条件付き価格の設定

**主要な処理内容**：
1. PriceSetの作成・更新・削除
2. Priceの作成・更新・削除
3. PriceRuleの作成・更新・削除
4. 価格の追加（addPrices）
5. 価格の削除（removePrices）

**関連システム・外部連携**：価格モジュールは商品モジュール、カートモジュール、注文モジュールと連携し、各モジュールで価格計算の基盤データを提供する。

**権限による制御**：価格設定は管理者のみが実行可能。ストアフロントAPIでは価格の参照のみ可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 6 | 商品作成画面 | 補助機能 | 商品価格の設定 |
| 12 | 商品価格編集画面 | 主機能 | 商品価格の編集 |
| 18 | 商品バリアント作成画面 | 補助機能 | バリアント価格の設定 |
| 76 | 価格追加画面 | 主機能 | 価格表への価格追加 |
| 77 | 価格編集画面 | 主機能 | 価格の編集 |

## 機能種別

CRUD操作

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | string | No | PriceSetのID（更新時） | pset_プレフィックス |
| prices | CreatePricesDTO[] | No | 価格の配列 | - |
| prices[].currency_code | string | Yes | 通貨コード | ISO 4217形式 |
| prices[].amount | BigNumberInput | Yes | 価格金額 | 0以上の数値 |
| prices[].min_quantity | BigNumberInput | No | 最小数量 | 0以上の数値 |
| prices[].max_quantity | BigNumberInput | No | 最大数量 | min_quantity以上 |
| prices[].rules | Record<string, string> | No | 価格ルール | - |

### 入力データソース

- リクエストパラメータ（API経由）
- 商品モジュールからの連携（商品・バリアント作成時）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | string | PriceSetのID |
| prices | PriceDTO[] | 価格の配列 |
| prices[].id | string | 価格のID |
| prices[].currency_code | string | 通貨コード |
| prices[].amount | BigNumber | 価格金額 |
| prices[].price_rules | PriceRuleDTO[] | 価格ルールの配列 |

### 出力先

- API レスポンス（JSON形式）
- 商品モジュールへの連携

## 処理フロー

### 処理シーケンス

```
1. 入力データの正規化
   └─ normalizePrices()で価格データを正規化
2. 価格ルールの処理
   └─ rulesをprice_rulesに変換
3. PriceSetの作成/更新
   └─ priceSetService_.createまたはupsertWithReplace
4. Priceの作成/更新
   └─ priceService_.createまたはupdate
5. PriceRuleの作成/更新
   └─ priceRuleService_.createまたはupdate
6. 結果の返却
   └─ シリアライズしてDTO形式で返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[入力データの正規化]
    B --> C{PriceSetが存在?}
    C -->|No| D[PriceSet作成]
    C -->|Yes| E[PriceSet更新]
    D --> F[Price作成]
    E --> G[Price更新/作成/削除]
    F --> H[PriceRule作成]
    G --> I[PriceRule処理]
    H --> J[結果を返却]
    I --> J
    J --> K[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-52-01 | 通貨コード必須 | 価格には必ず通貨コードが必要 | Price作成時 |
| BR-52-02 | 金額必須 | 価格には必ず金額が必要 | Price作成時 |
| BR-52-03 | 数量範囲検証 | max_quantityはmin_quantity以上 | min/max_quantity指定時 |
| BR-52-04 | ルール演算子検証 | operatorはeq, gt, gte, lt, lte, neのいずれか | ルール設定時 |
| BR-52-05 | 価格ハッシュ重複防止 | 同一条件の価格は重複不可 | Price作成/更新時 |

### 計算ロジック

```typescript
// 価格のハッシュ計算（重複判定用）
priceHash = `cc:${currency_code}|ps:${price_set_id}|pl:${price_list_id}|min:${min_quantity}|max:${max_quantity}|rules:${sortedRules}`

// ルール数のカウント
rules_count = price_rules.length
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 価格セット作成 | price_set | INSERT | 価格セットの新規作成 |
| 価格作成 | price | INSERT | 価格の新規作成 |
| 価格ルール作成 | price_rule | INSERT | 価格ルールの新規作成 |
| 価格更新 | price | UPDATE | 価格の更新 |
| 価格削除 | price | DELETE | 価格の削除 |

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

#### price_set

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | 自動生成（pset_プレフィックス） | - |

#### price

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id, currency_code, amount, min_quantity, max_quantity, price_set_id | リクエストパラメータから | - |
| UPDATE | currency_code, amount, min_quantity, max_quantity | WHERE id = :id | - |
| DELETE | - | WHERE id IN (:ids) | 物理削除 |

#### price_rule

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id, attribute, value, price_id | リクエストパラメータから | - |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| INVALID_DATA | MedusaError | 無効な演算子が指定された | 正しい演算子を使用 |
| INVALID_DATA | MedusaError | ルール値が数値でない | 数値を指定 |
| NOT_FOUND | MedusaError | PriceSetが存在しない | 正しいIDを指定 |

### リトライ仕様

データベース一時エラー時はアプリケーション層でリトライ可能。

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

PriceSet、Price、PriceRuleの操作は同一トランザクション内で実行される。@InjectTransactionManagerデコレータによりトランザクション管理される。いずれかの操作が失敗した場合は全体がロールバックされる。

## パフォーマンス要件

- 単一PriceSet作成: 200ms以内
- 大量価格更新（100件）: 1000ms以内
- インデックス: price_set_id, price_list_id, currency_codeにインデックス設定

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

- 認証: 価格設定操作はAdmin API認証必須
- 認可: 価格参照はStore APIで公開（認証不要）
- 監査ログ: 価格変更は監査ログに記録

## 備考

- 価格キャッシュはclearAvailableAttributes()で明示的にクリア可能
- 大量の価格更新時はバッチ処理を推奨

---

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

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

### 推奨読解順序

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

まず、価格関連のデータモデルを理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | price-set.ts | `packages/modules/pricing/src/models/price-set.ts` | PriceSetの構造、pricesリレーションを確認 |
| 1-2 | price.ts | `packages/modules/pricing/src/models/price.ts` | Priceの構造、currency_code, amount, price_rulesを確認 |
| 1-3 | price-rule.ts | `packages/modules/pricing/src/models/price-rule.ts` | PriceRuleの構造、attribute, value, operatorを確認 |

**読解のコツ**: model.belongsTo/hasManyでリレーションを定義。cascadesでカスケード削除を設定。

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

価格設定のメインメソッドを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | pricing-module.ts | `packages/modules/pricing/src/services/pricing-module.ts` | createPriceSets, updatePriceSets, addPricesメソッドの実装 |

**主要処理フロー**:
1. **557-600行目**: createPriceSets - 価格セットの作成
2. **656-709行目**: updatePriceSets - 価格セットの更新
3. **978-1017行目**: addPrices - 価格の追加
4. **897-976行目**: normalizePrices - 価格データの正規化

#### Step 3: 内部処理を理解する

価格正規化とハッシュ計算の詳細を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | pricing-module.ts | `packages/modules/pricing/src/services/pricing-module.ts` | hashPrice, normalizePricesの実装 |

**主要処理フロー**:
- **1810-1844行目**: hashPrice - 価格の重複判定用ハッシュ計算
- **1850-1922行目**: buildPreNormalizationPriceConstraintsFromData - クエリ条件の構築

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

```
PricingModuleService.createPriceSets()
    │
    ├─ createPriceSets_()
    │      ├─ normalizePrices()
    │      │      └─ 価格データの正規化とルール変換
    │      │
    │      └─ priceSetService_.create()
    │             └─ PriceSetの作成（Priceも含む）
    │
    └─ listPriceSets()
           └─ 作成結果の取得

PricingModuleService.addPrices()
    │
    ├─ listPriceSets()
    │      └─ 対象PriceSetの取得
    │
    ├─ priceService_.list()
    │      └─ 既存価格の取得
    │
    ├─ normalizePrices()
    │      └─ 価格データの正規化
    │
    └─ priceService_.upsertWithReplace()
           └─ 価格のUpsert
```

### データフロー図

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

CreatePriceSetDTO ───▶ normalizePrices()      ───▶ PriceSetDTO
                      hashPrice()
        │             priceSetService_.create()
        │
        ▼
prices[] ─────────▶ priceService_.create()   ───▶ PriceDTO[]

        │
        ▼
rules{} ──────────▶ price_rules[]変換        ───▶ PriceRuleDTO[]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| pricing-module.ts | `packages/modules/pricing/src/services/pricing-module.ts` | ソース | 価格モジュールのメインサービス |
| price-set.ts | `packages/modules/pricing/src/models/price-set.ts` | ソース | PriceSetのデータモデル |
| price.ts | `packages/modules/pricing/src/models/price.ts` | ソース | Priceのデータモデル |
| price-rule.ts | `packages/modules/pricing/src/models/price-rule.ts` | ソース | PriceRuleのデータモデル |
| index.ts | `packages/modules/pricing/src/models/index.ts` | ソース | モデルのエクスポート |
