# 機能設計書 33-商談サマリー（ユーザー別）

## 概要

本ドキュメントは、Fat Free CRMシステムにおける「商談サマリー（ユーザー別）」機能の設計を定義する。ユーザーごとの商談パイプラインを一覧表示するダッシュボード機能である。

### 本機能の処理概要

商談サマリー（ユーザー別）機能は、営業チーム全体の商談状況を俯瞰するためのレポート機能である。

**業務上の目的・背景**：営業マネージャーやチームリーダーが、チームメンバーの商談状況を一覧で把握し、営業進捗の管理や支援が必要なメンバーの特定を行うために必要な機能。各担当者に割り当てられた商談をパイプライン（won/lost 以外のステージ）でグループ化して表示することで、チーム全体の営業パフォーマンスを可視化する。また、未アサインの商談も表示することで、割り当て漏れを防止する。

**機能の利用シーン**：週次・月次の営業ミーティングでの進捗確認、マネージャーによるチームの商談状況確認、未アサイン商談の割り当て検討、売上予測のための現状把握、営業メンバーへのコーチング対象の特定。

**主要な処理内容**：
1. アサインされた商談を持つユーザー一覧の取得
2. ユーザーごとのパイプライン商談の取得
3. 未アサイン商談の取得
4. ユーザー別・金額順での商談表示

**関連システム・外部連携**：特になし。内部データの集計・表示機能。

**権限による制御**：CanCanCanによるアクセス権限チェックが行われる。ユーザーがアクセス可能な商談のみが表示対象となる。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 33 | 商談概要画面 | 主画面 | ユーザー別商談パイプラインサマリー表示 |

## 機能種別

レポート / データ集計・表示

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| なし | - | - | パラメータなしでアクセス | - |

### 入力データソース

- セッション情報（current_user）
- データベース（opportunitiesテーブル、usersテーブル）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| @users_with_opportunities | Array<User> | アサインされた商談を持つユーザー一覧 |
| @unassigned_opportunities | Array<Opportunity> | 未アサインの商談一覧 |

### 出力先

- 画面表示（HTML）

## 処理フロー

### 処理シーケンス

```
1. opportunities_overviewリクエスト受信
   └─ GET /users/opportunities_overview
2. アサイン商談を持つユーザー取得
   └─ User.have_assigned_opportunities
3. 未アサイン商談の取得
   └─ Opportunity.my(current_user).unassigned.pipeline
4. ビューのレンダリング
   └─ 各ユーザーごとの商談一覧表示
```

### フローチャート

```mermaid
flowchart TD
    A[サマリーリクエスト受信] --> B[認可チェック]
    B --> C{アクセス権あり?}
    C -->|No| D[403エラー]
    C -->|Yes| E[アサイン商談持ちユーザー取得]
    E --> F[未アサイン商談取得]
    F --> G{データあり?}
    G -->|Yes| H[ユーザー別商談表示]
    G -->|No| I[商談なしメッセージ表示]
    H --> J[未アサイン商談表示]
    J --> K[完了]
    I --> K
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-33-01 | パイプライン表示 | won/lost 以外のステージの商談のみ表示 | 常時 |
| BR-33-02 | 金額降順 | 商談は金額の降順で表示 | 常時 |
| BR-33-03 | 名前順ユーザー | ユーザーは名前順（first_name）で表示 | 常時 |
| BR-33-04 | アクセス制限 | ユーザーがアクセス可能な商談のみ表示 | 常時 |

### 計算ロジック

- パイプラインフィルタ: `stage IS NULL OR (stage != 'won' AND stage != 'lost')`
- 未アサイン: `assigned_to IS NULL`

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ユーザー取得 | users | SELECT | アサイン商談持ちユーザーの取得 |
| 商談取得 | opportunities | SELECT | ユーザー別商談の取得 |
| 商談取得 | opportunities | SELECT | 未アサイン商談の取得 |

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

#### users（have_assigned_opportunitiesスコープ）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | INNER JOIN opportunities ON users.id = opportunities.assigned_to | アサイン商談持ちユーザー |
| WHERE | stage | stage <> 'lost' AND stage <> 'won' | パイプラインのみ |

#### opportunities（unassigned.pipelineスコープ）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | assigned_to IS NULL | 未アサイン |
| WHERE | stage | stage IS NULL OR (stage != 'won' AND stage != 'lost') | パイプラインのみ |

## エラー処理

### エラーケース一覧

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

### リトライ仕様

特になし。

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

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

## パフォーマンス要件

- ページ表示: 2秒以内
- INNERJOINによる効率的なクエリ
- includesによる関連データの一括取得（N+1問題の回避）

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

- CanCanCanによるアクセス権限チェック（load_and_authorize_resource）
- my(current_user)スコープによるアクセス可能レコードのフィルタリング
- 他ユーザーの非公開商談は表示されない

## 備考

- 商談がない場合は「No opportunities found」メッセージを表示
- ユーザー別レポートのパーシャルは_user_report.html.hamlで定義
- 商談表示には共通の_opportunity.html.hamlパーシャルを使用

---

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

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

### 推奨読解順序

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

ユーザーモデルと商談モデルの関連スコープを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | user.rb | `app/models/users/user.rb` | have_assigned_opportunitiesスコープ |
| 1-2 | opportunity.rb | `app/models/entities/opportunity.rb` | pipeline, unassignedスコープ |

**読解のコツ**:
- **user.rb 81-85行目**: `have_assigned_opportunities` スコープの実装
  - INNER JOINでopportunitiesテーブルと結合
  - stage が won/lost 以外の条件
  - DISTINCTで重複排除
- **opportunity.rb 51行目**: `pipeline` スコープ - won/lost以外のステージ
- **opportunity.rb 52行目**: `unassigned` スコープ - assigned_to IS NULL

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

UsersControllerのopportunities_overviewアクションが処理の起点。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | users_controller.rb | `app/controllers/users_controller.rb` | opportunities_overviewアクション |

**主要処理フロー**:
1. **105-110行目**: opportunities_overviewアクション全体
2. **108行目**: `User.have_assigned_opportunities.order(:first_name)` - アサイン商談持ちユーザー取得
3. **109行目**: `Opportunity.my(current_user).unassigned.pipeline` - 未アサイン商談取得
4. 商談は`stage`順でソート、取引先・ユーザー・タグを一括取得（includes）

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

サマリー表示のビューファイルを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | opportunities_overview.html.haml | `app/views/users/opportunities_overview.html.haml` | メインビュー |
| 3-2 | _user_report.html.haml | `app/views/users/_user_report.html.haml` | ユーザー別商談表示 |

**主要処理フロー**:
- **5行目**: ユーザー商談または未アサイン商談がある場合の条件分岐
- **7行目**: `_user_report` パーシャルのコレクションレンダリング
- **8-14行目**: 未アサイン商談セクションの表示
- **16行目**: 商談なし時のメッセージ

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

```
UsersController#opportunities_overview
    │
    ├─ load_and_authorize_resource (CanCanCan)
    │      └─ Ability#can?(:opportunities_overview)
    │
    ├─ User.have_assigned_opportunities.order(:first_name)
    │      └─ SELECT DISTINCT users.*
    │         FROM users
    │         INNER JOIN opportunities ON users.id = opportunities.assigned_to
    │         WHERE stage <> 'lost' AND stage <> 'won'
    │         ORDER BY first_name
    │
    ├─ Opportunity.my(current_user).unassigned.pipeline
    │      └─ SELECT opportunities.*
    │         FROM opportunities
    │         WHERE [my条件] AND assigned_to IS NULL
    │         AND (stage IS NULL OR (stage != 'won' AND stage != 'lost'))
    │         ORDER BY stage
    │         INCLUDES account, user, tags
    │
    └─ render "opportunities_overview"
           ├─ render partial: "_user_report", collection: @users_with_opportunities
           │      └─ user_report.assigned_opportunities.pipeline.order(amount DESC)
           │             └─ render partial: "opportunities/opportunity"
           │
           └─ render partial: "opportunities/opportunity", collection: @unassigned_opportunities
```

### データフロー図

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

GET /users/              UsersController#opportunities_overview
opportunities_overview                │
                                      ▼
                          User.have_assigned_opportunities
                                      │
                    ┌─────────────────┴─────────────────┐
                    ▼                                   ▼
              usersテーブル                    opportunitiesテーブル
              (INNER JOIN)                    (JOIN条件)
                    │                                   │
                    └─────────────────┬─────────────────┘
                                      ▼
                          @users_with_opportunities
                                      │
                                      ▼
                          Opportunity.my.unassigned.pipeline
                                      │
                                      ▼
                          @unassigned_opportunities
                                      │
                                      ▼
                    ┌─────────────────┴─────────────────┐
                    ▼                                   ▼
            ユーザー別商談表示              未アサイン商談表示
            (_user_report)                 (_opportunity)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| users_controller.rb | `app/controllers/users_controller.rb` | コントローラ | opportunities_overviewアクション |
| user.rb | `app/models/users/user.rb` | モデル | have_assigned_opportunitiesスコープ |
| opportunity.rb | `app/models/entities/opportunity.rb` | モデル | pipeline, unassignedスコープ |
| opportunities_overview.html.haml | `app/views/users/opportunities_overview.html.haml` | ビュー | メインテンプレート |
| _user_report.html.haml | `app/views/users/_user_report.html.haml` | ビュー | ユーザー別商談パーシャル |
| _opportunity.html.haml | `app/views/opportunities/_opportunity.html.haml` | ビュー | 商談表示パーシャル |
| routes.rb | `config/routes.rb` | 設定 | /users/opportunities_overviewルーティング（25行目） |
