# 機能設計書 20-カートに追加

## 概要

本ドキュメントは、Eコマースモバイルアプリケーションにおけるカートに追加機能の設計仕様を定義する。本機能は、商品詳細画面から様々な商品タイプ（simple, grouped, bundle, configurable, downloadable, booking）をカートに追加する処理を担う。

### 本機能の処理概要

**業務上の目的・背景**：
ECサイトにおいて、カートへの商品追加は購買フローの起点となる最重要機能である。多様な商品タイプ（通常商品、グループ商品、バンドル商品、設定可能商品、ダウンロード商品、予約商品）それぞれに対応した追加処理が必要であり、適切なバリデーションと明確なフィードバックを提供することで、スムーズな購買体験を実現する。

**機能の利用シーン**：
- 商品詳細画面で「カートに追加」ボタンをタップした際
- 必要なオプション（色、サイズ等）を選択後にカートに追加する際
- グループ商品、バンドル商品の構成商品を選択してカートに追加する際
- ダウンロード商品のリンクを選択してカートに追加する際
- 予約商品の日時スロットを選択してカートに追加する際

**主要な処理内容**：
1. 商品タイプに応じたパラメータ収集
2. バリデーション（必須オプション、数量等）
3. AddToCartProductEventの発行
4. GraphQL APIを通じたカート追加処理
5. 結果通知（成功/エラー）

**関連システム・外部連携**：
- Bagisto GraphQL API（addItemToCart mutation）

**権限による制御**：
- ゲストユーザー：カートに追加可能（ゲストカート）
- ログインユーザー：カートに追加可能（ユーザーカート）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 7 | 商品詳細画面 | 主画面 | カートに追加ボタン、オプション選択、数量選択 |
| 8 | カート画面 | 遷移先 | 追加成功後のカート表示 |

## 機能種別

CRUD操作（Create）/ データ連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| quantity | int | Yes | 数量 | 1以上 |
| productId | String | Yes | 商品ID | 有効な商品ID |
| downloadLinks | List | No | ダウンロードリンク選択 | downloadable商品の場合必須 |
| groupedParams | List | No | グループ商品パラメータ | grouped商品の場合必須 |
| bundleParams | List | No | バンドル商品パラメータ | bundle商品の場合必須 |
| configurableParams | List | No | 設定可能商品パラメータ | configurable商品の場合必須 |
| configurableId | String | No | 設定可能商品のバリアントID | configurable商品の場合必須 |
| bookingParams | Map<String, dynamic> | No | 予約パラメータ | booking商品の場合必須 |
| customizableOptions | List<Map<String, dynamic>> | No | カスタマイズオプション | 対応商品の場合 |
| customizableFiles | List | No | カスタマイズファイル | ファイルアップロードの場合 |

### 入力データソース

- ユーザー入力（数量変更、オプション選択）
- 商品詳細データ（productData）
- オプション選択状態（configurableParams, bundleParams等）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| addToCartModel | AddToCartModel? | カート追加結果 |
| cart | CartModel? | 更新されたカート情報 |
| success | bool | 成功フラグ |
| message | String? | 結果メッセージ |

### 出力先

- 通知（成功/エラーメッセージ）
- カート画面（成功時の遷移先）
- カートアイコンバッジ更新

## 処理フロー

### 処理シーケンス

```
1. 商品詳細画面で商品オプション選択
   └─ configurableParams, bundleParams等を収集
2. 数量選択
   └─ qty変数を更新
3. カートに追加ボタンタップ
   └─ バリデーション実行
4. AddToCartProductEvent発行
   └─ ProductScreenBLocに送信
5. API呼び出し
   └─ ProductScreenRepo.callAddToCartAPi()
6. 結果処理
   ├─ 成功：成功通知表示、カート画面へ遷移可能
   └─ 失敗：エラー通知表示
```

### フローチャート

```mermaid
flowchart TD
    A[商品詳細画面] --> B{商品タイプ判定}
    B -->|simple| C[数量選択]
    B -->|grouped| D[構成商品選択]
    B -->|bundle| E[バンドルオプション選択]
    B -->|configurable| F[バリアント選択]
    B -->|downloadable| G[ダウンロードリンク選択]
    B -->|booking| H[予約スロット選択]
    C --> I[カートに追加ボタン]
    D --> I
    E --> I
    F --> I
    G --> I
    H --> I
    I --> J{バリデーション}
    J -->|NG| K[警告通知表示]
    J -->|OK| L[AddToCartProductEvent発行]
    L --> M[ProductScreenRepo.callAddToCartAPi]
    M --> N[GraphQL addItemToCart]
    N --> O{成功?}
    O -->|Yes| P[成功通知表示]
    O -->|No| Q[エラー通知表示]
    P --> R[カートアイコン更新]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 数量下限 | 最低1個以上をカートに追加 | 常時 |
| BR-02 | バリアント必須 | configurable商品はバリアント選択が必須 | configurable商品 |
| BR-03 | リンク必須 | downloadable商品はダウンロードリンク選択が必須 | downloadable商品 |
| BR-04 | グループ必須 | grouped商品は最低1つの構成商品選択が必須 | grouped商品 |
| BR-05 | バンドル必須 | bundle商品はバンドルオプション選択が必須 | bundle商品 |
| BR-06 | 予約必須 | booking商品は日時スロット選択が必須 | booking商品 |
| BR-07 | 在庫確認 | 在庫切れ商品はカートに追加不可 | 常時 |

### 計算ロジック

- configurableId取得: `getId(productData, configurableParams)`でバリアントIDを特定
- バリアントID特定ロジック: `productData.configurableData.index`から`attributeOptionIds`を照合

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| カート追加 | cart | INSERT/UPDATE | カートレコード作成/更新 |
| カートアイテム追加 | cart_items | INSERT | カートアイテムレコード作成 |

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

#### cart（GraphQL経由）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | customer_id, items_count, items_qty | 商品追加情報 | GraphQL: addItemToCart |

#### cart_items（GraphQL経由）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | cart_id, product_id, quantity, additional | 商品情報 | GraphQL: addItemToCart |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | バリデーションエラー | 必須オプション未選択 | 警告通知表示（ShowMessage.showNotification） |
| - | API失敗 | カート追加エラー | AddToCartProductState.failでエラー通知表示 |
| - | 在庫切れ | 在庫不足 | エラーメッセージ表示 |
| - | ネットワークエラー | 接続失敗 | エラー通知表示 |

### リトライ仕様

- 再度「カートに追加」ボタンをタップで再試行可能

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

サーバーサイドでトランザクション管理。クライアントは結果を受け取るのみ。

## パフォーマンス要件

- カート追加: 2秒以内
- ローダー表示でUXを確保（OnClickProductLoaderEvent）

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

- ゲストユーザーはゲストカートを使用
- ログインユーザーはユーザーカートを使用
- トークン認証でユーザー識別

## 備考

- 商品タイプ: simple, grouped, bundle, configurable, downloadable, booking
- マルチパートリクエスト: カスタマイズファイルがある場合はマルチパートリクエストを使用

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | add_to_cart_model.dart | `lib/screens/cart_screen/cart_model/add_to_cart_model.dart` | AddToCartModelの構造（cart, success, message） |
| 1-2 | cart_data_model.dart | `lib/screens/cart_screen/cart_model/cart_data_model.dart` | CartModelの構造（id, itemsCount, items等） |
| 1-3 | product_details_model.dart | `lib/screens/product_screen/data_model/product_details_model.dart` | 商品データ構造（type, configurableData等） |

**読解のコツ**:
- AddToCartModelはBaseModelを継承し、cartフィールドにCartModelを持つ
- CartModelにはカートの詳細情報（アイテム数、合計金額等）が含まれる

#### Step 2: イベント定義を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | product_page_event.dart | `lib/screens/product_screen/bloc/product_page_event.dart` | AddToCartProductEventの定義（47-71行目） |

**読解のコツ**:
- AddToCartProductEventには多数のパラメータが含まれる
- quantity, productIdは必須、その他は商品タイプに応じて使用
- bookingParamsは予約商品用のMapパラメータ

#### Step 3: View層（AddToCartView）を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | add_to_cart_view.dart | `lib/screens/product_screen/view/widget/add_to_cart_view.dart` | AddToCartViewクラス全体 |

**主要処理フロー（add_to_cart_view.dart）**:
- **13-38行目**: コンストラクタで各種パラメータを受け取る
- **56-62行目**: ボタンタップ時にローダー表示とカート追加処理
- **85-198行目**: `_addToCart()`メソッドで商品タイプ別の処理
  - **88-111行目**: grouped商品の処理（groupedParams必須チェック）
  - **112-137行目**: bundle商品の処理（bundleParams必須チェック）
  - **138-160行目**: downloadable商品の処理（downloadLinks必須チェック）
  - **161-185行目**: configurable商品の処理（configurableId必須チェック）
  - **186-197行目**: simple/その他商品の処理

- **200-232行目**: `getId()`メソッドでconfigurable商品のバリアントID特定

#### Step 4: BLoC処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | product_page_bloc.dart | `lib/screens/product_screen/bloc/product_page_bloc.dart` | AddToCartProductEventの処理（38-61行目） |
| 4-2 | product_page_state.dart | `lib/screens/product_screen/bloc/product_page_state.dart` | AddToCartProductStateの定義（87-95行目） |

**主要処理フロー（product_page_bloc.dart）**:
- **38行目**: event is AddToCartProductEventの判定
- **40-51行目**: repository.callAddToCartAPi()呼び出し
- **52-54行目**: 成功時のAddToCartProductState.success発行
- **55-58行目**: エラー時のAddToCartProductState.fail発行

#### Step 5: リポジトリ層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | product_page_repository.dart | `lib/screens/product_screen/bloc/product_page_repository.dart` | callAddToCartAPi()の実装（58-87行目） |

#### Step 6: APIクライアントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | api_client.dart | `lib/services/api_client.dart` | addToCart()の実装（186行目以降） |

**読解のコツ**:
- カスタマイズファイルがある場合はマルチパートリクエスト
- 通常はGraphQL mutationでaddItemToCartを呼び出し

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

```
ProductScreen (view/product_screen.dart)
    │
    └─ ProductView (view/widget/product_view.dart)
           │
           └─ AddToCartView (view/widget/add_to_cart_view.dart)
                  │
                  ├─ OnClickProductLoaderEvent発行（ローダー表示）
                  │
                  └─ _addToCart()
                         │
                         ├─ 商品タイプ別バリデーション
                         │      │
                         │      ├─ grouped: groupedParams必須チェック
                         │      ├─ bundle: bundleParams必須チェック
                         │      ├─ downloadable: downloadLinks必須チェック
                         │      ├─ configurable: configurableId必須チェック
                         │      └─ simple: バリデーションなし
                         │
                         └─ AddToCartProductEvent発行
                                │
                                └─ ProductScreenBLoc
                                       │
                                       └─ ProductScreenRepo.callAddToCartAPi()
                                              │
                                              └─ ApiClient.addToCart()
                                                     │
                                                     ├─ 通常: GraphQL mutation
                                                     └─ ファイルあり: マルチパートリクエスト
                                                            │
                                                            └─ AddToCartModel
```

### データフロー図

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

オプション選択 ─────────▶ configurableParams等に格納
数量選択 ───────────────▶ qty変数に格納
                              │
カートに追加タップ ────────▶ _addToCart()
                              │
                              ▼
                         商品タイプ判定
                              │
                              ├─ grouped → groupedParams検証
                              ├─ bundle → bundleParams検証
                              ├─ downloadable → downloadLinks検証
                              ├─ configurable → configurableId検証
                              └─ simple → 検証なし
                                    │
                                    ▼
                         AddToCartProductEvent
                              │
                              ▼
                         ProductScreenBLoc.mapEventToState()
                              │
                              ▼
                         ProductScreenRepo.callAddToCartAPi()
                              │
                              ▼
                         ApiClient.addToCart()
                              │
                              ├─ カスタマイズファイルあり
                              │      └─ マルチパートリクエスト
                              │
                              └─ カスタマイズファイルなし
                                     └─ GraphQL mutation
                                           │
                                           ▼
                                     AddToCartModel ─────▶ 成功/エラー通知
                                           │
                                           └─ cart ────▶ カートアイコン更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| add_to_cart_view.dart | `lib/screens/product_screen/view/widget/add_to_cart_view.dart` | ソース | カートに追加ボタンUI |
| add_to_cart_model.dart | `lib/screens/cart_screen/cart_model/add_to_cart_model.dart` | ソース | カート追加結果モデル |
| cart_data_model.dart | `lib/screens/cart_screen/cart_model/cart_data_model.dart` | ソース | カートデータモデル |
| product_page_bloc.dart | `lib/screens/product_screen/bloc/product_page_bloc.dart` | ソース | AddToCartProductEvent処理 |
| product_page_event.dart | `lib/screens/product_screen/bloc/product_page_event.dart` | ソース | AddToCartProductEvent定義 |
| product_page_state.dart | `lib/screens/product_screen/bloc/product_page_state.dart` | ソース | AddToCartProductState定義 |
| product_page_repository.dart | `lib/screens/product_screen/bloc/product_page_repository.dart` | ソース | callAddToCartAPi()実装 |
| api_client.dart | `lib/services/api_client.dart` | ソース | addToCart() API呼び出し |
| mutation_query.dart | `lib/services/mutation_query.dart` | ソース | GraphQL mutation定義 |
| product_screen.dart | `lib/screens/product_screen/view/product_screen.dart` | ソース | 商品詳細画面 |
| product_details_model.dart | `lib/screens/product_screen/data_model/product_details_model.dart` | ソース | 商品データモデル |
