# 機能設計書 23-カート税計算

## 概要

本ドキュメントは、Medusaコマースプラットフォームにおけるカート内商品および配送方法に対する税金自動計算機能について記載する。

### 本機能の処理概要

本機能は、カート内の商品明細（ラインアイテム）および配送方法に対して、配送先住所に基づいた税金を自動計算し、税行（Tax Line）として設定する機能である。リージョンの税設定や外部税プロバイダーとの連携により、正確な税金計算を実現する。

**業務上の目的・背景**：ECサイトでは、顧客の所在地や配送先に応じて適用される税率が異なる。特に国際取引や、米国のように州・郡・市レベルで税率が異なる地域では、正確な税金計算が法令遵守の観点から必須である。また、顧客に対して透明性の高い価格表示を行うためにも、チェックアウト時点で正確な税額を提示する必要がある。

**機能の利用シーン**：
- カートに商品が追加・更新された時
- 配送先住所が設定・変更された時
- 配送方法が選択・変更された時
- チェックアウト画面で税金計算を明示的にリクエストした時

**主要な処理内容**：
1. カートの配送先住所から適用されるリージョン・税設定を特定
2. 各ラインアイテムに対して税率を計算し、税行（LineItemTaxLine）を設定
3. 各配送方法に対して税率を計算し、税行（ShippingMethodTaxLine）を設定
4. 外部税プロバイダー（TaxJar、Avalara等）が設定されている場合は、APIを通じて税率を取得
5. 計算結果をカートに反映し、合計金額を更新

**関連システム・外部連携**：
- 税モジュール（Tax Module）: 税率の管理、税地域の設定
- 外部税プロバイダー: TaxJar、Avalara等のサードパーティ税計算サービス
- リージョンモジュール: 国・地域ごとの税設定

**権限による制御**：Store APIから誰でも自身のカートの税計算をトリガー可能。Admin APIでは管理者が任意のカートの税計算を実行可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | チェックアウト画面 | 主画面 | 税金計算・表示 |
| - | カート画面 | 参照画面 | 税込み合計の表示 |

## 機能種別

計算処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| cart_id | string | Yes | 対象カートのID | カートが存在すること |
| force_tax_calculation | boolean | No | 強制的に税計算を再実行するか | デフォルト: false |
| items | CartLineItemDTO[] | No | 計算対象のアイテム（指定なしの場合は全アイテム） | - |
| shipping_methods | CartShippingMethodDTO[] | No | 計算対象の配送方法（指定なしの場合は全配送方法） | - |

### 入力データソース

- Store API: POST /store/carts/{id}/taxes
- 内部ワークフロー呼び出し（カート更新時等）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| cart | CartDTO | 税計算後のカート情報 |
| cart.items[].tax_lines | LineItemTaxLineDTO[] | ラインアイテムの税行 |
| cart.items[].tax_lines[].rate | number | 税率（パーセント） |
| cart.items[].tax_lines[].code | string | 税コード |
| cart.items[].tax_lines[].description | string | 税の説明 |
| cart.shipping_methods[].tax_lines | ShippingMethodTaxLineDTO[] | 配送方法の税行 |
| cart.tax_total | BigNumber | 税金合計 |
| cart.total | BigNumber | 税込み合計 |

### 出力先

- APIレスポンス（JSON形式）
- データベース（cart_line_item_tax_line, cart_shipping_method_tax_lineテーブル）

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ cart_idとforce_tax_calculationを取得

2. カートの取得
   └─ カート情報、アイテム、配送方法、配送先住所を取得

3. カート検証
   └─ validateCartStepでカート状態を検証

4. 分散ロックの取得
   └─ カートIDをキーとしてロック取得

5. 税率計算
   └─ getItemTaxLinesStepで税率を計算
      ├─ リージョンの自動税計算設定を確認
      ├─ 外部プロバイダーが設定されている場合はAPI呼び出し
      └─ ラインアイテムと配送方法それぞれに税行を計算

6. 税行の翻訳（多言語対応）
   └─ getTranslatedTaxLinesStepでロケールに応じた説明を設定

7. 税行の設定
   └─ setTaxLinesForItemsStepでカートに税行を保存
      ├─ 既存の税行を削除/更新
      └─ 新しい税行を追加

8. ロック解放
   └─ releaseLockStepでロック解放
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B[カート取得]
    B --> C{カート存在?}
    C -->|No| D[エラー返却]
    C -->|Yes| E[カート検証]
    E --> F[ロック取得]
    F --> G{外部プロバイダー?}
    G -->|Yes| H[外部API呼び出し]
    G -->|No| I[内部税率計算]
    H --> J[税行データ作成]
    I --> J
    J --> K[税行翻訳]
    K --> L[税行をDBに保存]
    L --> M[ロック解放]
    M --> N[レスポンス返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-23-01 | リージョン自動税計算 | リージョンのautomatic_taxesがtrueの場合のみ自動計算 | 常時 |
| BR-23-02 | 配送先住所必須 | 税計算には配送先住所（特にcountry_code）が必要 | 税計算実行時 |
| BR-23-03 | 税込み価格 | is_tax_inclusiveフラグにより税込み/税抜きを判定 | 価格計算時 |
| BR-23-04 | 強制再計算 | force_tax_calculation=trueで外部プロバイダーへの再問い合わせ | 明示的リクエスト時 |
| BR-23-05 | 税行の一意性 | 同一アイテム・税コードの税行は1つのみ | 税行設定時 |

### 計算ロジック

```
■ ラインアイテム税計算
税額 = (unit_price * quantity) * (tax_rate / 100)

■ 配送方法税計算
配送税額 = shipping_amount * (tax_rate / 100)

■ 税込み価格の場合
税額 = price - (price / (1 + tax_rate / 100))

■ カート全体の税合計
tax_total = Σ(ラインアイテム税額) + Σ(配送方法税額)
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 税行設定 | cart_line_item_tax_line | UPSERT | ラインアイテムの税行を作成/更新 |
| 税行削除 | cart_line_item_tax_line | DELETE（論理） | 不要になった税行を削除 |
| 配送税行設定 | cart_shipping_method_tax_line | UPSERT | 配送方法の税行を作成/更新 |
| 配送税行削除 | cart_shipping_method_tax_line | DELETE（論理） | 不要になった税行を削除 |

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

#### cart_line_item_tax_line

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPSERT | id | 自動生成（calitxlプレフィックス） | 主キー |
| UPSERT | item_id | ラインアイテムID | 外部キー |
| UPSERT | rate | 税率（パーセント） | 数値 |
| UPSERT | code | 税コード | 文字列 |
| UPSERT | description | 税の説明 | 文字列 |
| UPSERT | provider_id | 税プロバイダーID | NULL許可 |

#### cart_shipping_method_tax_line

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPSERT | id | 自動生成（casmtxlプレフィックス） | 主キー |
| UPSERT | shipping_method_id | 配送方法ID | 外部キー |
| UPSERT | rate | 税率（パーセント） | 数値 |
| UPSERT | code | 税コード | 文字列 |
| UPSERT | description | 税の説明 | 文字列 |
| UPSERT | provider_id | 税プロバイダーID | NULL許可 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| NOT_FOUND | MedusaError | カートが存在しない | 有効なカートIDを指定 |
| INVALID_DATA | MedusaError | 配送先住所が設定されていない | 住所を設定してから実行 |
| EXTERNAL_PROVIDER_ERROR | MedusaError | 外部税プロバイダーのAPI呼び出し失敗 | 再試行またはフォールバック |

### リトライ仕様

外部税プロバイダーへのAPI呼び出し失敗時は、プロバイダーの実装に応じてリトライが行われる場合がある。

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

- 税行のUPSERT操作はトランザクション内で実行
- 既存税行の削除と新規税行の追加は並列で実行される
- エラー発生時はロールバック

## パフォーマンス要件

- 内部税率計算: 500ms以内
- 外部プロバイダー利用時: プロバイダーのレスポンス時間に依存（通常1-3秒）

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

- 外部税プロバイダーへの認証情報は安全に管理
- 顧客の住所情報は外部送信時に必要最小限に

## 備考

- 税計算は配送先住所のcountry_codeとprovinceに大きく依存
- リージョンのautomatic_taxes設定がfalseの場合、手動で税率を設定する必要がある

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | line-item-tax-line.ts | `packages/modules/cart/src/models/line-item-tax-line.ts` | ラインアイテム税行モデル |
| 1-2 | shipping-method-tax-line.ts | `packages/modules/cart/src/models/shipping-method-tax-line.ts` | 配送方法税行モデル |

**読解のコツ**: 税行はラインアイテム・配送方法それぞれに紐付き、rate（税率）、code（税コード）、description（説明）を持つ。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | route.ts | `packages/medusa/src/api/store/carts/[id]/taxes/route.ts` | Store API税計算エンドポイント |

**主要処理フロー**:
- **6-24行目**: POST メソッド - 税計算のエントリーポイント
- **10-14行目**: updateTaxLinesWorkflowの呼び出し、force_tax_calculation=true

#### Step 3: ワークフローを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | update-tax-lines.ts | `packages/core/core-flows/src/cart/workflows/update-tax-lines.ts` | 税計算のメインワークフロー |

**主要処理フロー**:
- **128-185行目**: updateTaxLinesWorkflowの定義
- **131-145行目**: カート取得（cart_idからの取得、またはcart引数を使用）
- **151-157行目**: validateCartStepとロック取得
- **159-165行目**: getItemTaxLinesStepで税率計算
- **168-172行目**: getTranslatedTaxLinesStepで多言語対応
- **174-179行目**: setTaxLinesForItemsStepで税行を保存
- **181-183行目**: ロック解放

#### Step 4: サービス層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | cart-module.ts | `packages/modules/cart/src/services/cart-module.ts` | 税行関連メソッド |

**主要処理フロー**:
- **853-886行目**: upsertLineItemTaxLines - ラインアイテム税行のupsert
- **923-956行目**: upsertShippingMethodTaxLines - 配送方法税行のupsert
- **1320-1387行目**: setLineItemTaxLines - ラインアイテム税行の設定（削除＋upsert）
- **1480-1549行目**: setShippingMethodTaxLines - 配送方法税行の設定

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

```
POST /store/carts/{id}/taxes
    │
    ├─ updateTaxLinesWorkflow
    │      │
    │      ├─ useQueryGraphStep (カート取得)
    │      │
    │      ├─ validateCartStep (カート検証)
    │      │
    │      ├─ acquireLockStep (ロック取得)
    │      │
    │      ├─ getItemTaxLinesStep (税率計算)
    │      │      └─ Tax Module / External Provider
    │      │
    │      ├─ getTranslatedTaxLinesStep (翻訳)
    │      │
    │      ├─ setTaxLinesForItemsStep (税行保存)
    │      │      ├─ CartModuleService.setLineItemTaxLines
    │      │      │      └─ lineItemTaxLineService_.upsert
    │      │      └─ CartModuleService.setShippingMethodTaxLines
    │      │             └─ shippingMethodTaxLineService_.upsert
    │      │
    │      └─ releaseLockStep (ロック解放)
    │
    └─ refetchCart
```

### データフロー図

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

cart_id          ───┐
force_tax_calc   ───┼──▶ updateTaxLinesWorkflow ───▶ 更新後カート
                    │         │
                    │         ├─▶ Tax Module/Provider ───▶ 税率取得
                    │         │
                    │         ├─▶ 税行計算
                    │         │
                    │         └─▶ 税行保存 ───▶ cart_line_item_tax_line
                    │                     ───▶ cart_shipping_method_tax_line
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| route.ts | `packages/medusa/src/api/store/carts/[id]/taxes/route.ts` | ソース | Store APIエントリーポイント |
| update-tax-lines.ts | `packages/core/core-flows/src/cart/workflows/update-tax-lines.ts` | ソース | 税計算ワークフロー |
| get-item-tax-lines.ts | `packages/core/core-flows/src/tax/steps/get-item-tax-lines.ts` | ソース | 税率計算ステップ |
| cart-module.ts | `packages/modules/cart/src/services/cart-module.ts` | ソース | 税行操作メソッド |
| line-item-tax-line.ts | `packages/modules/cart/src/models/line-item-tax-line.ts` | ソース | ラインアイテム税行モデル |
| shipping-method-tax-line.ts | `packages/modules/cart/src/models/shipping-method-tax-line.ts` | ソース | 配送方法税行モデル |
