# 機能設計書 28-商談一覧表示

## 概要

本ドキュメントは、Fat Free CRMシステムにおける商談一覧表示機能の設計を定義する。この機能は、ユーザーがアクセス可能な商談を一覧表示し、ステージによるフィルタリングやCSV/XLS形式でのエクスポートを提供する。

### 本機能の処理概要

商談一覧表示機能は、営業パイプラインを可視化し、商談の進捗状況を一元管理するための機能である。

**業務上の目的・背景**：営業チームが進行中の商談を把握し、売上予測や営業活動の優先順位付けを行うために必要な機能である。商談をステージ別にフィルタリングすることで、パイプラインの各段階にある商談数や金額を確認できる。また、CSV/XLS形式でのエクスポートにより、外部ツールでの分析やレポート作成が可能である。

**機能の利用シーン**：営業マネージャーがチームの商談状況を確認する場合、営業担当者が自分の商談リストを管理する場合、売上予測レポートを作成する場合、特定ステージの商談に絞って分析する場合に利用される。

**主要な処理内容**：
1. ユーザーがアクセス可能な商談の取得
2. ステージによるフィルタリング
3. ソート（名前、金額、確度、クローズ予定日等）
4. ページネーション
5. CSV/XLS形式でのエクスポート
6. 重み付け金額の算出

**関連システム・外部連携**：CSV/XLSエクスポートにより、Excelやスプレッドシートツールとの連携が可能である。

**権限による制御**：CanCanによる認可制御が行われ、ユーザーは自分が作成した商談、自分に割り当てられた商談、または共有された商談のみ閲覧可能である。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 24 | 商談一覧画面 | 主画面 | 商談一覧表示、ステージフィルタリング、CSV/XLSエクスポート |
| 24 | 商談一覧画面 | 補助機能 | 高度な検索（機能No.81） |
| 24 | 商談一覧画面 | 補助機能 | 商談削除（機能No.32） |
| 6 | ホーム画面（ダッシュボード） | 補助機能 | 自分の商談をダッシュボードに表示 |

## 機能種別

データ参照（READ） / 一覧表示 / エクスポート処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| stage | String | No | フィルタリングするステージ | 設定値リストから選択 |
| page | Integer | No | ページ番号 | 正の整数 |
| per_page | Integer | No | 1ページあたりの表示件数 | 1-200の整数 |
| sort_by | String | No | ソート項目 | name/amount/probability/closes_on/created_at/updated_at |
| query | String | No | 検索キーワード | 任意文字列 |
| q | Hash | No | Ransack検索パラメータ | Ransack形式 |

### 入力データソース

- URL: GET `/opportunities`
- HTTPメソッド: GET
- データソース: opportunitiesテーブル

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| @opportunities | Array | 商談オブジェクトの配列 |
| @opportunity_stage_total | Hash | ステージ別商談件数 |
| @stage | Array | ステージ設定の配列 |
| @search_results_count | Integer | 検索結果の総件数 |

### 出力先

- HTML形式: 一覧画面表示（index.html.haml）
- JS形式: AJAX更新用（index.js.haml）
- CSV形式: CSVファイルダウンロード
- XLS形式: Excelファイルダウンロード

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ GET /opportunities を受信
2. 認証・認可チェック
   └─ CanCanによる権限確認
3. 設定読み込み
   └─ load_settingsでステージ設定を読み込み
4. パラメータ設定
   └─ set_paramsでper_page、sort_by、stageを設定
5. 商談データ取得
   └─ get_opportunitiesでフィルタ、検索、ソート、ページネーション適用
6. サイドバーデータ取得
   └─ get_data_for_sidebarでステージ別件数を取得
7. レスポンス生成
   └─ フォーマットに応じた出力
```

### フローチャート

```mermaid
flowchart TD
    A[GET /opportunities] --> B[認証チェック]
    B --> C{認可チェック}
    C -->|許可| D[load_settings]
    C -->|拒否| E[403 Forbidden]
    D --> F[set_params]
    F --> G[get_opportunities]
    G --> H[get_data_for_sidebar]
    H --> I{フォーマット判定}
    I -->|HTML| J[一覧画面表示]
    I -->|JS| K[AJAX応答]
    I -->|CSV| L[CSVダウンロード]
    I -->|XLS| M[Excelダウンロード]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-28-01 | アクセス権限チェック | ユーザーは自分が作成、割当て、または共有された商談のみ閲覧可能 | 常時 |
| BR-28-02 | デフォルトソート | ソート未指定時はcreated_at DESC | ソート未指定時 |
| BR-28-03 | デフォルト表示件数 | 1ページあたり20件 | per_page未指定時 |
| BR-28-04 | ステージフィルタ | セッションにstageフィルタを保存し、リクエスト間で維持 | フィルタ適用時 |
| BR-28-05 | 重み付けソート | amount*probabilityでソート可能 | weighted_sortスコープ使用時 |
| BR-28-06 | エクスポート時ページネーション無効 | CSV/XLS形式の場合、全件出力 | エクスポート時 |

### 計算ロジック

重み付け金額の計算:
- `weighted_amount = (amount - discount) * probability / 100`

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 商談一覧取得 | opportunities | SELECT | フィルタ/検索条件に基づく商談一覧取得 |
| 取引先情報取得 | accounts | SELECT | 商談に紐付く取引先情報を結合取得 |
| ユーザー情報取得 | users | SELECT | 商談に紐付くユーザー情報を結合取得 |
| タグ情報取得 | tags/taggings | SELECT | 商談に紐付くタグ情報を取得 |

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

#### opportunities

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | my(current_user) | ユーザーアクセス可能な商談 |
| SELECT | * | stage IN (フィルタ) | ステージフィルタ適用 |
| SELECT | * | ORDER BY sort_by | ソート適用 |
| SELECT | *, amount*probability | weighted_sortスコープ | 重み付け金額を追加 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 403 | Forbidden | アクセス権限がない | エラーページ表示 |

### リトライ仕様

リトライは実装されていない（参照操作のため不要）

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

参照操作のみのためトランザクション制御は不要

## パフォーマンス要件

- 一覧画面のレスポンスタイム: 2秒以内
- CSVエクスポート（1000件）: 5秒以内

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

- CanCanによる認可制御でアクセス権限をチェック
- XSS対策としてビューでのエスケープ処理
- エクスポートファイルには閲覧権限のあるデータのみ含まれる

## 備考

- includes(:account, :user, :tags)でN+1問題を回避
- サイドバーにステージ別件数を表示

---

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

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

### 推奨読解順序

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

商談のデータ構造とスコープを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | opportunity.rb | `app/models/entities/opportunity.rb` | 商談モデルの属性、関連、スコープ |

**読解のコツ**:
- **8-27行目**: Schema Informationでテーブル構造を確認
- **43-52行目**: scope定義（state, won, lost, pipeline等）
- **53行目**: weighted_sortスコープ - amount*probabilityで選択
- **79行目**: sortable定義 - ソート可能な項目と順序
- **103-105行目**: weighted_amountメソッド - 重み付け金額の計算

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

コントローラーのindexアクションがエントリーポイントである。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | opportunities_controller.rb | `app/controllers/entities/opportunities_controller.rb` | indexアクションの実装 |
| 2-2 | entities_controller.rb | `app/controllers/entities_controller.rb` | 基底クラスの共通処理 |

**主要処理フロー**:
1. **9行目**: before_action :load_settings - ステージ設定読み込み
2. **10行目**: before_action :get_data_for_sidebar - サイドバーデータ取得
3. **11行目**: before_action :set_params - パラメータ設定
4. **15-21行目** (indexアクション): get_opportunities呼び出し、CSV/XLS応答
5. **162-164行目**: order_by_attributesオーバーライド - weighted_sort適用
6. **167行目**: get_opportunities = get_list_of_records（エイリアス）

#### Step 3: ビューを理解する

一覧画面の構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.html.haml | `app/views/opportunities/index.html.haml` | 一覧画面のレイアウト |

**主要処理フロー**:
- **5行目**: .remote#create_opportunity - 新規作成フォームの表示領域
- **11-15行目**: 商談一覧の表示（@opportunities.anyで条件分岐）
- **17-18行目**: ページネーションとエクスポートボタン

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

```
OpportunitiesController#index
    │
    ├─ load_settings (before_action)
    │      └─ @stage = Setting.unroll(:opportunity_stage)
    │
    ├─ set_params (before_action)
    │      ├─ per_page設定
    │      ├─ sort_by設定
    │      └─ stage(フィルタ)設定
    │
    ├─ get_opportunities
    │      │
    │      └─ get_list_of_records (EntitiesController)
    │             ├─ entities.merge(ransack_search.result)
    │             ├─ scope.state(filter)
    │             ├─ scope.text_search(query)
    │             ├─ scope.tagged_with(tags)
    │             ├─ order_by_attributes → weighted_sort.order
    │             └─ scope.paginate(page, per_page)
    │
    ├─ get_data_for_sidebar (before_action)
    │      └─ ステージ別件数を集計
    │
    └─ respond_with(@opportunities)
           ├─ format.html → index.html.haml
           ├─ format.js → index.js.haml
           ├─ format.csv → CSV生成
           └─ format.xls → Excel生成
```

### データフロー図

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

GET /opportunities ───▶ OpportunitiesController#index ───▶ HTML/JS/CSV/XLS
      │                        │
      │                        ├─▶ Opportunity.my(current_user)
      │                        │       └─▶ [opportunities]テーブル
      │                        │
      │                        ├─▶ ransack_search.result
      │                        │       └─▶ Ransack検索実行
      │                        │
      │                        ├─▶ weighted_sort
      │                        │       └─▶ SELECT *, amount*probability
      │                        │
      │                        └─▶ includes(:account, :user, :tags)
      │                                ├─▶ [accounts]テーブル
      │                                ├─▶ [users]テーブル
      │                                └─▶ [tags]テーブル
      │
      └─▶ index.html.haml
              ├─▶ _opportunity.html.haml (各行)
              ├─▶ _sidebar_index.html.haml
              └─▶ shared/_paginate.html.haml
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| opportunities_controller.rb | `app/controllers/entities/opportunities_controller.rb` | コントローラー | 商談操作のエントリーポイント |
| entities_controller.rb | `app/controllers/entities_controller.rb` | コントローラー | エンティティ共通処理の基底クラス |
| opportunity.rb | `app/models/entities/opportunity.rb` | モデル | 商談のデータモデル定義 |
| opportunities_helper.rb | `app/helpers/opportunities_helper.rb` | ヘルパー | 商談表示用ヘルパーメソッド |
| index.html.haml | `app/views/opportunities/index.html.haml` | ビュー | 一覧画面のメインテンプレート |
| index.js.haml | `app/views/opportunities/index.js.haml` | ビュー | AJAX応答用テンプレート |
| _opportunity.html.haml | `app/views/opportunities/_opportunity.html.haml` | ビュー | 商談行のパーシャル |
| _sidebar_index.html.haml | `app/views/opportunities/_sidebar_index.html.haml` | ビュー | サイドバーパーシャル |
| index.xls.builder | `app/views/opportunities/index.xls.builder` | ビュー | Excelエクスポートテンプレート |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義 |
