# 機能設計書 58-税プロバイダー管理

## 概要

本ドキュメントは、Medusa ECプラットフォームにおける税プロバイダー管理機能の設計を記述する。外部税計算プロバイダーとの連携機能の詳細仕様を定義する。

### 本機能の処理概要

税プロバイダー管理機能は、TaxJar等の外部税計算サービスとの連携を管理する機能である。税プロバイダーの登録状態管理、プロバイダーの有効/無効切り替え、税地域との紐付けを行う。プロバイダーを通じて外部サービスに税計算を委譲することで、より高精度な税計算を実現する。

**業務上の目的・背景**：各国の税法は複雑であり、特に米国では州、郡、市レベルで税率が異なる。このような複雑な税計算を内部で完全に実装することは困難なため、TaxJar、Avalara等の専門サービスに税計算を委譲する需要がある。

**機能の利用シーン**：
1. 外部税計算プロバイダーの設定
2. 税地域へのプロバイダー割り当て
3. プロバイダーの有効/無効切り替え
4. 税計算時のプロバイダーへの委譲

**主要な処理内容**：
1. TaxProviderの登録・取得
2. プロバイダーの有効/無効状態管理
3. プロバイダーを通じた税計算の実行
4. 税地域とプロバイダーの紐付け

**関連システム・外部連携**：TaxJar、Avalara等の外部税計算サービスと連携する。税地域管理、税計算機能と密接に連携する。

**権限による制御**：税プロバイダーの設定変更は管理者のみが実行可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 税リージョン作成画面 | API連携 | プロバイダーの選択・設定 |
| - | 税リージョン詳細画面 | API連携 | 割り当てプロバイダーの表示 |

## 機能種別

データ取得 / 外部連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| providerId | string | Yes | プロバイダーID | tp_プレフィックス |
| itemLines | TaxTypes.ItemTaxCalculationLine[] | Yes | 課税対象商品明細 | - |
| shippingLines | TaxTypes.ShippingTaxCalculationLine[] | No | 課税対象配送明細 | - |
| context | TaxTypes.TaxCalculationContext | Yes | 税計算コンテキスト | - |
| context.address | object | Yes | 住所情報 | - |
| context.customer | object | No | 顧客情報 | - |

### 入力データソース

- リクエストパラメータ（API経由）
- 税計算モジュールからの呼び出し

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ItemTaxLineDTO[] | array | 商品の税額明細 |
| ShippingTaxLineDTO[] | array | 配送の税額明細 |
| [].rate_id | string | 適用された税率ID |
| [].rate | number | 税率 |
| [].name | string | 税率名 |
| [].code | string | 税率コード |
| [].line_item_id | string | 対象アイテムID |

### 出力先

- 呼び出し元のサービス/ワークフロー
- 税計算モジュール

## 処理フロー

### 処理シーケンス

```
1. プロバイダーの取得
   └─ retrieveProvider()でDIコンテナからプロバイダーを取得
2. プロバイダー存在確認
   └─ 取得できない場合はAwilixResolutionErrorをハンドリング
3. 税計算の委譲
   └─ provider.getTaxLines()を呼び出し
4. 結果の返却
   └─ 税額明細を返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[プロバイダーIDを受け取る]
    B --> C[DIコンテナからプロバイダーを取得]
    C --> D{取得成功?}
    D -->|No| E[AwilixResolutionError?]
    E -->|Yes| F[設定不備エラーメッセージ]
    E -->|No| G[一般エラーメッセージ]
    F --> H[例外スロー]
    G --> H
    D -->|Yes| I[provider.getTaxLines呼び出し]
    I --> J[税額明細を返却]
    H --> K[終了]
    J --> K
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-58-01 | プロバイダー登録必須 | 使用するプロバイダーは事前にDIコンテナに登録が必要 | プロバイダー呼び出し時 |
| BR-58-02 | プレフィックス規約 | プロバイダーIDはtp_プレフィックスを使用 | プロバイダー取得時 |
| BR-58-03 | 親地域のみプロバイダー設定可 | 子地域（州/都道府県）にはプロバイダーを設定不可 | 税地域作成時 |
| BR-58-04 | ITaxProviderインターフェース | プロバイダーはITaxProviderインターフェースを実装 | プロバイダー登録時 |

### 計算ロジック

```typescript
// プロバイダーの取得
retrieveProvider(providerId: string): ITaxProvider {
  try {
    return this.__container__[providerId] as ITaxProvider
  } catch (err) {
    if (err.name === "AwilixResolutionError") {
      const errMessage = `
Unable to retrieve the tax provider with id: ${providerId}
Please make sure that the provider is registered in the container and it is configured correctly in your project configuration file.`
      this.#logger.error(`AwilixResolutionError: ${err.message}`, err)
      throw new Error(errMessage)
    }
    throw new Error(`Unable to retrieve the tax provider with id: ${providerId}`)
  }
}

// 税計算の委譲
async getTaxLines(
  providerId: string,
  itemLines: TaxTypes.ItemTaxCalculationLine[],
  shippingLines: TaxTypes.ShippingTaxCalculationLine[],
  context: TaxTypes.TaxCalculationContext
): Promise<(TaxTypes.ItemTaxLineDTO | TaxTypes.ShippingTaxLineDTO)[]> {
  const provider = this.retrieveProvider(providerId)
  return provider.getTaxLines(itemLines, shippingLines, context)
}
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| プロバイダー取得 | tax_provider | SELECT | プロバイダー情報の取得 |
| 税地域紐付け | tax_region | UPDATE | プロバイダーIDの設定 |

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

#### tax_provider

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, is_enabled | WHERE id = :providerId | - |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------)|
| - | AwilixResolutionError | プロバイダーがDIコンテナに登録されていない | プロバイダーを正しく設定 |
| - | Error | プロバイダー取得時の一般エラー | ログを確認して対処 |

### リトライ仕様

外部プロバイダーへの接続エラー時はアプリケーション層でリトライ可能。ネットワークエラー時は適切なリトライ間隔を設定。

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

読み取り操作および外部API呼び出しが主な処理のため、トランザクション管理は不要。プロバイダー設定の更新時はトランザクション内で実行。

## パフォーマンス要件

- プロバイダー取得: 10ms以内
- 税計算（外部API含む）: プロバイダー依存（通常100-500ms）
- キャッシュ: プロバイダーインスタンスはDIコンテナでシングルトン管理

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

- 認証: Admin API認証必須（設定変更時）
- 認可: 管理者ロールが必要
- 外部API: プロバイダーごとのAPIキー管理が必要
- ログ: エラー時にフルスタックトレースをログ出力

## 備考

- プロバイダーはプロジェクト設定ファイルで登録
- is_enabledフラグでプロバイダーの有効/無効を制御
- 税地域にはregionsリレーションで複数地域を紐付け可能

---

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

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

### 推奨読解順序

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

まず、税プロバイダーのデータモデルを理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | tax-provider.ts | `packages/modules/tax/src/models/tax-provider.ts` | TaxProviderの構造、id, is_enabled, regionsを確認 |

**読解のコツ**: regionsリレーションに注目。税地域との1対多の関係を理解することが重要。

#### Step 2: サービス実装を理解する

税プロバイダーサービスの実装を確認する。

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

**主要処理フロー**:
1. **17-22行目**: コンストラクタ - ロガーの初期化
2. **24-44行目**: retrieveProvider - DIコンテナからプロバイダーを取得
3. **46-54行目**: getTaxLines - プロバイダーに税計算を委譲

#### Step 3: プロバイダーインターフェースを理解する

外部プロバイダーが実装すべきインターフェースを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | types/tax.ts | `packages/core/types/src/tax/` | ITaxProviderインターフェースの定義 |

**主要メソッド**:
- `getTaxLines()` - 税額明細を計算して返却

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

```
TaxModuleService.getTaxLinesFromProvider()
    │
    └─ TaxProviderService.getTaxLines()
           │
           ├─ retrieveProvider()
           │      └─ DIコンテナからプロバイダー取得
           │
           └─ provider.getTaxLines()
                  └─ 外部プロバイダーで税計算
```

### データフロー図

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

providerId      ───▶ retrieveProvider()    ───▶ ITaxProvider
        │
        ▼
itemLines       ───▶ provider.getTaxLines() ───▶ ItemTaxLineDTO[]
shippingLines                                    ShippingTaxLineDTO[]
context
```

### 関連ファイル一覧

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