# 機能設計書 71-販売チャネル商品紐付け

## 概要

本ドキュメントは、Medusaコマースプラットフォームにおける販売チャネルと商品の紐付け管理機能について記載する。

### 本機能の処理概要

販売チャネル商品紐付け機能は、特定の販売チャネルで販売する商品を管理するための機能である。商品を販売チャネルに追加・削除することで、各チャネルで表示・販売可能な商品を制御する。

**業務上の目的・背景**：
ECサイトでは、複数の販売チャネル（ウェブサイト、モバイルアプリ、マーケットプレイス等）を運営することが一般的である。各チャネルで取り扱う商品が異なる場合があり、例えばウェブサイトでは全商品を販売し、モバイルアプリでは限定商品のみを販売するといった運用が必要となる。本機能はこのような複数チャネル運営における商品の可視性制御を実現する。

**機能の利用シーン**：
- 新商品を特定の販売チャネルで先行販売する場合
- 季節限定商品を特定チャネルでのみ販売する場合
- マーケットプレイス出品商品を制限する場合
- 販売終了商品を特定チャネルから除外する場合

**主要な処理内容**：
1. 販売チャネルへの商品追加（リンク作成）
2. 販売チャネルからの商品削除（リンク解除）
3. 一括での商品追加・削除処理

**関連システム・外部連携**：
- 商品モジュール（Product Module）との連携
- 販売チャネルモジュール（Sales Channel Module）との連携
- リンクモジュールによるエンティティ間紐付け管理

**権限による制御**：
- 管理者権限を持つユーザーのみ操作可能
- API認証トークンによるアクセス制御

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 7 | 商品詳細画面 | 遷移先機能 | 販売チャネル設定画面への遷移 |
| 14 | 商品販売チャネル編集画面 | 主画面 | 商品と販売チャネルの紐付け |
| 126 | 販売チャネル詳細画面 | 補助機能 | チャネル内商品の表示 |
| 128 | 商品追加画面 | 主画面 | 販売チャネルに商品を追加 |

## 機能種別

データ連携 / リンク管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | string | Yes | 販売チャネルID | 有効なSalesChannel ID |
| add | string[] | No | 追加する商品IDの配列 | 有効なProduct IDの配列 |
| remove | string[] | No | 削除する商品IDの配列 | 有効なProduct IDの配列 |

### 入力データソース

- 管理画面からのREST API呼び出し
- POST /admin/sales-channels/{id}/products

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| sales_channel | SalesChannelDTO | 更新後の販売チャネル情報 |
| sales_channel.id | string | 販売チャネルID |
| sales_channel.name | string | 販売チャネル名 |
| sales_channel.description | string | 販売チャネル説明 |
| sales_channel.is_disabled | boolean | 無効フラグ |

### 出力先

- REST APIレスポンス（JSON形式）
- product_sales_channelリンクテーブル

## 処理フロー

### 処理シーケンス

```
1. APIリクエスト受信
   └─ POST /admin/sales-channels/{id}/products
2. リクエストバリデーション
   └─ add/removeパラメータの検証
3. ワークフロー実行
   └─ linkProductsToSalesChannelWorkflow
4. 追加処理（addパラメータがある場合）
   └─ associateProductsWithSalesChannelsStep
5. 削除処理（removeパラメータがある場合）
   └─ detachProductsFromSalesChannelsStep
6. 販売チャネル情報の再取得
   └─ refetchSalesChannel
7. レスポンス返却
   └─ 更新後の販売チャネル情報
```

### フローチャート

```mermaid
flowchart TD
    A[APIリクエスト受信] --> B{バリデーション}
    B -->|エラー| C[エラーレスポンス]
    B -->|成功| D[ワークフロー開始]
    D --> E{追加商品あり?}
    E -->|Yes| F[商品リンク作成]
    E -->|No| G{削除商品あり?}
    F --> G
    G -->|Yes| H[商品リンク削除]
    G -->|No| I[販売チャネル再取得]
    H --> I
    I --> J[レスポンス返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-71-01 | 同一商品重複防止 | 同一商品を同一チャネルに重複追加しない | 商品追加時 |
| BR-71-02 | 存在確認 | 追加・削除する商品IDが実在すること | リンク操作時 |
| BR-71-03 | チャネル存在確認 | 対象販売チャネルが存在すること | API呼び出し時 |

### 計算ロジック

該当なし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 商品追加 | product_sales_channel | INSERT | 商品と販売チャネルのリンク作成 |
| 商品削除 | product_sales_channel | DELETE | 商品と販売チャネルのリンク削除 |
| チャネル取得 | sales_channel | SELECT | 更新後の販売チャネル情報取得 |

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

#### product_sales_channel

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | sales_channel_id | 入力パラメータのid | - |
| INSERT | product_id | addパラメータの各要素 | - |
| DELETE | sales_channel_id | 入力パラメータのid | - |
| DELETE | product_id | removeパラメータの各要素 | - |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 404 | NOT_FOUND | 販売チャネルが存在しない | 正しいIDを指定 |
| 400 | INVALID_DATA | 商品IDが不正 | 正しい商品IDを指定 |
| 401 | UNAUTHORIZED | 認証失敗 | 有効なトークンを使用 |

### リトライ仕様

トランザクション失敗時は自動ロールバック。リトライは呼び出し元で制御。

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

- ワークフロー内の全操作は単一トランザクションで実行
- 追加処理と削除処理は並列実行可能
- エラー発生時は全操作がロールバック

## パフォーマンス要件

- 商品追加・削除は1000件以下を推奨
- 大量データの場合はバッチ処理を検討

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

- 管理者APIのため認証必須
- Actor IDによる操作者追跡
- 監査ログへの記録

## 備考

- 商品が販売チャネルに紐付いていない場合、ストアフロントAPIでその商品は表示されない
- デフォルト販売チャネルの設定はストア管理機能で行う

---

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

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

### 推奨読解順序

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

販売チャネルと商品のリンク関係を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | sales-channel.ts | `packages/modules/sales-channel/src/models/sales-channel.ts` | 販売チャネルのデータモデル定義（id, name, description, is_disabled, metadata） |

**読解のコツ**: Medusaでは`model.define()`を使用してエンティティを定義する。`model.id({ prefix: "sc" })`でIDプレフィックスを設定している。

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

API呼び出しの起点を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | route.ts | `packages/medusa/src/api/admin/sales-channels/[id]/products/route.ts` | POSTハンドラでworkflowを呼び出している |

**主要処理フロー**:
1. **10-35行目**: POSTハンドラの定義
2. **18行目**: `req.validatedBody`からadd/removeを取得
3. **20-27行目**: `linkProductsToSalesChannelWorkflow`を実行
4. **29-33行目**: 更新後の販売チャネルを再取得して返却

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

商品紐付けのビジネスロジックを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | link-products-to-sales-channel.ts | `packages/core/core-flows/src/sales-channel/workflows/link-products-to-sales-channel.ts` | ワークフローの全体構成 |

**主要処理フロー**:
- **39-61行目**: ワークフロー定義
- **44-49行目**: 追加用リンクデータの変換（transform）
- **51-56行目**: 削除用リンクデータの変換
- **58行目**: `associateProductsWithSalesChannelsStep`で追加処理
- **59行目**: `detachProductsFromSalesChannelsStep`で削除処理

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

モジュールサービスの実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | sales-channel-module.ts | `packages/modules/sales-channel/src/services/sales-channel-module.ts` | CRUD操作の実装 |

**主要処理フロー**:
- **34-53行目**: クラス定義と依存性注入
- **60-84行目**: createSalesChannels実装（@InjectManager, @EmitEventsデコレータ）

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

```
POST /admin/sales-channels/{id}/products (route.ts)
    │
    ├─ linkProductsToSalesChannelWorkflow
    │      │
    │      ├─ transform (入力データ変換)
    │      │
    │      ├─ associateProductsWithSalesChannelsStep
    │      │      └─ LinkModuleService.create()
    │      │
    │      └─ detachProductsFromSalesChannelsStep
    │             └─ LinkModuleService.delete()
    │
    └─ refetchSalesChannel
           └─ RemoteQuery (sales_channel取得)
```

### データフロー図

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

HTTPリクエスト            linkProductsToSalesChannel          SalesChannelDTO
{                         Workflow
  id: "sc_xxx",             │
  add: ["prod_1"],          ├─> product_sales_channel        product_sales_channel
  remove: ["prod_2"]        │   (INSERT)                     テーブル更新
}                           │
                            └─> product_sales_channel
                                (DELETE)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| route.ts | `packages/medusa/src/api/admin/sales-channels/[id]/products/route.ts` | ソース | APIエンドポイント定義 |
| link-products-to-sales-channel.ts | `packages/core/core-flows/src/sales-channel/workflows/link-products-to-sales-channel.ts` | ソース | ワークフロー定義 |
| associate-products-with-channels.ts | `packages/core/core-flows/src/sales-channel/steps/associate-products-with-channels.ts` | ソース | 商品追加ステップ |
| detach-products-from-sales-channels.ts | `packages/core/core-flows/src/sales-channel/steps/detach-products-from-sales-channels.ts` | ソース | 商品削除ステップ |
| sales-channel-module.ts | `packages/modules/sales-channel/src/services/sales-channel-module.ts` | ソース | モジュールサービス |
| sales-channel.ts | `packages/modules/sales-channel/src/models/sales-channel.ts` | ソース | データモデル定義 |
| helpers.ts | `packages/medusa/src/api/admin/sales-channels/helpers.ts` | ソース | 共通ヘルパー関数 |
| validators.ts | `packages/medusa/src/api/admin/sales-channels/validators.ts` | ソース | バリデーション定義 |
