# 機能設計書 41-フルフィルメントセット管理

## 概要

本ドキュメントは、Medusaコマースプラットフォームにおけるフルフィルメントセット管理機能の設計を記述する。フルフィルメントセットは配送オプションをグループ化し、サービスゾーンを通じて配送可能な地域を定義するためのコンテナエンティティである。

### 本機能の処理概要

**業務上の目的・背景**：ECサイトにおいて、配送方法は地域やビジネスモデルによって異なる。例えば、ピックアップ用、配送用、国際配送用など、異なる目的の配送オプションセットを管理する必要がある。フルフィルメントセットは、これらの配送オプションを論理的にグループ化し、特定のロケーションや販売チャネルに紐付けるための基盤を提供する。

**機能の利用シーン**：
- 新規店舗開設時に店舗固有の配送オプションセットを構成する場合
- 配送方法（店頭受取、宅配便、速達など）を種別ごとに分類管理する場合
- 地域限定の配送サービスを設定する場合
- マルチチャネル販売で異なる配送ポリシーを適用する場合

**主要な処理内容**：
1. フルフィルメントセットの作成（名前、タイプ、メタデータの登録）
2. フルフィルメントセット配下のサービスゾーンの同時作成
3. フルフィルメントセットの更新（サービスゾーンの追加・削除を含む）
4. フルフィルメントセットの取得・一覧表示
5. フルフィルメントセットの削除（ソフトデリート対応）

**関連システム・外部連携**：本機能はモジュール内で完結し、直接の外部システム連携はない。ただし、Link機能を通じてStock Locationモジュールと連携し、ロケーションごとの配送設定を実現する。

**権限による制御**：管理画面からの操作は管理者権限が必要。APIレベルでの権限制御はルート定義にて実装。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 132 | ロケーション詳細画面 | 参照画面 | サービスゾーンの表示 |

## 機能種別

CRUD操作

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | string | Yes | フルフィルメントセットの名前 | ユニーク制約あり |
| type | string | Yes | セットの種別（pickup, shipping等） | - |
| metadata | object | No | カスタムメタデータ | JSON形式 |
| service_zones | array | No | 同時に作成するサービスゾーン | 後述のサービスゾーン構造 |
| service_zones[].name | string | Yes | サービスゾーン名 | ユニーク制約あり |
| service_zones[].geo_zones | array | No | 地理ゾーンの配列 | 後述の地理ゾーン構造 |

### 入力データソース

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

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | string | フルフィルメントセットID（fuset_xxx形式） |
| name | string | セット名 |
| type | string | セットタイプ |
| service_zones | array | 関連サービスゾーンの配列 |
| metadata | object | メタデータ |
| created_at | datetime | 作成日時 |
| updated_at | datetime | 更新日時 |
| deleted_at | datetime | 削除日時（ソフトデリート用） |

### 出力先

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

## 処理フロー

### 処理シーケンス

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

2. バリデーション処理
   └─ サービスゾーン内の地理ゾーンの整合性チェック
   └─ 名前のユニーク性確認（DBインデックスによる）

3. フルフィルメントセットの作成
   └─ fulfillmentSetService_.create()を呼び出し
   └─ サービスゾーンも同時に作成される（カスケード）

4. レスポンスの生成
   └─ 作成されたエンティティをシリアライズ
   └─ DTOに変換して返却

5. イベント発行
   └─ @EmitEvents()デコレータによりドメインイベント発行
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[入力データ受信]
    B --> C{service_zones あり?}
    C -->|Yes| D[geo_zones検証]
    C -->|No| E[フルフィルメントセット作成]
    D --> F{検証OK?}
    F -->|Yes| E
    F -->|No| G[エラー返却]
    E --> H[DBに永続化]
    H --> I[シリアライズ]
    I --> J[イベント発行]
    J --> K[レスポンス返却]
    G --> L[終了]
    K --> L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-41-01 | 名前ユニーク制約 | フルフィルメントセット名は一意でなければならない | 作成・更新時 |
| BR-41-02 | カスケード削除 | フルフィルメントセット削除時、配下のサービスゾーンも削除される | 削除時 |
| BR-41-03 | ソフトデリート | 物理削除ではなくdeleted_atを設定 | 削除時 |
| BR-41-04 | サービスゾーン更新整合性 | 更新時に既存のサービスゾーンIDが存在するかチェック | 更新時 |

### 計算ロジック

特になし。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| createFulfillmentSets | fulfillment_set | INSERT | 新規フルフィルメントセット作成 |
| createFulfillmentSets | service_zone | INSERT | 同時にサービスゾーン作成 |
| createFulfillmentSets | geo_zone | INSERT | 同時に地理ゾーン作成 |
| updateFulfillmentSets | fulfillment_set | UPDATE | セット情報更新 |
| updateFulfillmentSets | service_zone | DELETE | 削除対象のサービスゾーン削除 |
| updateFulfillmentSets | geo_zone | DELETE | 削除対象の地理ゾーン削除 |
| deleteFulfillmentSets | fulfillment_set | UPDATE | deleted_atを設定（ソフトデリート） |

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

#### fulfillment_set

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | 自動生成（fuset_xxx形式） | プライマリキー |
| INSERT | name | 入力値 | ユニークインデックス |
| INSERT | type | 入力値 | - |
| INSERT | metadata | 入力値（nullable） | JSON |
| UPDATE | deleted_at | 現在時刻 | ソフトデリート時 |

#### service_zone

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | 自動生成（serzo_xxx形式） | プライマリキー |
| INSERT | name | 入力値 | ユニークインデックス |
| INSERT | fulfillment_set_id | 親セットのID | 外部キー |
| DELETE | id | 更新時に削除対象となったゾーンID | カスケード削除 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| NOT_FOUND | MedusaError | 更新時に指定されたフルフィルメントセットIDが存在しない | 正しいIDを指定 |
| NOT_FOUND | MedusaError | 更新時に指定されたサービスゾーンIDが存在しない | 正しいサービスゾーンIDを指定 |
| INVALID_DATA | MedusaError | 地理ゾーンの必須プロパティが不足 | 地理ゾーンタイプに応じた必須項目を入力 |
| UNIQUE_VIOLATION | DBError | 名前が重複 | 別の名前を使用 |

### リトライ仕様

DBの一時的なエラーに対しては、トランザクションレベルでのリトライを実施。アプリケーションレベルでの明示的なリトライ処理は実装されていない。

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

- `@InjectTransactionManager()`デコレータにより、内部メソッドはトランザクション内で実行される
- フルフィルメントセット作成時、サービスゾーンと地理ゾーンは同一トランザクションで作成
- 更新時の削除処理と更新処理も同一トランザクション内で実行
- エラー発生時は全体がロールバック

## パフォーマンス要件

- 通常のCRUD操作は100ms以内にレスポンスを返却
- リスト取得時はページネーション対応（FindConfigによるtake/skip）

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

- 管理者認証必須（Admin APIエンドポイント）
- メタデータには機密情報を含めないこと
- SQLインジェクション対策はORMにより実施

## 備考

- フルフィルメントセットはStock Locationモジュールとリンクモジュールを通じて連携される
- IDプレフィックスは「fuset」

---

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

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

### 推奨読解順序

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

まず、フルフィルメントセットのエンティティ定義を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | fulfillment-set.ts | `packages/modules/fulfillment/src/models/fulfillment-set.ts` | エンティティの定義、プロパティ、リレーション |
| 1-2 | service-zone.ts | `packages/modules/fulfillment/src/models/service-zone.ts` | サービスゾーンとの関係性 |
| 1-3 | geo-zone.ts | `packages/modules/fulfillment/src/models/geo-zone.ts` | 地理ゾーンの構造 |

**読解のコツ**: Medusaのモデル定義は`@medusajs/framework/utils`の`model`ヘルパーを使用。`model.define()`でエンティティ名とスキーマを定義し、`.indexes()`や`.cascades()`でインデックスとカスケード削除を設定。

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

処理の起点となるサービスメソッドを特定。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | fulfillment-module-service.ts | `packages/modules/fulfillment/src/services/fulfillment-module-service.ts` | モジュールサービスのエントリーポイント |

**主要処理フロー**:
1. **269-302行目**: `createFulfillmentSets()`パブリックメソッド - 単一/複数作成のオーバーロード対応、EmitEventsデコレータ適用
2. **304-333行目**: `createFulfillmentSets_()`内部メソッド - 実際の作成ロジック、地理ゾーンバリデーション呼び出し
3. **717-920行目**: `updateFulfillmentSets()`/`updateFulfillmentSets_()`- 更新処理、サービスゾーン差分検出と削除

#### Step 3: バリデーション処理を理解する

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

**主要処理フロー**:
- **2145-2175行目**: `validateGeoZones()`静的メソッド - 地理ゾーンタイプに応じた必須プロパティチェック

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

```
FulfillmentModuleService
    │
    ├─ createFulfillmentSets() [public, @InjectManager, @EmitEvents]
    │      └─ createFulfillmentSets_() [protected, @InjectTransactionManager]
    │             ├─ validateGeoZones() [static] - geo_zonesの検証
    │             └─ fulfillmentSetService_.create() - DB永続化
    │
    ├─ updateFulfillmentSets() [public, @InjectManager, @EmitEvents]
    │      └─ updateFulfillmentSets_() [protected, @InjectTransactionManager]
    │             ├─ fulfillmentSetService_.list() - 既存データ取得
    │             ├─ getSetDifference() - 差分検出
    │             ├─ geoZoneService_.delete() - 不要地理ゾーン削除
    │             ├─ serviceZoneService_.delete() - 不要サービスゾーン削除
    │             └─ fulfillmentSetService_.update() - 更新実行
    │
    └─ deleteFulfillmentSets() [inherited from MedusaService]
           └─ softDelete処理
```

### データフロー図

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

CreateFulfillmentSetDTO   FulfillmentModuleService        FulfillmentSetDTO
         │                         │                              │
         ▼                         ▼                              ▼
    ┌─────────┐           ┌───────────────┐              ┌──────────────┐
    │ API     │ ───────▶  │ create/update │ ───────────▶ │ JSON Response│
    │ Request │           │ FulfillmentSets│              │ (serialized) │
    └─────────┘           └───────────────┘              └──────────────┘
                                  │
                                  ▼
                          ┌───────────────┐
                          │ fulfillment_  │
                          │ set table     │
                          └───────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| fulfillment-module-service.ts | `packages/modules/fulfillment/src/services/fulfillment-module-service.ts` | ソース | モジュールサービス本体 |
| fulfillment-set.ts | `packages/modules/fulfillment/src/models/fulfillment-set.ts` | ソース | エンティティ定義 |
| service-zone.ts | `packages/modules/fulfillment/src/models/service-zone.ts` | ソース | サービスゾーンエンティティ |
| geo-zone.ts | `packages/modules/fulfillment/src/models/geo-zone.ts` | ソース | 地理ゾーンエンティティ |
| joiner-config.ts | `packages/modules/fulfillment/src/joiner-config.ts` | 設定 | モジュール間連携設定 |
| index.ts | `packages/modules/fulfillment/src/models/index.ts` | ソース | モデルのエクスポート |
