# 帳票設計書 3-リード一覧

## 概要

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

### 本帳票の処理概要

リード一覧エクスポート機能は、システムに登録された見込み客（リード）データを一括でダウンロードし、営業活動の分析やマーケティング施策の効果測定を可能にする帳票出力機能です。

**業務上の目的・背景**：営業パイプラインの初期段階であるリード管理は、企業の売上向上に直結する重要な業務プロセスです。本帳票により、リードのソース別分析、キャンペーン効果測定、ステータス別の進捗管理、営業担当者別のリード配分分析、外部MAツールとのデータ連携など、多様なマーケティング・営業ニーズに対応します。特にキャンペーンとの紐付け情報を含むため、マーケティングROIの分析に有効です。

**帳票の利用シーン**：週次・月次の営業パイプラインレビュー時、マーケティングキャンペーン効果分析時、リード配分の見直し時、外部MAツールへのデータエクスポート時に利用されます。

**主要な出力内容**：
1. リード基本情報（ID、氏名、役職、会社名、メール、電話番号等）
2. キャンペーン関連情報（紐付けキャンペーン）
3. 分類・評価情報（ソース、ステータス、評価、電話禁止フラグ）
4. SNS・Web情報（ブログ、LinkedIn、Facebook、Twitter）
5. 住所情報（ビジネス住所）
6. カスタムフィールド（システム設定による拡張項目）

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

**帳票の利用者**：営業担当者、マーケティング担当者、営業マネージャー、システム管理者

## 帳票種別

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

## 利用画面

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

## 出力形式

### 基本仕様

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

### Excel固有設定

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

## 帳票レイアウト

### レイアウト概要

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

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

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | ID | リードID | leads.id | Number |
| 2 | User | 作成ユーザー名 | users.name（via user_id） | String |
| 3 | Campaign | キャンペーン名 | campaigns.name（via campaign_id） | String |
| 4 | Job Title | 役職 | leads.title | String |
| 5 | Name | フルネーム | lead.name（computed） | String |
| 6 | Email | メールアドレス | leads.email | String |
| 7 | Alt Email | 代替メール | leads.alt_email | String |
| 8 | Phone | 電話番号 | leads.phone | String |
| 9 | Mobile | 携帯電話 | leads.mobile | String |
| 10 | Company | 会社名 | leads.company | String |
| 11 | Background Info | 背景情報 | leads.background_info | String |
| 12 | Blog | ブログURL | leads.blog | String |
| 13 | LinkedIn | LinkedInプロフィール | leads.linkedin | String |
| 14 | Facebook | Facebookプロフィール | leads.facebook | String |
| 15 | Twitter | Twitterアカウント | leads.twitter | String |
| 16 | Date Created | 作成日 | leads.created_at | String |
| 17 | Date Updated | 更新日 | leads.updated_at | String |
| 18 | Assigned To | 担当者名 | users.name（via assigned_to） | String |
| 19 | Access | アクセス権 | leads.access | String |
| 20 | Source | ソース | leads.source | String |
| 21 | Status | ステータス | leads.status | String |
| 22 | Rating | 評価 | leads.rating | Number |
| 23 | Do Not Call | 電話禁止フラグ | leads.do_not_call | String |
| 24 | Street1 | 住所1 | addresses.street1 | String |
| 25 | Street2 | 住所2 | addresses.street2 | String |
| 26 | City | 市区町村 | addresses.city | String |
| 27 | State | 都道府県 | addresses.state | String |
| 28 | Zipcode | 郵便番号 | addresses.zipcode | String |
| 29 | Country | 国 | addresses.country | String |
| 30 | Address | 完全住所 | addresses.full_address | String |
| 31+ | カスタムフィールド | 動的に追加 | fields.name | String/Number |

### 明細部

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

### フッター部

なし（データ一覧のみ）

## 出力条件

### 抽出条件

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

### ソート順

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

### 改ページ条件

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

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

### 参照テーブル一覧

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| leads | リード基本情報 | メインテーブル |
| users | ユーザー情報 | leads.user_id = users.id, leads.assigned_to = users.id |
| campaigns | キャンペーン情報 | leads.campaign_id = campaigns.id |
| addresses | 住所情報 | addresses.addressable_id = leads.id AND addresses.addressable_type = 'Lead' AND addresses.address_type = 'Business' |
| fields | カスタムフィールド定義 | fields.klass_name = 'Lead' |

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

#### leads

| 参照項目（カラム名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| id | ID | - | 主キー |
| first_name | Name（一部） | - | 最大64文字 |
| last_name | Name（一部） | - | 最大64文字 |
| title | Job Title | - | 役職 |
| company | Company | - | 会社名、最大64文字 |
| email | Email | - | 最大64文字 |
| alt_email | Alt Email | - | 最大64文字 |
| phone | Phone | - | 最大32文字 |
| mobile | Mobile | - | 最大32文字 |
| blog | Blog | - | 最大128文字 |
| linkedin | LinkedIn | - | 最大128文字 |
| facebook | Facebook | - | 最大128文字 |
| twitter | Twitter | - | 最大128文字 |
| rating | Rating | - | 0-5の整数、デフォルト0 |
| do_not_call | Do Not Call | - | ブール値、デフォルトfalse |
| source | Source | - | リード獲得元 |
| status | Status | - | リードステータス |
| background_info | Background Info | - | 最大255文字 |
| access | Access | - | Public/Private/Shared |
| created_at | Date Created | - | - |
| updated_at | Date Updated | - | - |

#### addresses（Business Address）

| 参照項目（カラム名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| street1 | Street1 | address_type = 'Business' | - |
| street2 | Street2 | address_type = 'Business' | - |
| city | City | address_type = 'Business' | - |
| state | State | address_type = 'Business' | - |
| zipcode | Zipcode | address_type = 'Business' | - |
| country | Country | address_type = 'Business' | - |
| full_address | Address | address_type = 'Business' | 結合住所 |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| Name | "#{first_name} #{last_name}" | - | full_nameメソッドで生成 |
| データ型判定 | value.respond_to?(:abs) ? 'Number' : 'String' | - | セルのデータ型を動的に判定 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[XLS/CSVエクスポート要求] --> B[LeadsController#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
```

## エラー処理

### エラーケース一覧

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

## パフォーマンス要件

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

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

- ユーザー権限に基づくアクセス制御（uses_user_permissions）により、ユーザーがアクセス権を持つリードのみエクスポート可能
- 認証が必要（authenticate_user!）
- Access列の値（Public/Private/Shared）に基づくデータ可視性制御
- do_not_call（電話禁止）フラグにより、連絡制限のあるリードを識別可能
- パスワード・トークン等の機密情報はCSVエクスポート時に除外

## 備考

- 国際化対応：ヘッダーラベルはI18nを使用して多言語対応
- カスタムフィールド対応：Lead.fieldsメソッドにより動的にカスタムフィールドを追加出力
- リードが連絡先に変換されると、statusが"converted"に更新される
- キャンペーンとの紐付けにより、leads_countが自動的に増減管理される

---

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

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

### 推奨読解順序

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | lead.rb | `app/models/entities/lead.rb` | Leadモデルの定義、belongs_to :campaign、has_one :contact関連、ステータス管理を確認 |
| 1-2 | address.rb | `app/models/polymorphic/address.rb` | 住所モデルのポリモーフィック関連を理解 |

**読解のコツ**: after_create/after_destroyコールバックでのleads_countの増減処理、promoteメソッドによる連絡先への変換処理に注目

#### 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/leads/index.xls.builder` | ヘッダー定義（8-37行目）、キャンペーン関連の出力、データ出力（53-99行目） |
| 3-2 | header.xls.builder | `app/views/layouts/header.xls.builder` | XMLワークブック構造の定義 |

**主要処理フロー**:
- **7-50行目**: ヘッダー行の生成
- **10行目**: campaign項目（キャンペーン関連）
- **40-42行目**: カスタムフィールドラベルの動的追加
- **53-99行目**: データ行の生成
- **57-58行目**: user.try(:name)、campaign.try(:name)で関連データ取得

#### Step 4: CSV出力を理解する

CSV形式での出力処理を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | export_csv.rb | `lib/fat_free_crm/export_csv.rb` | CSV生成ロジック |

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

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

### データフロー図

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

Request             EntitiesController        Response
/leads.xls     ───▶ #index                ───▶ leads.xls
                         │
                         ▼
                    get_list_of_records
                         │
                         ├─ state(filter) ───▶ ステータスフィルタ
                         │
                         ▼
                    @leads (Collection)
                         │
                         ▼
                    index.xls.builder
                         │
                         ▼
                    XML生成 (Builder)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| lead.rb | `app/models/entities/lead.rb` | ソース | Leadモデル定義 |
| entities_controller.rb | `app/controllers/entities_controller.rb` | ソース | 共通コントローラー処理 |
| application_controller.rb | `app/controllers/application_controller.rb` | ソース | 基底コントローラー |
| index.xls.builder | `app/views/leads/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` | 設定 | ルーティング定義（103-123行目） |
