# 通知設計書 10-クーポン適用成功/失敗通知

## 概要

本ドキュメントは、Bagisto ECモバイルアプリにおけるクーポンコード適用の成否を通知する機能の設計書である。

### 本通知の処理概要

本通知は、ユーザーがカート画面でクーポンコードを適用または削除した際に、その操作の成否を緑色（成功）または赤色（失敗）のオーバーレイ通知で画面上部に表示し、結果をフィードバックする機能を提供する。

**業務上の目的・背景**：ECアプリにおいて、クーポンやプロモーションコードの適用は購買促進の重要な機能である。ユーザーがクーポンコードを入力した際、そのコードが有効かどうか、正しく適用されたかどうかを即座にフィードバックすることで、ユーザーは割引が適用されたことを確認し、安心して購入手続きに進むことができる。無効なコードの場合も明確にエラーを伝え、正しいコードの入力を促す。

**通知の送信タイミング**：カート画面でクーポンコードを入力し「適用」ボタンをタップした後、サーバーへのリクエストが完了した直後。BLoCパターンにおいて `AddCouponState.success` または `AddCouponState.fail` が emit された時点、または `RemoveCouponCartState.success` / `RemoveCouponCartState.fail` が emit された時点で通知が表示される。

**通知の受信者**：カート画面でクーポン操作を実行したユーザー本人。

**通知内容の概要**：成功時は「Success」タイトルとサーバーからの成功メッセージ（緑色）。失敗時は「Failed」タイトルとエラーメッセージ（赤色）。クーポン削除時も同様のパターン。

**期待されるアクション**：成功時はユーザーが割引額を確認し、チェックアウトに進む。失敗時はクーポンコードを再確認し、正しいコードを入力し直す。

## 通知種別

アプリ内通知（オーバーレイ通知 / 成功通知・エラー通知）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（BLoC状態変化時に即座表示） |
| 優先度 | 中 |
| リトライ | なし |

### 送信先決定ロジック

1. カート画面でクーポン操作を実行したユーザーに対してのみ表示
2. `BlocConsumer` の `listener` で状態変化を検出
3. `AddCouponState` / `RemoveCouponCartState` の status に応じて適切な通知を表示

## 通知テンプレート

### 成功通知の場合

| 項目 | 内容 |
|-----|------|
| 表示位置 | 画面上部 |
| 背景色 | Colors.green.shade400 |
| アイコン | Icons.check_circle_outline（白色） |
| 消去方向 | 上方向スワイプ |

### 失敗通知の場合

| 項目 | 内容 |
|-----|------|
| 表示位置 | 画面上部 |
| 背景色 | Colors.red |
| アイコン | Icons.cancel_outlined（白色） |
| 消去方向 | 上方向スワイプ |

### 本文テンプレート

```
【クーポン適用成功】
タイトル: "Success" (StringConstants.success.localized())
サブタイトル: {baseModel.message} - サーバーからの成功メッセージ

【クーポン適用失敗】
タイトル: "Failed" (StringConstants.failed.localized())
サブタイトル: {error} - エラーメッセージ

【クーポン削除成功】
タイトル: "Success"
サブタイトル: {baseModel.message}

【クーポン削除失敗】
タイトル: "Failed"
サブタイトル: {error}
```

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| message | 成功メッセージ | ApplyCoupon.message | Yes（空文字許容） |
| error | エラーメッセージ | State.error | Yes（空文字許容） |
| context | ビルドコンテキスト | Flutter Widget | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 画面操作 | クーポン適用成功 | AddCouponState.status == CartStatus.success | cart_screen.dart 行83-88 |
| 画面操作 | クーポン適用失敗 | AddCouponState.status == CartStatus.fail | cart_screen.dart 行83-85 |
| 画面操作 | クーポン削除成功 | RemoveCouponCartState.status == CartStatus.success | cart_screen.dart 行91-97 |
| 画面操作 | クーポン削除失敗 | RemoveCouponCartState.status == CartStatus.fail | cart_screen.dart 行91-93 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| なし | 成功・失敗いずれの場合も通知を表示 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[クーポンコード入力] --> B[適用ボタンタップ]
    B --> C[AddCouponCartEvent発行]
    C --> D[CartScreenBloc処理]
    D --> E[repository.addCoupon呼び出し]
    E --> F{API結果}
    F -->|success: true| G[AddCouponState.success emit]
    F -->|success: false| H[AddCouponState.fail emit]
    F -->|Exception| I[AddCouponState.fail emit]
    G --> J[BlocConsumer listener検出]
    H --> J
    I --> J
    J --> K{status判定}
    K -->|success| L[ShowMessage.successNotification]
    K -->|fail| M[ShowMessage.errorNotification]
    L --> N[緑色オーバーレイ表示]
    M --> O[赤色オーバーレイ表示]
    N --> P[カート画面リフレッシュ]
    O --> Q[終了・再入力待ち]
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| coupons（サーバー側） | クーポンコードの検証 | API経由 |
| cart（サーバー側） | カートへのクーポン適用可否確認 | API経由 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| cart（サーバー側） | UPDATE | クーポンコードの関連付け |

#### 送信後の状態更新

| 操作 | 項目 | 更新値 | 備考 |
|-----|-----|-------|------|
| fetchCartData | カート全体 | 更新後のカートデータ | 行194（適用後）, 204（削除後） |
| 削除時 | _cartDetailsModel.couponCode | null | 行73 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 無効なクーポン | コードが存在しない/期限切れ | errorNotification表示（API message） |
| 適用条件未達 | 最低購入金額未満など | errorNotification表示（API message） |
| 既に適用済み | 同一クーポン重複適用 | errorNotification表示（API message） |
| Exception | ネットワークエラー等 | errorNotification表示（e.toString()） |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | なし |
| 1日あたり上限 | なし |

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

- クーポンコードの検証はサーバー側で実施（クライアント側での改ざん防止）
- 他ユーザーのクーポンは使用不可（サーバー側で検証）
- クーポンの適用回数制限はサーバー側で管理

## 備考

- クーポン適用成功後、自動的にカートデータを再取得（`fetchCartData()`）
- APIレスポンスの `success` フラグでBLoC側でも判定（行61-66, 73-78）
- クーポン削除時は `_cartDetailsModel.couponCode` を明示的にnullに設定
- 適用成功時はカート画面のクーポン入力欄にコードが表示される

---

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

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

### 推奨読解順序

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

クーポン適用のデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | cart_screen_state.dart | `lib/screens/cart_screen/bloc/cart_screen_state.dart` | AddCouponState, RemoveCouponCartStateの構造 |
| 1-2 | apply_coupon.dart | `lib/screens/cart_screen/cart_model/apply_coupon.dart` | ApplyCouponモデルのsuccess, messageプロパティ |

**読解のコツ**: クーポン操作では `ApplyCoupon` モデルを使用し、`success` フラグでAPI成否を判定する点に注目。

#### Step 2: イベント発行を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | cart_screen_event.dart | `lib/screens/cart_screen/bloc/cart_screen_event.dart` | AddCouponCartEvent, RemoveCouponCartEventの定義 |

**主要処理フロー**:
- **行23-26**: `AddCouponCartEvent` クラス
- **行24**: `String? code` - クーポンコード
- **行17-21**: `RemoveCouponCartEvent` クラス
- **行18**: `CartModel? cartDetailsModel` - 現在のカートモデル

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | cart_screen_bloc.dart | `lib/screens/cart_screen/bloc/cart_screen_bloc.dart` | AddCouponCartEvent, RemoveCouponCartEvent処理 |

**主要処理フロー（クーポン適用）**:
- **行57-69**: `AddCouponCartEvent` のハンドリング
- **行59-60**: `repository?.addCoupon(event.code ?? "")` 呼び出し
- **行61-64**: `success == true` の場合 `AddCouponState.success` emit
- **行65-66**: `success != true` の場合 `AddCouponState.fail` emit（API message使用）
- **行67-69**: Exception時 `AddCouponState.fail` emit（e.toString()）

**主要処理フロー（クーポン削除）**:
- **行70-82**: `RemoveCouponCartEvent` のハンドリング
- **行72**: `repository?.removeCoupon()` 呼び出し
- **行73**: 削除前に `cartDetailsModel.couponCode = null` を設定
- **行74-77**: `success == true` の場合 `RemoveCouponCartState.success` emit
- **行78-79**: `success != true` の場合 `RemoveCouponCartState.fail` emit

#### Step 4: 通知表示を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | cart_screen.dart | `lib/screens/cart_screen/cart_screen.dart` | listenerでの通知処理 |

**主要処理フロー（クーポン適用）**:
- **行83-89**: `AddCouponState` の処理
- **行84-85**: 失敗時 `ShowMessage.errorNotification(state.error ?? "", context)`
- **行86-88**: 成功時 `ShowMessage.successNotification(state.baseModel?.message ?? "", context)`

**主要処理フロー（クーポン削除）**:
- **行91-98**: `RemoveCouponCartState` の処理
- **行92-93**: 失敗時 `ShowMessage.errorNotification(state.error ?? "", context)`
- **行94-96**: 成功時 `ShowMessage.successNotification(state.baseModel?.message ?? "", context)`

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

```
CartScreen（カート画面）
    |
    +-- ApplyCouponView（クーポン入力UI）
            |
            +-- 適用ボタンタップ
                    |
                    +-- cartScreenBloc.add(AddCouponCartEvent(code))
                            |
                            +-- CartScreenBloc.mapEventToState()
                                    |
                                    +-- repository.addCoupon(code)
                                            |
                                            +-- API呼び出し（GraphQL Mutation）
                                                    |
                                                    +-- ApplyCoupon レスポンス
                                                            |
                                                            +-- success判定
                                                                    |
                                                            ┌───────┴───────┐
                                                            ▼               ▼
                                                        success         fail
                                                            |               |
                                                            ▼               ▼
                                            AddCouponState.success  AddCouponState.fail
                                                            |               |
                                                            ▼               ▼
                                                successNotification  errorNotification
```

### データフロー図

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

クーポンコード入力 ──────▶ AddCouponCartEvent(code)
                                    |
                                    ▼
                            CartScreenBloc
                                    |
                                    ▼
                            repository.addCoupon(code)
                                    |
                                    ▼
                            GraphQL API呼び出し
                                    |
                                    ▼
                            ApplyCoupon レスポンス
                                    |
                            ┌───────┴───────┐
                            ▼               ▼
                    success: true     success: false
                            |               |
                            ▼               ▼
                AddCouponState.success  AddCouponState.fail
                            |               |
                            ▼               ▼
                successNotification     errorNotification
                            |               |
                            ▼               ▼
                    fetchCartData()    再入力待ち
                            |
                            ▼
                カート合計金額更新（割引反映）
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| cart_screen.dart | `lib/screens/cart_screen/cart_screen.dart` | ソース | カート画面UI・通知表示トリガー |
| cart_screen_bloc.dart | `lib/screens/cart_screen/bloc/cart_screen_bloc.dart` | ソース | クーポン操作のビジネスロジック |
| 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呼び出し |
| apply_coupon.dart | `lib/screens/cart_screen/cart_model/apply_coupon.dart` | モデル | クーポン適用レスポンスモデル |
| apply_coupon_view.dart | `lib/screens/cart_screen/widget/apply_coupon_view.dart` | ソース | クーポン入力UI |
| show_message.dart | `lib/widgets/show_message.dart` | ソース | 成功/エラー通知表示 |
