# 機能設計書 53-価格リスト管理

## 概要

本ドキュメントは、Medusa ECプラットフォームにおける価格リスト管理機能の設計を記述する。特別価格リスト（会員価格、セール価格等）の管理機能の詳細仕様を定義する。

### 本機能の処理概要

価格リスト管理機能は、特定の条件下で適用される価格セット（PriceList）を管理する機能である。セール価格、会員限定価格、期間限定価格などを設定し、条件に応じて通常価格を上書きまたは割引適用できる。価格リストにはタイプ（SALE/OVERRIDE）、有効期間（starts_at/ends_at）、適用ルール（PriceListRule）を設定可能である。

**業務上の目的・背景**：ECサイトでは、セール、会員特典、期間限定キャンペーンなど、様々な価格戦略を実現する必要がある。価格リスト機能により、通常価格を変更せずに、条件付きの特別価格を柔軟に設定できる。

**機能の利用シーン**：
1. 季節セール時のセール価格設定
2. VIP会員向けの特別価格設定
3. 期間限定キャンペーン価格の設定
4. B2B顧客向けの卸売価格設定
5. 地域限定価格の設定

**主要な処理内容**：
1. PriceListの作成・更新・削除
2. PriceListRuleの作成・更新・削除
3. 価格リストへの価格追加（addPriceListPrices）
4. 価格リストの価格更新（updatePriceListPrices）
5. 価格リストルールの設定（setPriceListRules）
6. 価格リストルールの削除（removePriceListRules）

**関連システム・外部連携**：価格リストは価格計算時に評価され、条件に一致する場合は通常価格に代わって適用される。

**権限による制御**：価格リスト管理は管理者のみが実行可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 71 | 価格表一覧画面 | 主機能 | 価格表一覧の取得・表示 |
| 72 | 価格表作成画面 | 主機能 | 新規価格表の作成 |
| 73 | 価格表詳細画面 | 主機能 | 価格表詳細の表示 |
| 74 | 価格表編集画面 | 主機能 | 価格表情報の編集 |
| 75 | 価格表設定画面 | 主機能 | 価格表設定の編集 |

## 機能種別

CRUD操作

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | string | No | PriceListのID（更新時） | plist_プレフィックス |
| title | string | Yes | 価格リストのタイトル | 1文字以上 |
| description | string | No | 価格リストの説明 | - |
| status | PriceListStatus | No | ステータス（DRAFT/ACTIVE） | enum値 |
| type | PriceListType | No | タイプ（SALE/OVERRIDE） | enum値 |
| starts_at | Date | No | 開始日時 | ends_at以前 |
| ends_at | Date | No | 終了日時 | starts_at以降 |
| rules | Record<string, string> | No | 適用ルール | - |
| prices | CreatePricesDTO[] | No | 価格の配列 | - |

### 入力データソース

- リクエストパラメータ（API経由）
- 管理画面からの入力

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | string | PriceListのID |
| title | string | タイトル |
| description | string | 説明 |
| status | PriceListStatus | ステータス |
| type | PriceListType | タイプ |
| starts_at | Date | 開始日時 |
| ends_at | Date | 終了日時 |
| rules_count | number | ルール数 |
| prices | PriceDTO[] | 価格の配列 |
| price_list_rules | PriceListRuleDTO[] | ルールの配列 |

### 出力先

- API レスポンス（JSON形式）

## 処理フロー

### 処理シーケンス

```
1. 入力データの正規化
   └─ normalizePriceListDate()で日付を正規化
2. バリデーション
   └─ validatePriceListDates()で日付の整合性を検証
3. ルールの処理
   └─ rulesをprice_list_rulesに変換
4. PriceListの作成/更新
   └─ priceListService_.createまたはupsertWithReplace
5. Priceの作成/更新
   └─ normalizePricesで正規化後に作成
6. 結果の返却
   └─ シリアライズしてDTO形式で返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[日付の正規化]
    B --> C[日付の検証]
    C --> D{日付が有効?}
    D -->|No| E[エラー返却]
    D -->|Yes| F[ルールの変換]
    F --> G{PriceListが存在?}
    G -->|No| H[PriceList作成]
    G -->|Yes| I[PriceList更新]
    H --> J[Price作成]
    I --> K[Price更新]
    J --> L[結果を返却]
    K --> L
    E --> M[終了]
    L --> M
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-53-01 | 日付検証 | starts_atはends_at以前でなければならない | 日付が両方指定された場合 |
| BR-53-02 | タイプによる価格適用 | OVERRIDEは通常価格を上書き、SALEは最低価格を適用 | 価格計算時 |
| BR-53-03 | ステータス制御 | ACTIVEのみ価格計算時に評価される | 価格計算時 |
| BR-53-04 | 期間制御 | starts_at〜ends_atの期間内のみ適用 | 価格計算時 |
| BR-53-05 | ルール評価 | 全てのルール条件が一致した場合のみ適用 | 価格計算時 |

### 計算ロジック

```typescript
// 価格リストタイプによる適用ロジック
if (priceList.type === PriceListType.OVERRIDE) {
  // 通常価格を完全に上書き
  calculatedPrice = priceListPrice
  originalPrice = priceListPrice
} else if (priceList.type === PriceListType.SALE) {
  // 通常価格とセール価格の低い方を適用
  calculatedPrice = Math.min(priceListPrice, defaultPrice)
  originalPrice = defaultPrice
}
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 価格リスト作成 | price_list | INSERT | 価格リストの新規作成 |
| ルール作成 | price_list_rule | INSERT | 価格リストルールの作成 |
| 価格作成 | price | INSERT | 価格リストに紐づく価格の作成 |
| 価格リスト更新 | price_list | UPDATE | 価格リストの更新 |
| 価格リスト削除 | price_list | DELETE | カスケードで関連データも削除 |

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

#### price_list

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id, title, description, status, type, starts_at, ends_at, rules_count | リクエストパラメータから | - |
| UPDATE | title, description, status, type, starts_at, ends_at, rules_count | WHERE id = :id | - |

#### price_list_rule

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id, attribute, value, price_list_id | rulesから変換 | - |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| INVALID_DATA | MedusaError | starts_atがends_atより後 | 正しい日付を指定 |
| NOT_FOUND | MedusaError | PriceListが存在しない | 正しいIDを指定 |

### リトライ仕様

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

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

PriceList、PriceListRule、Priceの操作は同一トランザクション内で実行される。

## パフォーマンス要件

- 単一PriceList作成: 200ms以内
- 価格リスト一覧取得: 500ms以内
- インデックス: id, status, starts_at, ends_atにインデックス設定

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

- 認証: 価格リスト操作はAdmin API認証必須
- 認可: 管理者ロールが必要
- 監査ログ: 価格リスト変更は監査ログに記録

## 備考

- DRAFTステータスの価格リストは価格計算に影響しない
- 価格リスト削除時は関連するPriceListRuleとPriceもカスケード削除される

---

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

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

### 推奨読解順序

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | price-list.ts | `packages/modules/pricing/src/models/price-list.ts` | PriceListの構造、status, type, starts_at, ends_atを確認 |
| 1-2 | price-list-rule.ts | `packages/modules/pricing/src/models/price-list-rule.ts` | PriceListRuleの構造、attribute, valueを確認 |

**読解のコツ**: PriceListStatusとPriceListTypeはenumとして定義。cascadesでprice_list_rules, pricesのカスケード削除を設定。

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

価格リストのメインメソッドを確認する。

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

**主要処理フロー**:
1. **1019-1035行目**: createPriceLists - 価格リストの作成
2. **1037-1053行目**: updatePriceLists - 価格リストの更新
3. **1085-1100行目**: addPriceListPrices - 価格の追加
4. **1104-1117行目**: setPriceListRules - ルールの設定
5. **1119-1130行目**: removePriceListRules - ルールの削除

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

日付の正規化と検証の詳細を確認する。

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

**主要処理フロー**:
- **1747-1767行目**: normalizePriceListDate - 日付のISO文字列変換
- validatePriceListDates - 日付の検証（@utilsからインポート）

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

```
PricingModuleService.createPriceLists()
    │
    ├─ createPriceLists_()
    │      ├─ normalizePriceListDate()
    │      │      └─ 日付の正規化
    │      │
    │      ├─ validatePriceListDates()
    │      │      └─ 日付の検証
    │      │
    │      └─ priceListService_.create()
    │             └─ PriceListの作成
    │
    └─ baseRepository_.serialize()
           └─ DTO形式にシリアライズ

PricingModuleService.setPriceListRules()
    │
    ├─ setPriceListRules_()
    │      ├─ priceListService_.list()
    │      │      └─ 既存PriceListの取得
    │      │
    │      └─ priceListService_.upsertWithReplace()
    │             └─ ルールの更新
    │
    └─ baseRepository_.serialize()
           └─ DTO形式にシリアライズ
```

### データフロー図

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

CreatePriceListDTO ───▶ normalizePriceListDate() ───▶ PriceListDTO
                       validatePriceListDates()
        │              priceListService_.create()
        │
        ▼
rules{} ──────────▶ price_list_rules[]変換   ───▶ PriceListRuleDTO[]

        │
        ▼
prices[] ─────────▶ normalizePrices()        ───▶ PriceDTO[]
                    priceService_.create()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| pricing-module.ts | `packages/modules/pricing/src/services/pricing-module.ts` | ソース | 価格モジュールのメインサービス |
| price-list.ts | `packages/modules/pricing/src/models/price-list.ts` | ソース | PriceListのデータモデル |
| price-list-rule.ts | `packages/modules/pricing/src/models/price-list-rule.ts` | ソース | PriceListRuleのデータモデル |
| validate-price-list-dates.ts | `packages/modules/pricing/src/utils/validate-price-list-dates.ts` | ソース | 日付検証ユーティリティ |
