# 帳票設計書 5-キャンペーン一覧

## 概要

本ドキュメントは、Fat Free CRMシステムにおけるキャンペーン（Campaigns）データのエクスポート帳票に関する設計仕様書です。キャンペーンの基本情報、予算・目標、実績データ、カスタムフィールドを含む一覧データをXLS/CSV形式で出力する機能について記述します。

### 本帳票の処理概要

キャンペーン一覧エクスポート機能は、システムに登録されたマーケティングキャンペーンデータを一括でダウンロードし、マーケティング効果の分析やROI算出を可能にする帳票出力機能です。

**業務上の目的・背景**：マーケティングキャンペーンの効果測定は、マーケティング投資の最適化に不可欠です。本帳票により、キャンペーン別のROI分析、リード獲得コスト（CPL）の算出、コンバージョン率の評価、予算vs実績の比較分析、キャンペーン効果のトレンド分析など、マーケティング部門の意思決定を支援する多様なデータ分析ニーズに対応します。リード数、商談数、売上といった実績データも含まれるため、包括的なキャンペーン評価が可能です。

**帳票の利用シーン**：キャンペーン終了後の効果測定レポート作成時、マーケティング予算の策定・見直し時、経営報告書作成時、次期キャンペーン計画立案時に利用されます。

**主要な出力内容**：
1. キャンペーン基本情報（ID、名前、ステータス、期間）
2. 予算・目標情報（予算、目標リード数、目標コンバージョン率、目標売上）
3. 実績情報（リード数、商談数、売上）
4. 目的・背景情報
5. カスタムフィールド（システム設定による拡張項目）

**帳票の出力タイミング**：キャンペーン一覧画面において、ユーザーがエクスポートボタンをクリックした際にリアルタイムで生成・ダウンロードされます。

**帳票の利用者**：マーケティング担当者、マーケティングマネージャー、営業マネージャー、経営幹部

## 帳票種別

一覧表（エクスポート帳票）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | キャンペーン一覧画面 | /campaigns | XLS/CSVエクスポートリンク |
| - | キャンペーン一覧画面 | /campaigns.xls | XLS形式でダウンロード |
| - | キャンペーン一覧画面 | /campaigns.csv | CSV形式でダウンロード |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | XLS（Excel XML Spreadsheet）/ CSV |
| 用紙サイズ | 該当なし（データエクスポート） |
| 向き | 該当なし |
| ファイル名 | campaigns.xls / campaigns.csv |
| 出力方法 | ダウンロード |
| 文字コード | UTF-8 |

### Excel固有設定

| 項目 | 内容 |
|-----|------|
| シート名 | Campaigns（I18n.t(:tab_campaigns)による国際化対応） |
| 保護設定 | 無 |

## 帳票レイアウト

### レイアウト概要

XLS形式のスプレッドシートとして、1行目にヘッダー、2行目以降にデータ行が続く標準的な表形式レイアウトです。

```
┌─────────────────────────────────────┐
│         ヘッダー行（項目名）          │
├─────────────────────────────────────┤
│         データ行1                    │
│         データ行2                    │
│         ...                         │
│         データ行N                    │
└─────────────────────────────────────┘
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | ID | キャンペーンID | campaigns.id | Number |
| 2 | User | 作成ユーザー名 | users.name（via user_id） | String |
| 3 | Assigned To | 担当者名 | users.name（via assigned_to） | String |
| 4 | Name | キャンペーン名 | campaigns.name | String |
| 5 | Access | アクセス権 | campaigns.access | String |
| 6 | Status | ステータス | campaigns.status | String |
| 7 | Budget | 予算 | campaigns.budget | Number |
| 8 | Target Leads | 目標リード数 | campaigns.target_leads | Number |
| 9 | Target Conversion | 目標コンバージョン率 | campaigns.target_conversion | Number |
| 10 | Target Revenue | 目標売上 | campaigns.target_revenue | Number |
| 11 | Number of Leads | リード数（実績） | campaigns.leads_count | Number |
| 12 | Total Opportunities | 商談数（実績） | campaigns.opportunities_count | Number |
| 13 | Revenue | 売上（実績） | campaigns.revenue | Number |
| 14 | Starts On | 開始日 | campaigns.starts_on | String |
| 15 | Ends On | 終了日 | campaigns.ends_on | String |
| 16 | Objectives | 目的 | campaigns.objectives | String |
| 17 | Background Info | 背景情報 | campaigns.background_info | String |
| 18 | Date Created | 作成日 | campaigns.created_at | String |
| 19 | Date Updated | 更新日 | campaigns.updated_at | String |
| 20+ | カスタムフィールド | 動的に追加 | fields.name | String/Number |

### 明細部

各キャンペーンレコードについて、ヘッダー部と同じ順序で値を出力します。

### フッター部

なし（データ一覧のみ）

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| ユーザー権限 | 現在のユーザーがアクセス可能なキャンペーンのみ | Yes |
| 検索条件 | 画面で適用されているフィルタ・検索条件を継承 | No |
| ステータスフィルタ | session[:campaigns_filter]による状態フィルタ | No |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | ユーザー設定 | 設定による |
| 2 | created_at | 降順（デフォルト） |

### 改ページ条件

該当なし（全データを単一シートに出力）

## データベース参照仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| campaigns | キャンペーン基本情報 | メインテーブル |
| users | ユーザー情報 | campaigns.user_id = users.id, campaigns.assigned_to = users.id |
| fields | カスタムフィールド定義 | fields.klass_name = 'Campaign' |

### テーブル別参照項目詳細

#### campaigns

| 参照項目（カラム名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| id | ID | - | 主キー |
| name | Name | - | キャンペーン名、必須、最大64文字 |
| access | Access | - | Public/Private/Shared |
| status | Status | - | ステータス、最大64文字 |
| budget | Budget | - | 予算、decimal(12,2) |
| target_leads | Target Leads | - | 目標リード数、整数 |
| target_conversion | Target Conversion | - | 目標コンバージョン率、float |
| target_revenue | Target Revenue | - | 目標売上、decimal(12,2) |
| leads_count | Number of Leads | - | リード数（自動集計）、整数 |
| opportunities_count | Total Opportunities | - | 商談数（自動集計）、整数 |
| revenue | Revenue | - | 売上（手動入力）、decimal(12,2) |
| starts_on | Starts On | - | 開始日 |
| ends_on | Ends On | - | 終了日 |
| objectives | Objectives | - | 目的、text型 |
| background_info | Background Info | - | 背景情報、最大255文字 |
| created_at | Date Created | - | - |
| updated_at | Date Updated | - | - |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| leads_count | リード登録時に自動増減 | - | Lead.increment_leads_countにより管理 |
| opportunities_count | 商談登録時に自動増減 | - | Opportunity.increment_opportunities_countにより管理 |
| データ型判定 | value.respond_to?(:abs) ? 'Number' : 'String' | - | セルのデータ型を動的に判定 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[XLS/CSVエクスポート要求] --> B[CampaignsController#index]
    B --> C[EntitiesController#get_list_of_records]
    C --> D[Ransack検索実行]
    D --> E[ページネーション無効化]
    E --> F{出力形式}
    F -->|XLS| G[index.xls.builder実行]
    F -->|CSV| H[FatFreeCRM::ExportCSV.from_array実行]
    G --> I[XMLWorkbook生成]
    H --> J[CSV生成]
    I --> K[ファイルダウンロード]
    J --> K
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| データなし | @campaigns.empty? | 空のワークシートが出力される | ヘッダー・データ行とも出力されない |
| 認証エラー | 未ログイン | ログイン画面へリダイレクト | ログイン後に再実行 |
| 権限エラー | アクセス権限なし | 権限エラーメッセージ | 管理者に権限付与を依頼 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 数十〜数百件 |
| 目標出力時間 | 3秒以内（100件程度） |
| 同時出力数上限 | 明示的な制限なし |

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

- ユーザー権限に基づくアクセス制御（uses_user_permissions）により、ユーザーがアクセス権を持つキャンペーンのみエクスポート可能
- 認証が必要（authenticate_user!）
- Access列の値（Public/Private/Shared）に基づくデータ可視性制御
- 予算・売上情報を含むため、出力ファイルの取り扱いには注意が必要
- パスワード・トークン等の機密情報はCSVエクスポート時に除外

## 備考

- 国際化対応：ヘッダーラベルはI18nを使用して多言語対応
- カスタムフィールド対応：Campaign.fieldsメソッドにより動的にカスタムフィールドを追加出力
- ステータスは設定により変更可能（Setting.unroll(:campaign_status)）
- leads_countとopportunities_countは、リード・商談の作成・削除時に自動的に増減
- 開始日と終了日のバリデーション（ends_on > starts_on）

---

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

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

### 推奨読解順序

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

まず、キャンペーンエンティティの構造とリレーションを理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | campaign.rb | `app/models/entities/campaign.rb` | Campaignモデルの定義、has_many :leads、has_many :opportunities関連、カウンターカラムを確認 |

**読解のコツ**: leads_count/opportunities_countの自動更新機構、start_and_end_datesバリデーションに注目

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

処理の起点となるコントローラーを特定します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | entities_controller.rb | `app/controllers/entities_controller.rb` | 共通のindexアクション処理を理解 |
| 2-2 | application_controller.rb | `app/controllers/application_controller.rb` | respond_to宣言、認証フィルタを確認 |

**主要処理フロー**:
1. **138-178行目**: get_list_of_recordsでデータ取得

#### Step 3: ビューテンプレートを理解する

XLS出力の実際の処理を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.xls.builder | `app/views/campaigns/index.xls.builder` | ヘッダー定義（8-26行目）、目標vs実績の出力、データ出力（42-76行目） |
| 3-2 | header.xls.builder | `app/views/layouts/header.xls.builder` | XMLワークブック構造の定義 |

**主要処理フロー**:
- **7-39行目**: ヘッダー行の生成
- **14-20行目**: 目標・実績項目
- **28-31行目**: カスタムフィールドラベルの動的追加
- **42-76行目**: データ行の生成

#### Step 4: カウンター更新を理解する

リード・商談数の自動更新機構を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | lead.rb | `app/models/entities/lead.rb` | after_create :increment_leads_count、after_destroy :decrement_leads_count（81-82行目、170-177行目） |
| 4-2 | opportunity.rb | `app/models/entities/opportunity.rb` | after_create :increment_opportunities_count（89行目、178-180行目） |

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

```
HTTP Request (GET /campaigns.xls)
    │
    ├─ ApplicationController (認証・権限チェック)
    │      └─ authenticate_user!
    │
    ├─ EntitiesController#index (継承元)
    │      └─ get_list_of_records
    │             ├─ ransack_search.result (検索実行)
    │             ├─ state(filter) (ステータスフィルタ)
    │             └─ ページネーション制御
    │
    └─ campaigns/index.xls.builder (ビュー)
           ├─ header.xls.builder (レイアウト)
           │
           ├─ @campaigns.each (データイテレーション)
           │      ├─ campaign.user.try(:name)
           │      ├─ campaign.assignee.try(:name)
           │      ├─ campaign.leads_count (カウンター)
           │      ├─ campaign.opportunities_count (カウンター)
           │      └─ Campaign.fields.each (カスタムフィールド)
           │
           └─ XMLセル出力
```

### データフロー図

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

Request             EntitiesController        Response
/campaigns.xls ───▶ #index                ───▶ campaigns.xls
                         │
                         ▼
                    get_list_of_records
                         │
                         ├─ state(filter) ───▶ ステータスフィルタ
                         │
                         ▼
                    @campaigns (Collection)
                         │
                         ├─ leads_count (自動集計値)
                         ├─ opportunities_count (自動集計値)
                         │
                         ▼
                    index.xls.builder
                         │
                         ▼
                    XML生成 (Builder)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| campaign.rb | `app/models/entities/campaign.rb` | ソース | Campaignモデル定義 |
| lead.rb | `app/models/entities/lead.rb` | ソース | leads_count更新処理 |
| opportunity.rb | `app/models/entities/opportunity.rb` | ソース | opportunities_count更新処理 |
| entities_controller.rb | `app/controllers/entities_controller.rb` | ソース | 共通コントローラー処理 |
| application_controller.rb | `app/controllers/application_controller.rb` | ソース | 基底コントローラー |
| index.xls.builder | `app/views/campaigns/index.xls.builder` | テンプレート | XLS出力ビュー |
| header.xls.builder | `app/views/layouts/header.xls.builder` | テンプレート | XLSレイアウト |
| export_csv.rb | `lib/fat_free_crm/export_csv.rb` | ソース | CSV出力ユーティリティ |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義（64-82行目） |
