# 機能設計書 113-チーム別レポート

## 概要

本ドキュメントは、Chatwootのチーム別レポート機能の設計を記述する。この機能は、各チームのパフォーマンスメトリクスを集計・分析し、チーム単位でのサポート業務管理を支援するレポーティング機能である。

### 本機能の処理概要

チーム別レポート機能は、アカウント内で定義されたチームごとに、そのチームに割り当てられた会話の統計情報（会話数、解決数、平均応答時間、平均解決時間、平均返信時間）を集計し、チーム間のパフォーマンス比較や人員配置の最適化を支援する機能である。

**業務上の目的・背景**：カスタマーサポート組織では、専門分野や担当製品、言語対応などに基づいてチームを編成することが一般的である。チーム別レポートにより、各チームの対応状況を可視化し、チーム間の負荷バランス調整、リソース配分の最適化、パフォーマンス改善施策の立案に活用できる。また、チームリーダーが自チームの状況を把握するためにも重要な機能である。

**機能の利用シーン**：
- 週次・月次のチーム別パフォーマンスレビュー
- チーム間の負荷バランス確認と人員再配置の検討
- チーム別KPI達成状況のモニタリング
- CSV出力によるチーム別詳細分析

**主要な処理内容**：
1. アカウント内の全チームを取得
2. 各チームに割り当てられた会話数をteam_idでグループ化して集計
3. ReportingEventテーブルから解決数・平均応答時間・平均解決時間・平均返信時間を算出
4. 営業時間ベースの計算オプションに対応
5. JSON形式またはCSV形式でレポートデータを出力

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

**権限による制御**：管理者（Administrator）ロールを持つユーザーのみがレポートを閲覧可能（ReportPolicy#view?で制御）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 36 | チームレポート画面 | 主機能 | チーム別統計の表示 |
| 37 | チームレポート概要 | 主機能 | チームレポート概要一覧 |
| 38 | チームレポート詳細 | 主機能 | 特定チームの詳細レポート |

## 機能種別

データ集計・レポート出力

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| since | String | Yes | 集計開始日時（UNIXタイムスタンプ文字列） | 数値変換可能な文字列 |
| until | String | Yes | 集計終了日時（UNIXタイムスタンプ文字列） | 数値変換可能な文字列 |
| business_hours | Boolean | No | 営業時間ベースで計算するか | true/false |
| timezone_offset | Float | No | タイムゾーンオフセット | 数値 |

### 入力データソース

- HTTPリクエストパラメータ（API呼び出し時）
- Current.account（認証済みユーザーのアカウント情報）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | Integer | チームID |
| conversations_count | Integer | 期間内の会話数 |
| resolved_conversations_count | Integer | 期間内の解決会話数 |
| avg_first_response_time | Float | 平均初回応答時間（秒） |
| avg_resolution_time | Float | 平均解決時間（秒） |
| avg_reply_time | Float | 平均返信時間（秒） |

### 出力先

- JSON形式：SummaryReportsコントローラー（内部利用）
- CSV形式：ダウンロード用ファイル（`teams_report.csv`）

## 処理フロー

### 処理シーケンス

```
1. API呼び出し（GET /api/v2/accounts/:account_id/reports/teams）
   └─ ReportsController#teams アクションが起動

2. 認可チェック
   └─ ReportPolicy#view? で管理者権限を確認

3. レポートデータ生成
   └─ ReportsHelper#generate_teams_report を呼び出し
       └─ TeamSummaryBuilder でデータを集計

4. データロード
   └─ load_data で会話数とReportingEventデータを取得

5. 会話数の集計
   └─ Conversationテーブルからteam_id別にカウント

6. ReportingEventデータの集計
   └─ conversations.team_idでグループ化して各種メトリクスを算出

7. チーム一覧との結合
   └─ 各チームのIDと統計値をマッピング

8. CSV出力
   └─ レポートデータをCSV形式でレンダリング
```

### フローチャート

```mermaid
flowchart TD
    A[API呼び出し] --> B{認可チェック}
    B -->|権限あり| C[TeamSummaryBuilder初期化]
    B -->|権限なし| Z[403 Forbidden]
    C --> D[load_data]
    D --> E[会話数を集計]
    E --> F[ReportingEventデータを集計]
    F --> G[チーム別に統計を構築]
    G --> H[CSV形式でレンダリング]
    H --> I[レスポンス返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-113-01 | 期間指定必須 | since/untilパラメータは必須 | 常時 |
| BR-113-02 | 営業時間計算 | business_hours=trueの場合、value_in_business_hoursカラムを使用 | business_hoursパラメータがtrue |
| BR-113-03 | 全チーム対象 | アカウント内の全チームを対象に集計 | 常時 |
| BR-113-04 | team_idでグループ化 | conversations.team_idカラムでグループ化 | 常時 |

### 計算ロジック

**会話数の計算**：
```ruby
account.conversations.where(created_at: range).group(:team_id).count
```

**平均時間の計算**：
- 通常モード：`AVG(reporting_events.value)`
- 営業時間モード：`AVG(reporting_events.value_in_business_hours)`

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| チーム取得 | teams | SELECT | アカウント内の全チームを取得 |
| 会話数集計 | conversations | SELECT | チーム別会話数をカウント |
| イベント集計 | reporting_events, conversations | SELECT | チーム別メトリクスを算出 |

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

#### conversations

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | COUNT(*) | created_at BETWEEN since AND until, GROUP BY team_id | チーム別会話数 |

#### reporting_events

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | AVG(value), COUNT(*) | JOIN conversationsでteam_id別に集計 | イベント種別ごとに集計 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 403 | Forbidden | 管理者権限がない場合 | 権限のあるユーザーでログイン |
| 422 | Unprocessable Entity | パラメータ不正 | パラメータを確認して再リクエスト |

### リトライ仕様

該当なし（同期処理のため）

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

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

## パフォーマンス要件

- チーム数が多い場合（50以上）でも応答可能であること
- conversations.team_idとreporting_events関連のインデックスが適切に設定されていること

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

- 管理者ロールのみがアクセス可能（ReportPolicyで制御）
- アカウント間のデータ分離はCurrent.accountスコープで担保
- CSVダウンロード時もサーバーサイドで認証・認可チェックを実施

## 備考

- チームに割り当てられていない会話（team_id = null）は集計対象外となる
- CSV出力時は、時間値が人間可読形式（HH:MM:SS）に変換される
- TeamSummaryBuilderはBaseSummaryBuilderを継承し、共通ロジックを再利用

---

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

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

### 推奨読解順序

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

まず、チームモデルとレポートイベントモデルを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | team.rb | `app/models/team.rb` | Teamモデルの定義 |
| 1-2 | reporting_event.rb | `app/models/reporting_event.rb` | レポートイベントのスキーマ |
| 1-3 | conversation.rb | `app/models/conversation.rb` | team_idカラムの存在確認 |

**読解のコツ**: Conversationモデルにteam_idが外部キーとして存在し、チームへの所属関係を管理していることを理解する。

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

APIコントローラーからの処理フローを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | reports_controller.rb | `app/controllers/api/v2/accounts/reports_controller.rb` | teamsアクション（36-39行目） |

**主要処理フロー**:
1. **36-39行目**: `teams`アクションでgenerate_teams_reportを呼び出し、CSVレンダリング
2. **73-75行目**: `check_authorization`で権限チェック

#### Step 3: ヘルパー層を理解する

レポート生成のヘルパーメソッドを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | reports_helper.rb | `app/helpers/api/v2/accounts/reports_helper.rb` | generate_teams_report（26-35行目） |

**主要処理フロー**:
- **26-30行目**: TeamSummaryBuilderを初期化してbuildを呼び出し
- **32-35行目**: 各チームのデータを整形

#### Step 4: ビルダー層を理解する

実際のデータ集計ロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | base_summary_builder.rb | `app/builders/v2/reports/base_summary_builder.rb` | 共通の集計ロジック |
| 4-2 | team_summary_builder.rb | `app/builders/v2/reports/team_summary_builder.rb` | チーム固有の集計ロジック |

**主要処理フロー**:
- **base_summary_builder.rb 4-7行目**: buildメソッドでload_dataとprepare_reportを順次実行
- **base_summary_builder.rb 16-34行目**: load_reporting_events_dataで各種メトリクスを一括集計
- **team_summary_builder.rb 9-11行目**: fetch_conversations_countでteam_id別会話数を取得
- **team_summary_builder.rb 17-21行目**: prepare_reportでチーム一覧からレポート構築
- **team_summary_builder.rb 23-32行目**: build_team_statsで個別チームの統計情報を構築
- **team_summary_builder.rb 34-36行目**: group_by_keyでconversations.team_idを指定

#### Step 5: CSVテンプレートを理解する

CSV出力形式を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | teams.csv.erb | `app/views/api/v2/accounts/reports/teams.csv.erb` | CSV出力テンプレート |

**主要処理フロー**:
- **1行目**: 期間情報をヘッダー行として出力
- **3-10行目**: カラムヘッダーを定義（team_name, conversations_count等）
- **12-14行目**: レポートデータを行ごとに出力

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

```
ReportsController#teams
    │
    ├─ check_authorization
    │      └─ ReportPolicy#view?
    │
    ├─ ReportsHelper#generate_teams_report
    │      │
    │      ├─ TeamSummaryBuilder#new
    │      │
    │      └─ TeamSummaryBuilder#build (inherited from BaseSummaryBuilder)
    │             │
    │             ├─ load_data
    │             │      ├─ fetch_conversations_count (override)
    │             │      │      └─ conversations.group(:team_id).count
    │             │      └─ load_reporting_events_data (inherited)
    │             │
    │             └─ prepare_report (override)
    │                    └─ build_team_stats (per team)
    │
    └─ generate_csv
           └─ render teams.csv.erb
```

### データフロー図

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

since/until パラメータ ───▶ DateRangeHelper#range ───────────┐
                                                              │
business_hours パラメータ ─▶ average_value_key 判定 ─────────┤
                                                              │
                              ┌──────────────────────────────┘
                              ▼
Current.account ────────────▶ TeamSummaryBuilder
                              │
                              ├─▶ conversations.group(:team_id).count ────┐
                              │                                            │
                              └─▶ reporting_events.joins(:conversation)   │
                                   .group('conversations.team_id')         │
                                   .select(AVG, COUNT) ────────────────────┤
                                                                           │
                              ┌────────────────────────────────────────────┘
                              ▼
                         prepare_report
                              │
                              ├─▶ account.teams.map
                              │      └─▶ build_team_stats
                              │
                              ▼
                         CSVSafe.generate_line ───▶ teams_report.csv
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| reports_controller.rb | `app/controllers/api/v2/accounts/reports_controller.rb` | コントローラー | APIエンドポイント |
| reports_helper.rb | `app/helpers/api/v2/accounts/reports_helper.rb` | ヘルパー | レポート生成ヘルパー |
| team_summary_builder.rb | `app/builders/v2/reports/team_summary_builder.rb` | ビルダー | チーム集計ロジック |
| base_summary_builder.rb | `app/builders/v2/reports/base_summary_builder.rb` | ビルダー | 共通集計ロジック |
| date_range_helper.rb | `app/helpers/date_range_helper.rb` | ヘルパー | 日付範囲処理 |
| team.rb | `app/models/team.rb` | モデル | チームモデル |
| reporting_event.rb | `app/models/reporting_event.rb` | モデル | レポートイベントデータ |
| report_policy.rb | `app/policies/report_policy.rb` | ポリシー | 認可制御 |
| teams.csv.erb | `app/views/api/v2/accounts/reports/teams.csv.erb` | ビュー | CSV出力テンプレート |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義（442行目） |
