# 機能設計書 47-地理ゾーン管理

## 概要

本ドキュメントは、Medusaコマースプラットフォームにおける地理ゾーン管理機能の設計を記述する。地理ゾーン（GeoZone）は、サービスゾーン内で配送可能な地理的範囲を定義するエンティティであり、国、州/県、都市、郵便番号レベルでの配送エリア設定を可能にする。

### 本機能の処理概要

**業務上の目的・背景**：ECサイトにおいて、配送可能な地域を細かく制御する必要がある。地理ゾーンは、国単位の大まかな設定から、郵便番号パターンによる詳細な設定まで、階層的な地域定義を可能にし、顧客の住所に基づいて適切な配送オプションを提示するための基盤を提供する。

**機能の利用シーン**：
- 新規配送エリアの追加（例：日本全国、関東地方のみ）
- 特定都市への配送制限設定
- 郵便番号パターンによる配送可否設定
- 国際配送の対象国設定

**主要な処理内容**：
1. 地理ゾーンの作成（タイプ別の必須項目設定）
2. 地理ゾーンの更新
3. 地理ゾーンの削除
4. 地理ゾーンの取得・一覧表示
5. 住所に基づく地理ゾーンマッチング

**関連システム・外部連携**：
- サービスゾーンとの親子関係
- 配送オプション取得時の地理ゾーンマッチング

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 136 | サービスゾーン作成画面 | 補助機能 | 地理的範囲の設定 |
| 138 | エリア管理画面 | 主機能 | サービスゾーンのエリア管理 |

## 機能種別

CRUD操作、バリデーション

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| type | string | Yes | ゾーンタイプ（country/province/city/zip） | Enum値 |
| country_code | string | Yes | 国コード（ISO 3166-1 alpha-2） | 必須 |
| province_code | string | Conditional | 州/県コード | type=province/city/zip時に必須 |
| city | string | Conditional | 都市名 | type=city/zip時に必須 |
| postal_expression | object | Conditional | 郵便番号パターン | type=zip時に必須 |
| service_zone_id | string | Yes | 親サービスゾーンID | 存在チェック |
| metadata | object | No | カスタムメタデータ | JSON形式 |

### タイプ別必須項目

| タイプ | 必須項目 |
|-------|---------|
| country | country_code |
| province | country_code, province_code |
| city | country_code, province_code, city |
| zip | country_code, province_code, city, postal_expression |

### 入力データソース

- 管理画面からのフォーム入力
- Admin API経由
- サービスゾーン作成時の同時作成

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | string | 地理ゾーンID（fgz_xxx形式） |
| type | string | ゾーンタイプ |
| country_code | string | 国コード |
| province_code | string | 州/県コード |
| city | string | 都市名 |
| postal_expression | object | 郵便番号パターン |
| service_zone_id | string | 親サービスゾーンID |
| metadata | object | メタデータ |
| created_at | datetime | 作成日時 |
| updated_at | datetime | 更新日時 |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. 入力データの受信
   └─ タイプと地理情報を含む

2. タイプ別バリデーション
   └─ validateGeoZones()で必須項目チェック
   └─ 無効なタイプの場合はエラー
   └─ 必須項目欠落の場合はエラー

3. 地理ゾーンの作成/更新
   └─ geoZoneService_.create/update()

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

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[入力データ受信]
    B --> C[validateGeoZones]
    C --> D{タイプ有効?}
    D -->|No| E[INVALID_DATA エラー]
    D -->|Yes| F{必須項目あり?}
    F -->|No| G[INVALID_DATA エラー]
    F -->|Yes| H{IDあり?}
    H -->|Yes| I[更新処理]
    H -->|No| J[作成処理]
    I --> K[geoZoneService_.update]
    J --> L[geoZoneService_.create]
    K --> M[シリアライズ]
    L --> M
    M --> N[イベント発行]
    N --> O[レスポンス返却]
    E --> P[終了]
    G --> P
    O --> P
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-47-01 | タイプ別必須項目 | タイプに応じた必須項目が必要 | 作成・更新時 |
| BR-47-02 | 有効タイプ制約 | タイプはcountry/province/city/zipのいずれか | 作成・更新時 |
| BR-47-03 | 階層的マッチング | 住所マッチング時はzip→city→province→countryの順で評価 | 配送オプション取得時 |
| BR-47-04 | 親ゾーン必須 | 地理ゾーンは必ずサービスゾーンに属する | 作成時 |

### 計算ロジック

- **住所マッチング**：`buildGeoZoneConstraintsFromAddress()`で住所から地理ゾーン制約を構築
- 最も詳細なマッチ（zip）から最も広いマッチ（country）まで、OR条件で検索

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| createGeoZones | geo_zone | INSERT | 新規地理ゾーン作成 |
| updateGeoZones | geo_zone | UPDATE | 地理ゾーン更新 |
| deleteGeoZones | geo_zone | DELETE | 地理ゾーン削除 |

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

#### geo_zone

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | 自動生成（fgz_xxx形式） | プライマリキー |
| INSERT | type | 入力値（country/province/city/zip） | Enum、デフォルト: country |
| INSERT | country_code | 入力値 | インデックスあり |
| INSERT | province_code | 入力値（nullable） | インデックスあり |
| INSERT | city | 入力値（nullable） | インデックスあり |
| INSERT | postal_expression | 入力値（nullable） | JSON |
| INSERT | service_zone_id | 親ゾーンID | 外部キー |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| INVALID_DATA | MedusaError | 無効な地理ゾーンタイプ | country/province/city/zipのいずれかを指定 |
| INVALID_DATA | MedusaError | 必須プロパティが不足 | タイプに応じた必須項目を入力 |
| NOT_FOUND | MedusaError | 更新時に地理ゾーンIDが存在しない | 正しいIDを指定 |

### リトライ仕様

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

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

- 標準的なCRUDトランザクション

## パフォーマンス要件

- country_code, province_code, cityにインデックス設定済み
- 配送オプション取得時の地理ゾーンマッチングは効率的に実行

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

- 管理者認証必須

## 備考

- IDプレフィックスは「fgz」
- GeoZoneTypeはEnumで定義（@medusajs/framework/utils）
- postal_expressionは正規表現パターンを格納可能

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | geo-zone.ts | `packages/modules/fulfillment/src/models/geo-zone.ts` | エンティティ定義 |
| 1-2 | service-zone.ts | `packages/modules/fulfillment/src/models/service-zone.ts` | 親エンティティとの関係 |

**読解のコツ**: GeoZoneはtypeによって異なる必須項目を持つ。model.enum(GeoZoneType)でタイプを定義し、各地理情報はnullableで定義されているが、バリデーション時にタイプに応じた必須チェックが行われる。

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

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

**主要処理フロー**:
- **2145-2175行目**: `validateGeoZones()` - タイプ別必須項目チェック
  - **2151-2156行目**: タイプ別必須項目マッピング定義
  - **2158-2164行目**: 無効タイプのエラー処理
  - **2166-2173行目**: 必須項目欠落のエラー処理

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

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

**主要処理フロー**:
- **505-537行目**: `createGeoZones()` - 地理ゾーン作成
- **1712-1750行目**: `updateGeoZones()` - 地理ゾーン更新

#### Step 4: 住所マッチングを理解する

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

**主要処理フロー**:
- **2296-2345行目**: `buildGeoZoneConstraintsFromAddress()` - 住所から地理ゾーン制約構築
  - **2302-2319行目**: タイプ別必須プロパティ階層定義
  - **2327-2343行目**: 制約条件の構築（最も詳細なものから広いものへ）

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

```
FulfillmentModuleService
    │
    ├─ createGeoZones() [public]
    │      ├─ validateGeoZones() [static]
    │      └─ geoZoneService_.create()
    │
    ├─ updateGeoZones() [public]
    │      ├─ validateGeoZones() [static]
    │      └─ geoZoneService_.update()
    │
    └─ listShippingOptionsForContext() [public]
           └─ normalizeListShippingOptionsForContextParams() [static]
                  └─ buildGeoZoneConstraintsFromAddress() [static]
                         └─ 階層的制約条件を構築
```

### データフロー図

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

CreateGeoZoneDTO           FulfillmentModuleService            GeoZoneDTO
         │                          │                                │
         ▼                          ▼                                ▼
    ┌──────────┐           ┌─────────────────┐              ┌──────────────┐
    │ API      │ ────────▶ │ validateGeoZones│ ───────────▶ │ JSON Response│
    │ Request  │           │ + create/update │              │ (serialized) │
    └──────────┘           └─────────────────┘              └──────────────┘
                                   │
                                   ▼
                           ┌───────────────┐
                           │ geo_zone      │
                           │ table         │
                           └───────────────┘

[住所マッチング]

顧客住所                  buildGeoZoneConstraints            DB制約条件
    │                           │                                │
    ▼                           ▼                                ▼
┌────────────┐           ┌─────────────────┐              ┌──────────────┐
│ address:   │ ────────▶ │ 階層的制約構築  │ ───────────▶ │ $or: [       │
│ country_   │           │                 │              │   {type:zip},│
│ code, city │           │                 │              │   {type:city}│
│ etc.       │           │                 │              │   ...        │
└────────────┘           └─────────────────┘              └──────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| fulfillment-module-service.ts | `packages/modules/fulfillment/src/services/fulfillment-module-service.ts` | ソース | モジュールサービス本体 |
| geo-zone.ts | `packages/modules/fulfillment/src/models/geo-zone.ts` | ソース | 地理ゾーンエンティティ |
| service-zone.ts | `packages/modules/fulfillment/src/models/service-zone.ts` | ソース | 親エンティティ |
