# 機能設計書 19-カート表示

## 概要

本ドキュメントは、Eコマースモバイルアプリケーションにおけるカート表示機能の設計仕様を定義する。本機能は、ユーザーがカートに追加した商品の一覧表示、数量変更、削除、クーポン適用、合計金額の確認を提供する。

### 本機能の処理概要

**業務上の目的・背景**：
ECサイトにおいて、カート機能は購買プロセスの中核である。ユーザーが選択した商品を確認し、数量調整や削除、クーポン適用などの操作を行い、最終的なチェックアウトへ進むためのインターフェースを提供する。

**機能の利用シーン**：
- 商品をカートに追加した後、カート内容を確認したい際
- カート内の商品数量を変更したい際
- 不要な商品をカートから削除したい際
- クーポンコードを適用したい際
- チェックアウトに進む前に合計金額を確認したい際

**主要な処理内容**：
1. カート内商品の一覧取得と表示
2. 商品数量の変更
3. 商品のカートからの削除（個別/一括）
4. クーポンコードの適用と解除
5. 商品のウィッシュリストへの移動
6. 合計金額、税金、割引金額の表示
7. チェックアウト画面への遷移

**関連システム・外部連携**：
- Bagisto GraphQL API（カートデータ取得、更新、クーポン操作）

**権限による制御**：
- ゲストユーザー：カート機能利用可能
- ログインユーザー：カート機能利用可能 + ウィッシュリスト移動可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 12 | カート画面 | 主画面 | カート内容表示、数量変更、削除、クーポン適用 |
| 13 | チェックアウト画面 | 遷移先画面 | 「Proceed」ボタン押下時の遷移先 |
| 7 | 商品詳細画面 | 参照画面 | カート商品タップ時の遷移先 |

## 機能種別

CRUD操作（Read/Update/Delete）/ データ連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| cartItemId | int | Yes（削除時） | カートアイテムID | - |
| item | List<Map<dynamic, String>> | Yes（更新時） | 数量変更リスト | - |
| code | String | Yes（クーポン適用時） | クーポンコード | 空文字不可 |

### 入力データソース

- ユーザー入力（数量変更、クーポンコード）
- GraphQL API（カートデータ）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| cartDetailsModel | CartModel? | カート詳細情報 |
| removeCartProductModel | AddToCartModel? | 削除結果 |
| baseModel | ApplyCoupon? | クーポン適用結果 |

### 出力先

- 画面表示（カート商品一覧、合計金額、クーポン情報）
- GlobalData.cartCountController（カート数更新）
- SharedPreferences（カート数永続化）

## 処理フロー

### 処理シーケンス

```
1. 画面初期化（initState）
   └─ FetchCartDataEvent発行
2. カートデータ取得
   └─ CartScreenRepositoryImp.getCartData()
3. カート表示
   └─ BlocConsumerで状態監視、_cartScreenBody()で描画
4. ユーザーアクション
   ├─ 数量変更：UpdateCartEvent発行
   ├─ 商品削除：RemoveCartItemEvent発行
   ├─ 一括削除：RemoveAllCartItemEvent発行
   ├─ クーポン適用：AddCouponCartEvent発行
   ├─ クーポン解除：RemoveCouponCartEvent発行
   └─ ウィッシュリスト移動：MoveToCartEvent発行
5. 結果通知
   └─ ShowMessage.successNotification / errorNotification
```

### フローチャート

```mermaid
flowchart TD
    A[カート画面] --> B[FetchCartDataEvent発行]
    B --> C[API呼び出し]
    C --> D{成功?}
    D -->|Yes| E[カート表示]
    D -->|No| F[エラー表示]
    E --> G{カート空?}
    G -->|Yes| H[EmptyDataView表示]
    G -->|No| I[カートアイテム一覧表示]
    I --> J{ユーザー操作}
    J -->|数量変更| K[UpdateCartEvent発行]
    J -->|削除| L[RemoveCartItemEvent発行]
    J -->|一括削除| M[RemoveAllCartItemEvent発行]
    J -->|クーポン適用| N[AddCouponCartEvent発行]
    J -->|ウィッシュリスト移動| O[MoveToCartEvent発行]
    J -->|Proceed| P[チェックアウト画面へ遷移]
    K --> Q[成功/エラー通知]
    L --> Q
    M --> Q
    N --> Q
    O --> Q
    Q --> B
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 数量下限 | 商品数量は1以上 | 数量変更時 |
| BR-02 | 空カート表示 | カートが空の場合EmptyDataViewを表示 | カート表示時 |
| BR-03 | ウィッシュリスト移動 | ログインユーザーのみウィッシュリストへ移動可能 | 移動操作時 |
| BR-04 | クーポン排他 | 適用できるクーポンは1つのみ | クーポン適用時 |
| BR-05 | Proceed無効化 | 数量変更がある場合はUPDATEボタン表示、Proceed無効化 | 数量変更時 |

### 計算ロジック

- 合計金額: formattedPrice.grandTotalで表示
- 税金: formattedPrice.taxAmountで表示
- 割引: formattedPrice.discountAmountで表示
- 小計: formattedPrice.subTotalで表示
- 送料: formattedPrice.shippingAmountで表示

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| カート取得 | cart, cart_items | SELECT | カートとアイテム情報取得 |
| 数量更新 | cart_items | UPDATE | アイテムの数量を更新 |
| 商品削除 | cart_items | DELETE | アイテムを削除 |
| クーポン適用 | cart | UPDATE | クーポンコードを適用 |
| クーポン解除 | cart | UPDATE | クーポンコードを解除 |

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

#### cart（GraphQL経由）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | 全フィールド | customer/session基準 | GraphQL: cartDetail |
| UPDATE | coupon_code | クーポンコード | GraphQL: applyCoupon |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | API失敗 | カートデータ取得エラー | ErrorMessage.errorMsg表示 |
| - | クーポンエラー | 無効なクーポン | ShowMessage.errorNotification表示 |
| - | 更新失敗 | 数量更新エラー | ShowMessage.errorNotification表示 |
| - | 削除失敗 | 削除エラー | ShowMessage.errorNotification表示 |

### リトライ仕様

- Pull-to-Refreshで手動リトライ可能（RefreshIndicator）

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

カート操作はバックエンドでトランザクション制御。

## パフォーマンス要件

- カートデータ取得: 2秒以内
- CartLoaderViewでスケルトンUI表示

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

- カートはセッション/ユーザーIDに紐づく
- 他ユーザーのカートにはアクセス不可

## 備考

- ゲストカートはセッションに紐づく
- ログイン時にゲストカートをマージ可能

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | cart_data_model.dart | `lib/screens/cart_screen/cart_model/cart_data_model.dart` | CartModel, Items, FormattedPriceの構造 |
| 1-2 | add_to_cart_model.dart | `lib/screens/cart_screen/cart_model/add_to_cart_model.dart` | AddToCartModel（削除/更新結果） |
| 1-3 | apply_coupon.dart | `lib/screens/cart_screen/cart_model/apply_coupon.dart` | ApplyCoupon（クーポン結果） |

**読解のコツ**:
- CartModelにid, couponCode, itemsCount, itemsQty, grandTotal, formattedPrice, itemsがある
- FormattedPriceにgrandTotal, subTotal, discountAmount, taxAmount, shippingAmountがある

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | cart_screen.dart | `lib/screens/cart_screen/cart_screen.dart` | CartScreenクラス |

**主要処理フロー**:
1. **31-35行目**: initState()でBLoC取得、fetchCartData()呼び出し
2. **37-39行目**: fetchCartData()でFetchCartDataEvent発行
3. **61-121行目**: BlocConsumerのlistenerで各状態のメッセージ表示
4. **124-208行目**: buildContainer()で状態に応じたUI構築
5. **211-234行目**: _cartScreenBody()でカート内容/空表示の分岐
6. **237-294行目**: _cartItems()でカートアイテム、クーポン、価格詳細を表示

#### Step 3: BLoC層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | cart_screen_bloc.dart | `lib/screens/cart_screen/bloc/cart_screen_bloc.dart` | イベント処理ロジック |
| 3-2 | cart_screen_repository.dart | `lib/screens/cart_screen/bloc/cart_screen_repository.dart` | API呼び出し |

**主要処理フロー（cart_screen_bloc.dart）**:
- **22-28行目**: FetchCartDataEventの処理
- **29-37行目**: UpdateCartEventの処理
- **38-47行目**: RemoveCartItemEventの処理
- **48-56行目**: RemoveAllCartItemEventの処理
- **57-69行目**: AddCouponCartEventの処理
- **70-82行目**: RemoveCouponCartEventの処理
- **83-99行目**: MoveToCartEventの処理

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

```
CartScreen (cart_screen.dart)
    │
    ├─ CartScreenBloc (bloc/cart_screen_bloc.dart)
    │      │
    │      └─ CartScreenRepositoryImp (bloc/cart_screen_repository.dart)
    │             │
    │             ├─ ApiClient.getCartDetails() → カート取得
    │             ├─ ApiClient.updateItemToCart() → 数量更新
    │             ├─ ApiClient.removeItemFromCart() → 商品削除
    │             ├─ ApiClient.removeAllCartItem() → 一括削除
    │             ├─ ApiClient.applyCoupon() → クーポン適用
    │             ├─ ApiClient.removeCoupon() → クーポン解除
    │             └─ ApiClient.moveCartToWishlist() → ウィッシュリスト移動
    │
    ├─ CartListItem (widget/cart_list_item.dart)
    │      └─ 商品一覧表示、数量変更UI
    │
    ├─ ApplyCouponView (widget/apply_coupon_view.dart)
    │      └─ クーポン入力、適用、解除UI
    │
    ├─ ButtonView (widget/button_view.dart)
    │      └─ 一括削除ボタン
    │
    ├─ PriceDetailView (widget/price_detail_view.dart)
    │      └─ 価格詳細表示（小計、税金、割引、合計）
    │
    └─ ProceedView (widget/proceed_view.dart)
           └─ Proceed/Updateボタン
```

### データフロー図

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

画面表示 ────────────────▶ FetchCartDataEvent
                              │
                              ▼
                         CartScreenRepositoryImp.getCartData()
                              │
                              ▼
                         ApiClient.getCartDetails()
                              │
                              ▼
                         GraphQL API
                              │
                              ▼
                         CartModel ────────────────▶ カート画面表示
                                                    GlobalData.cartCountController更新

数量変更 ────────────────▶ UpdateCartEvent
                              │
                              ▼
                         CartScreenRepositoryImp.updateItemToCart()
                              │
                              ▼
                         AddToCartModel ───────────▶ 成功通知、カート再取得

商品削除 ────────────────▶ RemoveCartItemEvent
                              │
                              ▼
                         CartScreenRepositoryImp.removeFromCart()
                              │
                              ▼
                         AddToCartModel ───────────▶ 成功通知、カート再取得

クーポン適用 ────────────▶ AddCouponCartEvent
                              │
                              ▼
                         CartScreenRepositoryImp.addCoupon()
                              │
                              ▼
                         ApplyCoupon ──────────────▶ 成功/エラー通知、カート再取得
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| cart_screen.dart | `lib/screens/cart_screen/cart_screen.dart` | ソース | カート画面UI |
| cart_screen_bloc.dart | `lib/screens/cart_screen/bloc/cart_screen_bloc.dart` | ソース | 状態管理BLoC |
| cart_screen_event.dart | `lib/screens/cart_screen/bloc/cart_screen_event.dart` | ソース | イベント定義 |
| cart_screen_state.dart | `lib/screens/cart_screen/bloc/cart_screen_state.dart` | ソース | 状態定義 |
| cart_screen_repository.dart | `lib/screens/cart_screen/bloc/cart_screen_repository.dart` | ソース | APIリポジトリ |
| cart_data_model.dart | `lib/screens/cart_screen/cart_model/cart_data_model.dart` | ソース | カートデータモデル |
| add_to_cart_model.dart | `lib/screens/cart_screen/cart_model/add_to_cart_model.dart` | ソース | カート操作結果モデル |
| apply_coupon.dart | `lib/screens/cart_screen/cart_model/apply_coupon.dart` | ソース | クーポン結果モデル |
| cart_list_item.dart | `lib/screens/cart_screen/widget/cart_list_item.dart` | ソース | カートアイテムWidget |
| apply_coupon_view.dart | `lib/screens/cart_screen/widget/apply_coupon_view.dart` | ソース | クーポン入力Widget |
| button_view.dart | `lib/screens/cart_screen/widget/button_view.dart` | ソース | ボタンWidget |
| price_detail_view.dart | `lib/screens/cart_screen/widget/price_detail_view.dart` | ソース | 価格詳細Widget |
| proceed_view.dart | `lib/screens/cart_screen/widget/proceed_view.dart` | ソース | Proceedボタン Widget |
