# 機能設計書 43-配送オプション管理

## 概要

本ドキュメントは、Medusaコマースプラットフォームにおける配送オプション管理機能の設計を記述する。配送オプションは、サービスゾーン内で利用可能な具体的な配送方法（例：宅急便、速達、国際配送など）を定義し、価格タイプやルールを設定するためのエンティティである。

### 本機能の処理概要

**業務上の目的・背景**：ECサイトにおいて、顧客に提供する配送方法を柔軟に設定する必要がある。配送オプションは、価格タイプ（定額/計算）、適用条件（ルール）、配送プロバイダーとの紐付けを管理し、カート・チェックアウト時に適切な配送方法を提示するための基盤を提供する。

**機能の利用シーン**：
- 新規配送方法を追加する場合（例：翌日配送、店頭受取）
- 配送料金を設定・変更する場合
- 配送条件（最低注文額、特定商品除外など）をルールとして設定する場合
- 配送プロバイダー（ヤマト運輸、佐川急便など）を紐付ける場合

**主要な処理内容**：
1. 配送オプションの作成（名前、価格タイプ、サービスゾーン紐付け）
2. 配送オプションルールの設定（適用条件の定義）
3. 配送オプションの更新（ルールの追加・削除を含む）
4. 配送オプションのUpsert
5. コンテキストに応じた配送オプションのフィルタリング取得
6. 配送オプションの削除

**関連システム・外部連携**：
- 配送プロバイダー（FulfillmentProvider）との連携
- 価格モジュールとの連携（価格計算）
- カートモジュールとの連携（配送オプション選択）

**権限による制御**：管理画面からの操作は管理者権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 139 | 配送オプション作成画面 | 主機能 | 配送オプションの作成 |
| 140 | 配送オプション編集画面 | 主機能 | 配送オプションの編集 |
| 141 | 配送オプション料金設定画面 | 主機能 | 配送オプションの料金設定 |
| 146 | 配送オプションタイプ一覧画面 | 主機能 | 配送オプションタイプ一覧の表示 |
| 147 | 配送オプションタイプ作成画面 | 主機能 | 新規配送オプションタイプの作成 |
| 148 | 配送オプションタイプ詳細画面 | 主機能 | 配送オプションタイプ詳細の表示 |
| 149 | 配送オプションタイプ編集画面 | 主機能 | 配送オプションタイプの編集 |

## 機能種別

CRUD操作、ルール評価

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | string | Yes | 配送オプション名 | - |
| price_type | string | No | 価格タイプ（flat/calculated） | デフォルト: flat |
| service_zone_id | string | Yes | サービスゾーンID | 存在チェック |
| shipping_profile_id | string | No | 配送プロファイルID | 存在チェック |
| provider_id | string | No | 配送プロバイダーID | 存在チェック |
| type_id | string | No | 配送オプションタイプID | 存在チェック |
| data | object | No | プロバイダー固有データ | JSON形式 |
| metadata | object | No | カスタムメタデータ | JSON形式 |
| rules | array | No | 適用ルールの配列 | 後述のルール構造 |
| rules[].attribute | string | Yes | ルール対象属性 | - |
| rules[].operator | string | Yes | 比較演算子（eq, ne, gt, lt等） | - |
| rules[].value | any | Yes | 比較値 | - |

### 入力データソース

- 管理画面からのフォーム入力
- Admin API（POST /admin/shipping-options）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | string | 配送オプションID（so_xxx形式） |
| name | string | オプション名 |
| price_type | string | 価格タイプ（flat/calculated） |
| service_zone_id | string | サービスゾーンID |
| service_zone | object | サービスゾーン（リレーション展開時） |
| shipping_profile_id | string | 配送プロファイルID |
| shipping_profile | object | 配送プロファイル（リレーション展開時） |
| provider_id | string | 配送プロバイダーID |
| provider | object | 配送プロバイダー（リレーション展開時） |
| type | object | 配送オプションタイプ |
| rules | array | 適用ルールの配列 |
| data | object | プロバイダー固有データ |
| metadata | object | メタデータ |
| created_at | datetime | 作成日時 |
| updated_at | datetime | 更新日時 |

### 出力先

- API Response（JSON形式）
- DBテーブル：shipping_option

## 処理フロー

### 処理シーケンス

```
1. 入力データの受信
   └─ APIまたはサービスメソッド経由でデータを受信

2. ルールのバリデーション
   └─ validateAndNormalizeRules()でルール形式チェック
   └─ ルールの正規化処理

3. 配送オプションの作成/更新
   └─ shippingOptionService_.create/update()を呼び出し
   └─ ルールもORMにより同時に処理

4. 更新時のルール差分処理
   └─ 削除対象のルールを特定
   └─ shippingOptionRuleService_.delete()で削除

5. コンテキストフィルタリング（listShippingOptionsForContext）
   └─ 住所情報から地理ゾーン制約を構築
   └─ ルール評価でフィルタリング

6. レスポンスの生成とイベント発行
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[入力データ受信]
    B --> C{rules あり?}
    C -->|Yes| D[validateAndNormalizeRules]
    C -->|No| E{IDあり?}
    D --> F{検証OK?}
    F -->|Yes| E
    F -->|No| G[エラー返却]
    E -->|Yes| H[更新処理]
    E -->|No| I[作成処理]
    H --> J[ルール差分検出・削除]
    J --> K[shippingOptionService_.update]
    I --> L[shippingOptionService_.create]
    K --> M[シリアライズ]
    L --> M
    M --> N[イベント発行]
    N --> O[レスポンス返却]
    G --> P[終了]
    O --> P
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-43-01 | カスケード削除 | 配送オプション削除時、配下のルールも削除される | 削除時 |
| BR-43-02 | ルール評価 | コンテキスト（カート内容等）に基づきルールを評価 | listShippingOptionsForContext時 |
| BR-43-03 | 価格タイプ制約 | calculated価格タイプはプロバイダーの計算機能が必要 | 価格計算時 |
| BR-43-04 | 地理ゾーンマッチング | 住所に基づき適用可能なオプションをフィルタ | コンテキスト取得時 |
| BR-43-05 | ルール更新整合性 | 更新時に指定されたルールIDが存在するかチェック | 更新時 |

### 計算ロジック

- **価格計算**：price_type=calculated の場合、配送プロバイダーの`calculatePrice()`を呼び出し
- **ルール評価**：`isContextValid()`関数でルール条件をコンテキストと照合

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| createShippingOptions | shipping_option | INSERT | 新規配送オプション作成 |
| createShippingOptions | shipping_option_rule | INSERT | 同時にルール作成 |
| updateShippingOptions | shipping_option | UPDATE | オプション情報更新 |
| updateShippingOptions | shipping_option_rule | DELETE | 削除対象のルール削除 |
| updateShippingOptions | shipping_option_rule | INSERT/UPDATE | 新規/既存ルール処理 |
| deleteShippingOptions | shipping_option | DELETE | 配送オプション削除 |
| deleteShippingOptions | shipping_option_rule | DELETE | カスケード削除 |

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

#### shipping_option

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | 自動生成（so_xxx形式） | プライマリキー |
| INSERT | name | 入力値 | 検索可能、翻訳対応 |
| INSERT | price_type | 入力値（flat/calculated） | デフォルト: flat |
| INSERT | service_zone_id | サービスゾーンID | 外部キー |
| INSERT | shipping_profile_id | プロファイルID | 外部キー（nullable） |
| INSERT | provider_id | プロバイダーID | 外部キー（nullable） |
| INSERT | shipping_option_type_id | タイプID | 外部キー |

#### shipping_option_rule

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | 自動生成 | プライマリキー |
| INSERT | attribute | ルール対象属性 | - |
| INSERT | operator | 比較演算子 | - |
| INSERT | value | 比較値 | JSON |
| INSERT | shipping_option_id | 親オプションID | 外部キー |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| NOT_FOUND | MedusaError | 指定された配送オプションIDが存在しない | 正しいIDを指定 |
| NOT_FOUND | MedusaError | 指定されたルールIDが存在しない | 正しいルールIDを指定 |
| INVALID_DATA | MedusaError | 非calculated価格タイプで価格計算を要求 | 価格タイプをcalculatedに変更 |

### リトライ仕様

トランザクションレベルでのリトライ対応。

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

- ルールの削除と更新は同一トランザクション
- エラー発生時は全体がロールバック

## パフォーマンス要件

- listShippingOptionsForContext: コンテキスト評価を含むためやや重い処理
- ページネーション対応

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

- 管理者認証必須
- ルールの値には機密情報を含めないこと

## 備考

- 配送オプション名は検索可能（searchable）かつ翻訳対応（translatable）
- IDプレフィックスは「so」

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | shipping-option.ts | `packages/modules/fulfillment/src/models/shipping-option.ts` | エンティティ定義、リレーション |
| 1-2 | shipping-option-rule.ts | `packages/modules/fulfillment/src/models/shipping-option-rule.ts` | ルールエンティティ |
| 1-3 | shipping-option-type.ts | `packages/modules/fulfillment/src/models/shipping-option-type.ts` | タイプエンティティ |

**読解のコツ**: ShippingOptionは複数のリレーション（service_zone, shipping_profile, provider, type, rules, fulfillments）を持つ。特にrulesへのカスケード削除設定（40-42行目）に注目。

#### Step 2: 作成・更新処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | fulfillment-module-service.ts | `packages/modules/fulfillment/src/services/fulfillment-module-service.ts` | 配送オプション関連メソッド |

**主要処理フロー**:
1. **396-426行目**: `createShippingOptions()`パブリックメソッド
2. **428-452行目**: `createShippingOptions_()`内部メソッド - ルールバリデーション、作成
3. **1203-1251行目**: `updateShippingOptions()`パブリックメソッド
4. **1253-1393行目**: `updateShippingOptions_()`内部メソッド - ルール差分処理
5. **1395-1470行目**: `upsertShippingOptions()`/`upsertShippingOptions_()`

#### Step 3: コンテキストフィルタリングを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | fulfillment-module-service.ts | `packages/modules/fulfillment/src/services/fulfillment-module-service.ts` | listShippingOptionsForContext |

**主要処理フロー**:
- **155-173行目**: `listShippingOptions()` - contextまたはaddressがあればlistShippingOptionsForContextに委譲
- **175-212行目**: `listShippingOptionsForContext()` - フィルタ正規化、ルール評価
- **2177-2258行目**: `normalizeListShippingOptionsForContextParams()` - アドレスから地理ゾーン制約構築
- **2260-2345行目**: `buildGeoZoneConstraintsFromAddress()` - 地理ゾーンマッチング条件生成

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

```
FulfillmentModuleService
    │
    ├─ createShippingOptions() [public]
    │      └─ createShippingOptions_() [protected]
    │             ├─ validateAndNormalizeRules() - ルール検証
    │             └─ shippingOptionService_.create()
    │
    ├─ updateShippingOptions() [public]
    │      └─ updateShippingOptions_() [protected]
    │             ├─ shippingOptionService_.list() - 既存データ取得
    │             ├─ validateMissingShippingOptionRules() - ルールID検証
    │             ├─ validateAndNormalizeRules()
    │             ├─ shippingOptionRuleService_.delete() - 不要ルール削除
    │             └─ shippingOptionService_.update()
    │
    └─ listShippingOptionsForContext() [public]
           ├─ normalizeListShippingOptionsForContextParams() [static]
           │      └─ buildGeoZoneConstraintsFromAddress() [static]
           ├─ shippingOptionService_.list()
           └─ isContextValid() - ルール評価
```

### データフロー図

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

CreateShippingOptionDTO    FulfillmentModuleService           ShippingOptionDTO
         │                          │                                │
         ▼                          ▼                                ▼
    ┌──────────┐           ┌─────────────────┐              ┌──────────────┐
    │ API      │ ────────▶ │ createShipping  │ ───────────▶ │ JSON Response│
    │ Request  │           │ Options         │              │ (serialized) │
    └──────────┘           └─────────────────┘              └──────────────┘
                                   │
                    ┌──────────────┼──────────────┐
                    ▼              ▼              ▼
             ┌───────────┐  ┌───────────┐  ┌───────────┐
             │ shipping_ │  │ shipping_ │  │ shipping_ │
             │ option    │  │ option_   │  │ profile   │
             │           │  │ rule      │  │           │
             └───────────┘  └───────────┘  └───────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| fulfillment-module-service.ts | `packages/modules/fulfillment/src/services/fulfillment-module-service.ts` | ソース | モジュールサービス本体 |
| shipping-option.ts | `packages/modules/fulfillment/src/models/shipping-option.ts` | ソース | 配送オプションエンティティ |
| shipping-option-rule.ts | `packages/modules/fulfillment/src/models/shipping-option-rule.ts` | ソース | ルールエンティティ |
| shipping-option-type.ts | `packages/modules/fulfillment/src/models/shipping-option-type.ts` | ソース | タイプエンティティ |
| shipping-profile.ts | `packages/modules/fulfillment/src/models/shipping-profile.ts` | ソース | 配送プロファイル |
| service-zone.ts | `packages/modules/fulfillment/src/models/service-zone.ts` | ソース | 親エンティティ |
