# 機能設計書 108-サーベイダウンロード

## 概要

本ドキュメントは、Chatwootにおける CSAT（顧客満足度調査）レポートのダウンロード機能について記述する。CSATサーベイ回答データをCSV形式でエクスポートする機能である。

### 本機能の処理概要

**業務上の目的・背景**：カスタマーサポートの品質分析において、CSATデータを外部のBIツールやスプレッドシートで詳細分析したいというニーズがある。本機能により、フィルタ条件に合致するCSATサーベイ回答データをCSV形式でダウンロードし、外部ツールでの高度な分析や報告書作成を可能にする。

**機能の利用シーン**：
- CSATレポート画面から「エクスポート」ボタンをクリック
- 期間やエージェントでフィルタしたデータをCSVでダウンロード
- 経営報告用のCSATデータを取得
- 外部BIツールへのデータインポート

**主要な処理内容**：
1. フィルタ条件に基づくCSATサーベイ回答データの取得
2. CSV形式へのデータ変換
3. ファイルダウンロードレスポンスの生成

**関連システム・外部連携**：特になし（ファイルダウンロード）

**権限による制御**：CSATサーベイの閲覧にはレポート閲覧権限が必要。check_authorization によりアクセス制御される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 43 | CSATレポート画面 | 主画面 | CSATレポートのダウンロード |

## 機能種別

帳票出力

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| since | Integer (Unix timestamp) | No | 期間開始（秒） | - |
| until | Integer (Unix timestamp) | No | 期間終了（秒） | - |
| user_ids | Array | No | エージェントID配列 | - |
| inbox_id | Integer | No | インボックスID | - |
| team_id | Integer | No | チームID | - |
| rating | Integer | No | 評価値（1-5） | 1-5 の範囲 |

### 入力データソース

- 画面入力: レポート画面のフィルタ条件
- DBテーブル: csat_survey_responses テーブルからサーベイ回答を取得

## 出力仕様

### 出力データ（CSVカラム）

| 項目名 | 説明 |
|--------|------|
| Agent Name | エージェント名とメールアドレス |
| Rating | 評価値（1-5） |
| Feedback | フィードバックメッセージ |
| Contact Name | 連絡先名 |
| Contact Email Address | 連絡先メールアドレス |
| Contact Phone Number | 連絡先電話番号 |
| Link to the conversation | 会話へのリンク |
| Recorded at | 回答日時 |
| Review Notes | レビューノート（Enterprise版のみ） |

### 出力先

- ファイル: CSV形式でブラウザダウンロード
- ファイル名: `csat_report.csv`

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ APIエンドポイント: /api/v1/accounts/:account_id/csat_survey_responses/download

2. 認可チェック
   └─ check_authorization でレポート閲覧権限確認

3. CSATサーベイ回答取得
   ├─ 基本クエリ: Current.account.csat_survey_responses
   ├─ 関連データ: includes([:conversation, :assigned_agent, :contact])
   └─ フィルタ適用: 期間、エージェント、インボックス、チーム、評価

4. CSVレスポンス生成
   ├─ Content-Type: text/csv
   ├─ Content-Disposition: attachment; filename=csat_report.csv
   └─ ERBテンプレートでCSV生成

5. ファイルダウンロード
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B[認可チェック]
    B --> C[CSATサーベイ回答取得]
    C --> D[フィルタ適用]
    D --> E[CSVヘッダー生成]
    E --> F[データ行生成]
    F --> G[期間情報追加]
    G --> H[CSVダウンロード]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-108-01 | ファイル名固定 | ダウンロードファイル名は `csat_report.csv` | 常時 |
| BR-108-02 | Enterprise機能 | Review Notes カラムは Enterprise 版のみ出力 | ChatwootApp.enterprise? |
| BR-108-03 | 期間表示 | CSVの最終行に集計期間を表示 | 常時 |
| BR-108-04 | 会話リンク | 会話がある場合のみリンクを出力 | conversation.present? |

### 計算ロジック

会話リンク生成:
```ruby
app_account_conversation_url(account_id: Current.account.id, id: conversation.display_id)
```

期間表示:
```ruby
I18n.t('reports.period', since: Date.strptime(params[:since], '%s'), until: Date.strptime(params[:until], '%s'))
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| download | csat_survey_responses | SELECT | フィルタ付き一覧取得 |
| download | conversations | JOIN | 会話情報取得 |
| download | users | JOIN | エージェント情報取得 |
| download | contacts | JOIN | 連絡先情報取得 |

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

#### csat_survey_responses

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | account_id = Current.account.id | 基本条件 |
| SELECT | * | フィルタ条件適用 | 期間、エージェント等 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | Unauthorized | 未認証状態でアクセス | ログインページへリダイレクト |
| 403 | Forbidden | レポート閲覧権限なし | エラーメッセージ表示 |

### リトライ仕様

特になし。同期処理のため即時エラー返却。

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

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

## パフォーマンス要件

- レスポンス時間: データ量に依存（大量データ時は数秒）
- ストリーミング: ERBテンプレートで行単位出力

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

- 認証必須: Devise TokenAuthによる認証
- 認可: Punditによるレポート閲覧権限チェック
- アカウントスコープ: Current.account.id で他アカウントのデータにアクセス不可
- 個人情報: 連絡先情報が含まれるため取り扱い注意

## 備考

- CSVのエンコーディングはUTF-8
- Enterprise版では `csat_review_notes` カラムが追加出力される
- 日本語等のマルチバイト文字も正しくエクスポートされる

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | csat_survey_response.rb | `app/models/csat_survey_response.rb` | モデル構造とアソシエーション |

**読解のコツ**: `belongs_to :conversation`, `belongs_to :assigned_agent`, `belongs_to :contact` で関連データを確認。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | csat_survey_responses_controller.rb | `app/controllers/api/v1/accounts/csat_survey_responses_controller.rb` | download アクション |

**主要処理フロー**:
- **8行目**: `before_action :set_csat_survey_responses, only: [:index, :metrics, :download]`
- **22-26行目**: `download` アクション
  - `response.headers['Content-Type'] = 'text/csv'`
  - `response.headers['Content-Disposition'] = 'attachment; filename=csat_report.csv'`
  - `render layout: false, template: 'api/v1/accounts/csat_survey_responses/download', formats: [:csv]`

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | download.csv.erb | `app/views/api/v1/accounts/csat_survey_responses/download.csv.erb` | CSVテンプレート |

**主要処理フロー**:
- **1-13行目**: CSVヘッダー定義
  - 基本ヘッダー: agent_name, rating, feedback, contact情報, 会話リンク, 回答日時
  - Enterprise版: review_notes ヘッダー追加
- **14行目**: `CSV.generate_line(headers)` でヘッダー行出力
- **15-32行目**: データ行生成ループ
  - 各回答レコードから情報を抽出
  - `app_account_conversation_url` で会話リンク生成
- **33-42行目**: 期間情報行出力
  - `I18n.t('reports.period', ...)` で期間表示

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

```
CsatSurveyResponsesController#download
    │
    ├─ before_action
    │      ├─ check_authorization
    │      └─ set_csat_survey_responses
    │             ├─ Current.account.csat_survey_responses
    │             ├─ includes([:conversation, :assigned_agent, :contact])
    │             └─ filter_by_* (各種フィルタ)
    │
    └─ download アクション
           ├─ response.headers['Content-Type'] = 'text/csv'
           ├─ response.headers['Content-Disposition'] = 'attachment...'
           └─ render download.csv.erb
                  │
                  ├─ CSV.generate_line(headers)
                  │
                  ├─ @csat_survey_responses.each
                  │      ├─ assigned_agent (名前, メール)
                  │      ├─ rating
                  │      ├─ feedback_message
                  │      ├─ contact (名前, メール, 電話)
                  │      ├─ app_account_conversation_url
                  │      └─ created_at
                  │
                  └─ CSV.generate_line(period)
```

### データフロー図

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

フィルタ条件 ───────▶ CsatSurveyResponsesController ───▶ CSV ファイル
(期間, agent,               │                              │
 inbox, team,               │                              ├─ ヘッダー行
 rating)                    │                              ├─ データ行 x N
                           ▼                              └─ 期間情報行
        ┌─────────────────┴─────────────────┐
        │                                   │
csat_survey_responses              関連テーブル
    │                             (conversations,
    │                              users, contacts)
    │
    └────────▶ download.csv.erb
                    │
                    └────────▶ csat_report.csv
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| csat_survey_response.rb | `app/models/csat_survey_response.rb` | モデル | CSATサーベイ回答モデル |
| csat_survey_responses_controller.rb | `app/controllers/api/v1/accounts/csat_survey_responses_controller.rb` | コントローラー | download アクション |
| download.csv.erb | `app/views/api/v1/accounts/csat_survey_responses/download.csv.erb` | ビュー | CSVテンプレート |
| application_helper.rb | `app/helpers/application_helper.rb` | ヘルパー | app_account_conversation_url |
