# 画面設計書 12-カート画面

## 概要

本ドキュメントは、Bagisto eコマースモバイルアプリにおけるカート画面の設計仕様を定義するものです。カート画面は、ユーザーが購入予定の商品を管理し、チェックアウトへ進むための中核となる画面です。

### 本画面の処理概要

カート画面は、ユーザーがショッピングカートに追加した商品の一覧表示、数量変更、削除、クーポン適用などの操作を行い、購入手続き（チェックアウト）へ進むための画面です。

**業務上の目的・背景**：eコマースにおいてカート機能は購買プロセスの要です。ユーザーが興味を持った商品を一時的に保管し、購入前に内容を確認・調整できる場を提供します。カート画面では、商品の数量変更、不要商品の削除、クーポンによる割引適用、合計金額の確認など、購入決定前の最終調整を行います。また、商品をウィッシュリストに移動させて後で購入する選択肢も提供します。

**画面へのアクセス方法**：
- ボトムナビゲーションのカートアイコンをタップ
- 各画面のAppBarにあるカートアイコン（バッジ付き）をタップ
- 商品詳細画面から「カートに追加」後にカート画面へ遷移
- ウィッシュリスト画面から「カートへ移動」操作後に遷移

**主要な操作・処理内容**：
1. カート内商品の一覧表示（商品画像、名前、価格、数量）
2. 商品数量の増減（＋/−ボタン）
3. 個別商品のカートからの削除
4. 全商品のカートからの一括削除
5. クーポンコードの適用・削除
6. 商品をウィッシュリストへ移動
7. 小計、割引、税金、送料、合計金額の表示
8. チェックアウト画面への遷移
9. Pull-to-Refreshによるカートデータ更新

**画面遷移**：
- 遷移元：ホーム画面、商品詳細画面、ウィッシュリスト画面、比較画面、各画面のAppBar
- 遷移先：チェックアウト画面、商品詳細画面

**権限による表示制御**：ゲストユーザーもカート機能を利用可能ですが、チェックアウト時にはログインまたはゲストチェックアウトの選択が必要です。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 19 | カート表示 | 主機能 | カート内商品一覧と合計金額の表示 |
| 21 | カート数量更新 | 補助機能 | カート内商品の数量変更処理 |
| 22 | カートから削除 | 補助機能 | カート内の個別商品削除処理 |
| 23 | カート全削除 | 補助機能 | カート内の全商品一括削除処理 |
| 24 | クーポン適用 | 補助機能 | 割引クーポンコードの適用処理 |
| 25 | クーポン削除 | 補助機能 | 適用済みクーポンの削除処理 |
| 26 | ウィッシュリストへ移動 | 補助機能 | カート内商品をウィッシュリストに移動する処理 |
| 36 | チェックアウト画面 | 遷移先機能 | カートからチェックアウト画面へ遷移 |

## 画面種別

一覧 / 編集

## URL/ルーティング

```dart
const String cartScreen = "cartScreen";
```

ルート定義：`Navigator.pushNamed(context, cartScreen)`

## 入出力項目

| 項目名 | 入力/出力 | 型 | 必須 | 説明 |
|--------|----------|-----|------|------|
| - | - | - | - | 本画面は入力パラメータなしで遷移可能 |

## 表示項目

| No | 項目名 | 型 | 表示条件 | 説明 |
|----|--------|-----|----------|------|
| 1 | AppBar | CommonAppBar | 常時 | タイトル「カート」、戻るボタン |
| 2 | 商品リスト | CartListItem | 商品あり | カート内商品の一覧 |
| 3 | 商品画像 | Image | 商品あり | 各商品のサムネイル画像 |
| 4 | 商品名 | Text | 商品あり | 商品の名称 |
| 5 | 商品価格 | Text | 商品あり | 単価（フォーマット済み） |
| 6 | 数量コントロール | Widget | 商品あり | ＋/−ボタンと数量表示 |
| 7 | 削除ボタン | IconButton | 商品あり | 商品削除アイコン |
| 8 | ウィッシュリスト移動ボタン | IconButton | 商品あり | ウィッシュリストへ移動 |
| 9 | クーポン入力欄 | ApplyCouponView | 常時 | クーポンコード入力と適用ボタン |
| 10 | 全削除ボタン | ButtonView | 商品あり | カート全削除ボタン |
| 11 | 価格詳細 | PriceDetailView | 商品あり | 小計、割引、税金、送料、合計 |
| 12 | 支払金額 | Text | 商品あり | 最終支払金額 |
| 13 | チェックアウトボタン | ProceedView | 商品あり | チェックアウトへ進むボタン |
| 14 | 空カート表示 | EmptyDataView | 商品0件 | カートが空の場合の表示 |
| 15 | ローディング | CartLoaderView | 読み込み中 | スケルトンローダー表示 |

## イベント仕様

### 1-カートデータ取得

**トリガー**：画面初期表示時、Pull-to-Refresh時

**処理フロー**：
1. `CartScreenBloc`に`FetchCartDataEvent`を発行
2. `CartScreenRepository.getCartData()`でAPIを呼び出し
3. 取得したデータを`CartModel`モデルに変換
4. `FetchCartDataState.success`を発行しUIを更新
5. カート数をグローバルに更新（`GlobalData.cartCountController.sink.add`）

### 2-数量更新

**トリガー**：数量変更後、一定時間経過またはチェックアウト押下時

**処理フロー**：
1. `UpdateCartEvent(items)`を発行
2. `CartScreenRepository.updateItemToCart(items)`でAPIを呼び出し
3. 成功時：`UpdateCartState.success`を発行
4. 成功通知「カートを更新しました」を表示
5. カートデータを再取得

### 3-商品削除

**トリガー**：商品の削除アイコン押下

**処理フロー**：
1. `RemoveCartItemEvent(cartItemId)`を発行
2. `CartScreenRepository.removeFromCart(cartItemId)`でAPIを呼び出し
3. 成功時：`RemoveCartItemState.success`を発行
4. ローカルのカートリストから該当商品を削除
5. カート数を更新、成功通知を表示

### 4-全商品削除

**トリガー**：全削除ボタン押下

**処理フロー**：
1. `RemoveAllCartItemEvent`を発行
2. `CartScreenRepository.removeAllCartItem()`でAPIを呼び出し
3. 成功時：`RemoveAllCartItemState.success`を発行
4. カート数を0に更新
5. 成功通知を表示

### 5-クーポン適用

**トリガー**：クーポンコード入力後、適用ボタン押下

**処理フロー**：
1. `AddCouponCartEvent(code)`を発行
2. `CartScreenRepository.addCoupon(code)`でAPIを呼び出し
3. 成功時：`AddCouponState.success`を発行、カートデータ再取得
4. 失敗時：エラーメッセージを表示

### 6-クーポン削除

**トリガー**：適用済みクーポンの削除ボタン押下

**処理フロー**：
1. `RemoveCouponCartEvent`を発行
2. `CartScreenRepository.removeCoupon()`でAPIを呼び出し
3. 成功時：`RemoveCouponCartState.success`を発行
4. カートデータ再取得

### 7-ウィッシュリストへ移動

**トリガー**：ウィッシュリスト移動アイコン押下

**処理フロー**：
1. `MoveToCartEvent(id)`を発行（メソッド名は逆だがウィッシュリストへ移動）
2. `CartScreenRepository.moveToWishlist(id)`でAPIを呼び出し
3. 成功時：`MoveToCartState.success`を発行
4. 成功通知「ウィッシュリストに追加しました」を表示
5. カートデータ再取得

### 8-チェックアウトへ進む

**トリガー**：チェックアウトボタン押下

**処理フロー**：
1. 数量変更がある場合は先にカート更新処理を実行
2. ダウンロード可能商品のみか判定
3. `Navigator.pushNamed(context, checkoutScreen)`で遷移
4. 引数としてCartModel、合計金額、ダウンロード商品フラグを渡す

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| カート取得 | cart, cart_items | SELECT | カートと商品情報を取得 |
| 数量更新 | cart_items | UPDATE | 商品数量を更新 |
| 商品削除 | cart_items | DELETE | 指定商品をカートから削除 |
| 全削除 | cart_items | DELETE | カート内全商品を削除 |
| クーポン適用 | cart | UPDATE | クーポンコードと割引額を更新 |
| クーポン削除 | cart | UPDATE | クーポンコードをクリア |
| ウィッシュリスト移動 | cart_items, wishlist_items | DELETE/INSERT | カートから削除、ウィッシュリストに追加 |

### テーブル別更新項目詳細

#### cart

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, coupon_code, items_count, items_qty, grand_total, tax_total | customer_id/session_idで絞り込み | メインカート情報 |
| UPDATE | coupon_code, discount_amount | クーポン適用/削除時 | 割引計算はサーバー側 |

#### cart_items

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, name, quantity, price, product | cart_idで絞り込み | 商品一覧取得 |
| UPDATE | quantity | item_idで特定 | 数量変更 |
| DELETE | - | item_idで特定 | 個別削除 |
| DELETE | - | cart_idで絞り込み | 全削除 |

## メッセージ仕様

| No | メッセージID | メッセージ内容 | 表示タイミング | 種別 |
|----|------------|---------------|--------------|------|
| 1 | emptyCartPageLabel | カートに商品がありません | カート空時 | 情報 |
| 2 | addedToWishlist | ウィッシュリストに追加しました | 移動成功時 | 成功 |
| 3 | updateCartSuccess | カートを更新しました | 数量更新成功時 | 成功 |
| 4 | somethingWrong | 問題が発生しました | 各種エラー時 | エラー |
| 5 | - | （APIからのメッセージ） | クーポン適用/削除成功時 | 成功 |
| 6 | - | （APIからのエラーメッセージ） | クーポン適用失敗時 | エラー |

## 例外処理

| No | 例外状況 | 対応処理 |
|----|---------|---------|
| 1 | カートが空 | `EmptyDataView`を表示（emptyCart画像付き） |
| 2 | API通信エラー | `ErrorMessage.errorMsg`を表示 |
| 3 | クーポンコード無効 | APIからのエラーメッセージを表示 |
| 4 | 在庫切れ商品あり | 数量更新時にエラーメッセージ表示 |
| 5 | セッション期限切れ | カートデータが空で返却される |

## 備考

- `CommonAppBar`でカートアイコンを非表示に設定（isInCartScreen: true）
- Pull-to-Refreshでカートデータの再取得が可能
- 数量変更は即時反映ではなく、状態管理で変更をトラッキング
- ダウンロード商品のみの場合、配送ステップをスキップ
- 価格表示はフォーマット済み（`FormattedPrice`モデル）
- 税率は適用税率一覧として詳細表示可能（`AppliedTaxRate`）

---

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

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

### 推奨読解順序

#### 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` | カート操作結果のモデル |
| 1-3 | apply_coupon.dart | `lib/screens/cart_screen/cart_model/apply_coupon.dart` | クーポン適用結果のモデル |

**読解のコツ**: `FormattedPrice`には小計、割引、税金、送料、合計が含まれる。`Items`には商品詳細（`Product`）が含まれる。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | cart_screen.dart | `lib/screens/cart_screen/cart_screen.dart` | CartScreenクラス、initStateでのBloc初期化 |

**主要処理フロー**:
1. **行31-34**: initStateでBlocを取得し、`FetchCartDataEvent`を発行
2. **行46-57**: buildUIメソッドでScaffold構築
3. **行61-121**: BlocConsumerでイベント処理とUI更新
4. **行210-234**: カートが空の場合のEmptyDataView表示
5. **行237-293**: カートアイテム表示（CartListItem、ApplyCouponView、ButtonView、PriceDetailView、ProceedView）

#### Step 3: 状態管理（BLoC）を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | cart_screen_bloc.dart | `lib/screens/cart_screen/bloc/cart_screen_bloc.dart` | イベント処理の実装 |
| 3-2 | cart_screen_event.dart | `lib/screens/cart_screen/bloc/cart_screen_event.dart` | 各イベントクラスの定義 |
| 3-3 | cart_screen_state.dart | `lib/screens/cart_screen/bloc/cart_screen_state.dart` | 各状態クラスの定義 |

**主要処理フロー**:
- **行22-28**: FetchCartDataEvent処理
- **行29-37**: UpdateCartEvent処理（数量更新）
- **行38-47**: RemoveCartItemEvent処理（個別削除）
- **行48-56**: RemoveAllCartItemEvent処理（全削除）
- **行57-69**: AddCouponCartEvent処理
- **行70-82**: RemoveCouponCartEvent処理
- **行83-98**: MoveToCartEvent処理（ウィッシュリストへ移動）

#### Step 4: API通信（Repository）を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | cart_screen_repository.dart | `lib/screens/cart_screen/bloc/cart_screen_repository.dart` | API呼び出しの実装 |

#### Step 5: UIコンポーネントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | cart_item.dart | `lib/screens/cart_screen/widget/cart_item.dart` | 商品アイテム表示 |
| 5-2 | apply_coupon_view.dart | `lib/screens/cart_screen/widget/apply_coupon_view.dart` | クーポン入力UI |
| 5-3 | button_view.dart | `lib/screens/cart_screen/widget/button_view.dart` | 全削除ボタン |
| 5-4 | price_details_view.dart | `lib/screens/cart_screen/widget/price_details_view.dart` | 価格詳細表示 |
| 5-5 | proceed_view.dart | `lib/screens/cart_screen/widget/proceed_view.dart` | チェックアウトボタン |
| 5-6 | cart_loader_screen.dart | `lib/screens/cart_screen/widget/cart_loader_screen.dart` | ローディング表示 |

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

```
CartScreen (cart_screen.dart)
    │
    ├─ CartScreenBloc (cart_screen_bloc.dart)
    │      │
    │      ├─ FetchCartDataEvent
    │      │      └─ CartScreenRepository.getCartData()
    │      │
    │      ├─ UpdateCartEvent
    │      │      └─ CartScreenRepository.updateItemToCart()
    │      │
    │      ├─ RemoveCartItemEvent
    │      │      └─ CartScreenRepository.removeFromCart()
    │      │
    │      ├─ RemoveAllCartItemEvent
    │      │      └─ CartScreenRepository.removeAllCartItem()
    │      │
    │      ├─ AddCouponCartEvent
    │      │      └─ CartScreenRepository.addCoupon()
    │      │
    │      ├─ RemoveCouponCartEvent
    │      │      └─ CartScreenRepository.removeCoupon()
    │      │
    │      └─ MoveToCartEvent
    │             └─ CartScreenRepository.moveToWishlist()
    │
    └─ UI Components
           ├─ CartListItem (cart_item.dart)
           ├─ ApplyCouponView (apply_coupon_view.dart)
           ├─ ButtonView (button_view.dart)
           ├─ PriceDetailView (price_details_view.dart)
           └─ ProceedView (proceed_view.dart)
```

### データフロー図

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

ユーザー操作 ─────▶ CartScreenBloc ─────▶ UI更新
   │                     │
   │                     ├─ CartScreenRepository
   │                     │         │
   │                     │         └─ GraphQL API
   │                     │
   │                     └─ State発行
   │                            │
   └───────────────────────────▶ BlocConsumer
                                      │
                                      └─ Widget再描画
                                             │
                                             └─ GlobalData.cartCountController
                                                      │
                                                      └─ 全画面のカートバッジ更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| cart_screen.dart | `lib/screens/cart_screen/cart_screen.dart` | ソース | メイン画面ウィジェット |
| 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_item.dart | `lib/screens/cart_screen/widget/cart_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_details_view.dart | `lib/screens/cart_screen/widget/price_details_view.dart` | ソース | 価格詳細Widget |
| proceed_view.dart | `lib/screens/cart_screen/widget/proceed_view.dart` | ソース | チェックアウトWidget |
| cart_loader_screen.dart | `lib/screens/cart_screen/widget/cart_loader_screen.dart` | ソース | ローダーWidget |
| cart_index.dart | `lib/screens/cart_screen/utils/cart_index.dart` | ソース | エクスポート集約 |
| check_downlodable.dart | `lib/screens/cart_screen/utils/check_downlodable.dart` | ソース | ダウンロード商品判定 |
| route_constants.dart | `lib/utils/route_constants.dart` | 設定 | ルート定数定義 |
