# 機能設計書 56-税地域管理

## 概要

本ドキュメントは、Medusa ECプラットフォームにおける税地域管理機能の設計を記述する。地域ごとの税設定管理機能の詳細仕様を定義する。

### 本機能の処理概要

税地域管理機能は、国や州・都道府県レベルの税地域（TaxRegion）を作成・管理する機能である。税地域は階層構造を持ち、国レベルの親地域と州・都道府県レベルの子地域を設定できる。各税地域には税プロバイダー（TaxProvider）を紐づけることで、外部の税計算サービスと連携することも可能である。

**業務上の目的・背景**：国や地域によって税制が異なるため、国レベル、州・都道府県レベルで税設定を階層的に管理する必要がある。例えば、アメリカでは州ごとに消費税率が異なり、一部の州では地方税も加算される。この複雑な税制を適切に管理するための基盤が求められる。

**機能の利用シーン**：
1. 新しい国への販売展開時の税地域設定
2. 州・都道府県ごとの税設定追加
3. 税計算プロバイダーの割り当て
4. 税地域の階層構造の管理

**主要な処理内容**：
1. TaxRegionの作成・更新・削除
2. 親子関係（国-州/都道府県）の設定
3. 税プロバイダーの紐づけ
4. デフォルト税率の同時作成

**関連システム・外部連携**：税地域は税率管理、税計算機能と連携する。外部の税計算プロバイダー（TaxJar等）とも連携可能。

**権限による制御**：税地域管理は管理者のみが実行可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 160 | 税リージョン一覧画面 | 主機能 | 税リージョン一覧の取得・表示 |
| 161 | 税リージョン作成画面 | 主機能 | 新規税リージョンの作成 |
| 162 | 税リージョン詳細画面 | 主機能 | 税リージョン詳細の表示 |
| 163 | 税リージョン編集画面 | 主機能 | 税リージョン情報の編集 |
| 164 | 都道府県作成画面 | 主機能 | 都道府県の税設定作成 |
| 165 | 都道府県詳細画面 | 主機能 | 都道府県の税設定表示 |

## 機能種別

CRUD操作

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | string | No | TaxRegionのID（更新時） | txreg_プレフィックス |
| country_code | string | Yes | 国コード | ISO 3166-1 alpha-2 |
| province_code | string | No | 州/都道府県コード | 親地域との整合性チェック |
| parent_id | string | No | 親税地域ID | 存在チェック、国コード一致 |
| provider_id | string | No | 税プロバイダーID | 親地域がある場合は設定不可 |
| default_tax_rate | object | No | デフォルト税率 | - |
| created_by | string | No | 作成者ID | - |

### 入力データソース

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

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | string | TaxRegionのID |
| country_code | string | 国コード |
| province_code | string | 州/都道府県コード |
| parent_id | string | 親税地域ID |
| provider_id | string | 税プロバイダーID |
| tax_rates | TaxRateDTO[] | 税率の配列 |
| children | TaxRegionDTO[] | 子税地域の配列 |
| metadata | object | メタデータ |
| created_by | string | 作成者ID |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. 入力データの正規化
   └─ 国コード・州コードを小文字に正規化
2. 親子関係の検証
   └─ verifyProvinceToCountryMatch()で整合性チェック
3. TaxRegionの作成
   └─ taxRegionService_.create
4. デフォルト税率の作成（オプション）
   └─ default_tax_rateが指定されている場合
5. 結果の返却
   └─ シリアライズしてDTO形式で返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[国コード・州コードの正規化]
    B --> C{parent_idが指定?}
    C -->|Yes| D[親地域との整合性検証]
    C -->|No| E[国レベル地域として作成]
    D --> F{国コードが一致?}
    F -->|No| G[エラー返却]
    F -->|Yes| H[州/都道府県地域として作成]
    E --> I{default_tax_rateが指定?}
    H --> I
    I -->|Yes| J[デフォルト税率作成]
    I -->|No| K[結果を返却]
    J --> K
    G --> L[終了]
    K --> L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-56-01 | プロバイダー制約 | 親地域がある場合はprovider_idを設定不可 | 子地域作成時 |
| BR-56-02 | 国コード一致 | 子地域の国コードは親地域と一致が必要 | 子地域作成時 |
| BR-56-03 | 州コード必須 | 親地域がある場合はprovince_codeが必須 | 子地域作成時 |
| BR-56-04 | ユニーク制約 | 同一国コード・州コードの組み合わせは一意 | 地域作成時 |
| BR-56-05 | コード正規化 | 国コード・州コードは小文字に正規化 | 地域作成・取得時 |

### 計算ロジック

```typescript
// コードの正規化
normalizedCode = code.toLowerCase()

// 親子関係の検証
if (parent_id) {
  const parent = await getParentRegion(parent_id)
  if (parent.country_code !== region.country_code) {
    throw new MedusaError(
      MedusaError.Types.INVALID_DATA,
      `Province region must belong to a parent region with the same country code`
    )
  }
}
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 税地域作成 | tax_region | INSERT | 税地域の新規作成 |
| 税率作成 | tax_rate | INSERT | デフォルト税率の作成（オプション） |
| 税地域更新 | tax_region | UPDATE | 税地域の更新 |
| 税地域削除 | tax_region | DELETE | カスケードで子地域・税率も削除 |

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

#### tax_region

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id, country_code, province_code, parent_id, provider_id, created_by | リクエストパラメータから | コードは小文字正規化 |
| UPDATE | country_code, province_code, provider_id | WHERE id = :id | - |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| INVALID_DATA | MedusaError | 親地域が存在しない | 正しいparent_idを指定 |
| INVALID_DATA | MedusaError | 国コードが親地域と不一致 | 親地域と同じ国コードを指定 |
| INVALID_DATA | MedusaError | 子地域にprovider_idを設定 | provider_idを削除 |

### リトライ仕様

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

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

TaxRegion、TaxRate（デフォルト税率）の操作は同一トランザクション内で実行される。@InjectTransactionManagerデコレータによりトランザクション管理される。

## パフォーマンス要件

- 単一TaxRegion作成: 100ms以内
- インデックス: IDX_tax_region_unique_country_province, IDX_tax_region_unique_country_nullable_provinceを使用

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

- 認証: Admin API認証必須
- 認可: 管理者ロールが必要
- 監査ログ: created_byで作成者を記録

## 備考

- カスケード削除でchildren、tax_ratesも自動削除
- 国コード・州コードのユニーク制約あり

---

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

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

### 推奨読解順序

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

まず、税地域のデータモデルを理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | tax-region.ts | `packages/modules/tax/src/models/tax-region.ts` | TaxRegionの構造、country_code, province_code, 親子関係を確認 |

**読解のコツ**: checksに注目。CK_tax_region_provider_top_levelとCK_tax_region_country_top_levelでデータ整合性を保証。

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

税地域管理のメインメソッドを確認する。

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

**主要処理フロー**:
1. **307-334行目**: createTaxRegions - 税地域の作成
2. **336-368行目**: createTaxRegions_ - 内部実装
3. **567-600行目**: prepareTaxRegionInputForCreate - 入力の準備
4. **602-635行目**: verifyProvinceToCountryMatch - 親子関係の検証
5. **842-844行目**: normalizeRegionCodes - コードの正規化

#### Step 3: データ整合性の検証を理解する

親子関係の検証ロジックを確認する。

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

**主要処理フロー**:
- **602-635行目**: 親地域の取得と国コードの一致確認

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

```
TaxModuleService.createTaxRegions()
    │
    ├─ createTaxRegions_()
    │      ├─ prepareTaxRegionInputForCreate()
    │      │      └─ 入力の正規化とデフォルト税率の分離
    │      │
    │      ├─ verifyProvinceToCountryMatch()
    │      │      └─ 親子関係の検証
    │      │
    │      ├─ taxRegionService_.create()
    │      │      └─ TaxRegionの作成
    │      │
    │      └─ createTaxRates()
    │             └─ デフォルト税率の作成
    │
    └─ baseRepository_.serialize()
           └─ DTO形式にシリアライズ
```

### データフロー図

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

CreateTaxRegionDTO ───▶ prepareTaxRegionInput() ───▶ regionData
                        normalizeRegionCodes()        defaultRates
        │
        ▼
parent_id      ───▶ verifyProvinceToCountry() ───▶ 検証結果
                    Match()
        │
        ▼
regionData     ───▶ taxRegionService_.create() ───▶ TaxRegionDTO
        │
        ▼
defaultRates   ───▶ createTaxRates()          ───▶ TaxRateDTO
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| tax-module-service.ts | `packages/modules/tax/src/services/tax-module-service.ts` | ソース | 税モジュールのメインサービス |
| tax-region.ts | `packages/modules/tax/src/models/tax-region.ts` | ソース | TaxRegionのデータモデル |
| tax-provider.ts | `packages/modules/tax/src/models/tax-provider.ts` | ソース | TaxProviderのデータモデル |
