# 機能設計書 18-予約スロット取得

## 概要

本ドキュメントは、Eコマースモバイルアプリケーションにおける予約スロット取得機能の設計仕様を定義する。本機能は、予約可能商品（Booking商品）の利用可能な時間スロット情報を取得し、ユーザーが予約日時を選択できるようにする。

### 本機能の処理概要

**業務上の目的・背景**：
ECサイトでは物理的な商品だけでなく、サービス予約（美容院、レストラン、レンタル等）も取り扱う。予約商品においては、日付と時間スロットの選択が必要であり、リアルタイムの空き状況を提供することで、ダブルブッキングを防止し、スムーズな予約体験を実現する。

**機能の利用シーン**：
- 商品詳細画面で予約商品（Booking商品）の予約日時を選択する際
- 日付を選択した後、利用可能な時間スロットを表示する際
- レンタル商品の開始日時・終了日時を選択する際

**主要な処理内容**：
1. 予約商品ID（bookingId）と選択日（selectedDate）を基にスロット情報取得
2. 商品タイプに応じたスロット表示（default, rental, appointment, table, event）
3. 日付選択後の動的スロット更新
4. スロット選択結果の親コンポーネントへのコールバック

**関連システム・外部連携**：
- Bagisto GraphQL API（スロット情報取得API）

**権限による制御**：
- 認証不要（スロット情報の取得は誰でも可能）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 7 | 商品詳細画面 | 主画面 | 予約商品のスロット選択UI |

## 機能種別

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

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| bookingId | int | Yes | 予約商品ID | 正の整数 |
| selectedDate | String | Yes | 選択日（yyyy-MM-dd形式） | 日付形式 |

### 入力データソース

- ユーザー入力（DatePicker）
- 商品詳細データ（BookingProduct.id）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| bookingSlotsData | BookingSlotsData? | 予約スロット情報 |
| data | List<SlotGroup>? | スロットグループリスト |

### 出力先

- 画面表示（DropdownButtonFormField、スロット選択UI）
- BookingOptionViewのコールバック

## 処理フロー

### 処理シーケンス

```
1. 商品詳細画面でBooking商品を表示
   └─ BookingOptionViewを描画
2. ユーザーが日付を選択
   └─ showDatePickerで日付選択
3. スロット取得イベント発行
   └─ GetSlotEvent(bookingId, selectedDate)発行
4. API呼び出し
   └─ ProductScreenRepo.getSlot()
5. スロット表示
   └─ DropdownButtonFormFieldでスロット一覧表示
6. ユーザーがスロット選択
   └─ callBackで親コンポーネントに通知
```

### フローチャート

```mermaid
flowchart TD
    A[商品詳細画面] --> B{Booking商品?}
    B -->|Yes| C[BookingOptionView表示]
    B -->|No| D[通常商品表示]
    C --> E[日付選択UI表示]
    E --> F[ユーザーが日付選択]
    F --> G[GetSlotEvent発行]
    G --> H[ProductScreenRepo.getSlot]
    H --> I[GraphQL API]
    I --> J{成功?}
    J -->|Yes| K[BookingSlotsData取得]
    J -->|No| L[エラー処理]
    K --> M[スロット選択UI更新]
    M --> N[ユーザーがスロット選択]
    N --> O[callBackで親に通知]
    O --> P[selectedBooking更新]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 日付制限 | 選択可能日は本日から365日以内 | 日付選択時 |
| BR-02 | 商品タイプ別UI | default/rental/appointment/table/eventで異なるUI | Booking商品表示時 |
| BR-03 | スロット空き判定 | qty=trueのスロットのみ選択可能 | スロット表示時 |
| BR-04 | テーブル予約備考必須 | tableタイプはnoteフィールドが必須 | tableタイプ時 |
| BR-05 | レンタル期間選択 | rentalタイプはfromとtoの両方を選択 | rentalタイプ時 |

### 計算ロジック

- タイムスタンプ変換: `timeToMilliseconds(time, date)`で"HH:mm"形式とyyyy-MM-dd形式からUNIXタイムスタンプを計算

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| スロット取得 | booking_slots | SELECT | 指定日の利用可能スロット取得 |

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

#### booking_slots（GraphQL経由）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | 全フィールド | booking_product_id, date | GraphQL: getSlot |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | API失敗 | スロット取得エラー | GetSlotState.failでエラー処理 |
| - | 空スロット | 選択日に利用可能スロットなし | 「No slots available」メッセージ表示 |

### リトライ仕様

- 日付を再選択することで再取得可能

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

読み取り専用のため、トランザクション制御は不要。

## パフォーマンス要件

- スロット取得: 1秒以内

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

- スロット情報の取得に認証は不要

## 備考

- 予約タイプ: default（デフォルト予約）、rental（レンタル）、appointment（予約）、table（テーブル予約）、event（イベント）
- レンタルのrentingType: daily（日単位）、hourly（時間単位）、daily_hourly（両方）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | booking_slots_modal.dart | `lib/data_model/product_model/booking_slots_modal.dart` | BookingSlotsData, SlotGroup, BookingSlotの構造 |
| 1-2 | product_details_model.dart | `lib/screens/product_screen/data_model/product_details_model.dart` | BookingProduct, DefaultSlot, RentalSlot等の構造 |

**読解のコツ**:
- BookingSlotsData.dataにはSlotGroupのリストが含まれる
- SlotGroupにはfrom, to, timestamp, qty, time, slotsフィールドがある
- BookingSlotにはfrom, to, fromTimestamp, toTimestamp, qtyフィールドがある

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

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

**読解のコツ**: GetSlotEventにはbookingId（int）とselectedDate（String）が含まれる。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | product_page_bloc.dart | `lib/screens/product_screen/bloc/product_page_bloc.dart` | GetSlotEventの処理（147-156行目） |

**主要処理フロー**:
- **147行目**: event is GetSlotEventの判定
- **149行目**: repository.getSlot()呼び出し
- **150-151行目**: 成功時のGetSlotState.success発行
- **153-155行目**: エラー時のGetSlotState.fail発行

#### Step 4: View層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | booking_option_view.dart | `lib/screens/product_screen/view/booking_option_view.dart` | BookingOptionViewクラス |

**主要処理フロー（booking_option_view.dart）**:
1. **36-60行目**: initState()でselectedBookingの初期化
2. **101-138行目**: getSlotsForSelectedDate()で日付選択時にGetSlotEvent発行
3. **482-532行目**: レンタルタイプのrentingType選択（daily/hourly）
4. **537-644行目**: レンタル日単位の日付範囲選択
5. **645-844行目**: レンタル時間単位のスロット選択
6. **846-950行目**: default/appointmentタイプのスロット選択
7. **1063-1230行目**: tableタイプのスロット選択（note必須）
8. **1231-1238行目**: eventタイプの数量選択

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

```
ProductScreen (view/product_screen.dart)
    │
    └─ ProductView (view/widget/product_view.dart)
           │
           └─ ProductTypeView (view/widget/product_type_view.dart)
                  │
                  └─ BookingOptionView (view/booking_option_view.dart)
                         │
                         ├─ showDatePicker (日付選択)
                         │
                         ├─ GetSlotEvent発行
                         │      │
                         │      └─ ProductScreenBLoc
                         │             │
                         │             └─ ProductScreenRepo.getSlot()
                         │                    │
                         │                    └─ ApiClient.getSlot()
                         │                           │
                         │                           └─ GraphQL API
                         │
                         └─ DropdownButtonFormField (スロット選択)
                                │
                                └─ callBack(selectedBooking)
```

### データフロー図

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

日付選択 ────────────────▶ showDatePicker
                              │
                              ▼
                         selectedBooking["date"]更新
                              │
                              ▼
                         GetSlotEvent(bookingId, date)
                              │
                              ▼
                         ProductScreenBLoc
                              │
                              ▼
                         ProductScreenRepo.getSlot()
                              │
                              ▼
                         ApiClient.getSlot()
                              │
                              ▼
                         GraphQL API
                              │
                              ▼
                         BookingSlotsData ─────────▶ スロット選択UI表示

スロット選択 ────────────▶ onChanged
                              │
                              ▼
                         selectedBooking["slot"]更新
                              │
                              ▼
                         callBack(selectedBooking) ──▶ 親コンポーネント
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| booking_option_view.dart | `lib/screens/product_screen/view/booking_option_view.dart` | ソース | 予約オプションUI |
| booking_slots_modal.dart | `lib/data_model/product_model/booking_slots_modal.dart` | ソース | スロットデータモデル |
| product_page_bloc.dart | `lib/screens/product_screen/bloc/product_page_bloc.dart` | ソース | GetSlotEvent処理 |
| product_page_event.dart | `lib/screens/product_screen/bloc/product_page_event.dart` | ソース | GetSlotEvent定義 |
| product_page_state.dart | `lib/screens/product_screen/bloc/product_page_state.dart` | ソース | GetSlotState定義 |
| product_page_repository.dart | `lib/screens/product_screen/bloc/product_page_repository.dart` | ソース | getSlot()実装 |
| product_details_model.dart | `lib/screens/product_screen/data_model/product_details_model.dart` | ソース | BookingProduct等の定義 |
| event_booking.dart | `lib/screens/product_screen/view/widget/event_booking.dart` | ソース | イベントタイプUI |
| booking_product_availbility.dart | `lib/screens/product_screen/view/widget/booking_product_availbility.dart` | ソース | 空き状況表示 |
