# 帳票設計書 2-連絡先一覧

## 概要

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

### 本帳票の処理概要

連絡先一覧エクスポート機能は、システムに登録された連絡先（顧客担当者）データを一括でダウンロードし、外部ツールでの分析やコミュニケーション管理を可能にする帳票出力機能です。

**業務上の目的・背景**：営業活動において、顧客との接点となる連絡先情報の一括管理は極めて重要です。本帳票により、顧客担当者リストの作成、メーリングリストへのデータ投入、年賀状・DMなどの発送リスト作成、顧客分析のためのデータ抽出、CRMデータのバックアップなど、多様なビジネスニーズに対応します。リードから変換された連絡先と直接登録された連絡先の両方を含む包括的なデータ出力が可能です。

**帳票の利用シーン**：顧客へのダイレクトメール発送準備時、営業レポート作成時、マーケティングキャンペーンの対象者リスト作成時、データ移行・バックアップ時に利用されます。

**主要な出力内容**：
1. 連絡先基本情報（ID、氏名、役職、メール、電話番号等）
2. 関連リード情報（変換元リードID）
3. SNS・Web情報（ブログ、LinkedIn、Facebook、Twitter）
4. 担当者・アクセス制御情報
5. 住所情報（ビジネス住所）
6. カスタムフィールド（システム設定による拡張項目）

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

**帳票の利用者**：営業担当者、カスタマーサクセス担当者、マーケティング担当者、システム管理者

## 帳票種別

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

## 利用画面

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

## 出力形式

### 基本仕様

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

### Excel固有設定

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

## 帳票レイアウト

### レイアウト概要

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

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

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | ID | 連絡先ID | contacts.id | Number |
| 2 | Lead | 元リードID | leads.name（via lead_id） | String |
| 3 | Job Title | 役職 | contacts.title | String |
| 4 | Name | フルネーム | contact.name（computed） | String |
| 5 | First Name | 名 | contacts.first_name | String |
| 6 | Last Name | 姓 | contacts.last_name | String |
| 7 | Email | メールアドレス | contacts.email | String |
| 8 | Alt Email | 代替メール | contacts.alt_email | String |
| 9 | Phone | 電話番号 | contacts.phone | String |
| 10 | Mobile | 携帯電話 | contacts.mobile | String |
| 11 | Fax | FAX番号 | contacts.fax | String |
| 12 | Born On | 生年月日 | contacts.born_on | String |
| 13 | Background Info | 背景情報 | contacts.background_info | String |
| 14 | Blog | ブログURL | contacts.blog | String |
| 15 | LinkedIn | LinkedInプロフィール | contacts.linkedin | String |
| 16 | Facebook | Facebookプロフィール | contacts.facebook | String |
| 17 | Twitter | Twitterアカウント | contacts.twitter | String |
| 18 | Date Created | 作成日 | contacts.created_at | String |
| 19 | Date Updated | 更新日 | contacts.updated_at | String |
| 20 | Assigned To | 担当者名 | users.name（via assigned_to） | String |
| 21 | Access | アクセス権 | contacts.access | String |
| 22 | Department | 部門 | contacts.department | String |
| 23 | Source | ソース | contacts.source | String |
| 24 | Do Not Call | 電話禁止フラグ | contacts.do_not_call | String |
| 25 | Street1 | 住所1 | addresses.street1 | String |
| 26 | Street2 | 住所2 | addresses.street2 | String |
| 27 | City | 市区町村 | addresses.city | String |
| 28 | State | 都道府県 | addresses.state | String |
| 29 | Zipcode | 郵便番号 | addresses.zipcode | String |
| 30 | Country | 国 | addresses.country | String |
| 31 | Address | 完全住所 | addresses.full_address | String |
| 32+ | カスタムフィールド | 動的に追加 | fields.name | String/Number |

### 明細部

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

### フッター部

なし（データ一覧のみ）

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| ユーザー権限 | 現在のユーザーがアクセス可能な連絡先のみ | Yes |
| 検索条件 | 画面で適用されているフィルタ・検索条件を継承 | No |

### ソート順

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

### 改ページ条件

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

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

### 参照テーブル一覧

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| contacts | 連絡先基本情報 | メインテーブル |
| users | ユーザー情報 | contacts.assigned_to = users.id |
| leads | リード情報 | contacts.lead_id = leads.id |
| addresses | 住所情報 | addresses.addressable_id = contacts.id AND addresses.addressable_type = 'Contact' AND addresses.address_type = 'Business' |
| fields | カスタムフィールド定義 | fields.klass_name = 'Contact' |

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

#### contacts

| 参照項目（カラム名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| id | ID | - | 主キー |
| first_name | First Name | - | 最大64文字 |
| last_name | Last Name | - | 最大64文字 |
| title | Job Title | - | 役職、最大64文字 |
| department | Department | - | 部門、最大64文字 |
| email | Email | - | 最大254文字 |
| alt_email | Alt Email | - | 最大254文字 |
| phone | Phone | - | 最大32文字 |
| mobile | Mobile | - | 最大32文字 |
| fax | Fax | - | 最大32文字 |
| blog | Blog | - | 最大128文字 |
| linkedin | LinkedIn | - | 最大128文字 |
| facebook | Facebook | - | 最大128文字 |
| twitter | Twitter | - | 最大128文字 |
| born_on | Born On | - | 日付型 |
| do_not_call | Do Not Call | - | ブール値 |
| background_info | Background Info | - | 最大255文字 |
| source | Source | - | - |
| 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[ContactsController#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
```

## エラー処理

### エラーケース一覧

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

## パフォーマンス要件

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

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

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

## 備考

- 国際化対応：ヘッダーラベルはI18nを使用して多言語対応
- カスタムフィールド対応：Contact.fieldsメソッドにより動的にカスタムフィールドを追加出力
- 連絡先の名前表示順序は設定により「姓→名」または「名→姓」を切り替え可能（first_name_position設定）
- リードから変換された連絡先はlead_idを保持

---

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

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

### 推奨読解順序

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | contact.rb | `app/models/entities/contact.rb` | Contactモデルの定義、関連付け（belongs_to :lead, has_one :business_address, has_one :account）を確認 |
| 1-2 | address.rb | `app/models/polymorphic/address.rb` | 住所モデルのポリモーフィック関連を理解 |

**読解のコツ**: Railsのhas_one through関連、バリデーション（validates_length_of）、full_nameメソッドの実装に注目

#### 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/contacts/index.xls.builder` | ヘッダー定義（8-38行目）、データ出力（54-101行目）、SNS項目の出力位置 |
| 3-2 | header.xls.builder | `app/views/layouts/header.xls.builder` | XMLワークブック構造の定義 |

**主要処理フロー**:
- **7-51行目**: ヘッダー行の生成
- **40-43行目**: カスタムフィールドラベルの動的追加
- **54-101行目**: データ行の生成
- **56行目**: business_address取得（contact.business_address）

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

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

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

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

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

### データフロー図

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

Request             EntitiesController        Response
/contacts.xls  ───▶ #index                ───▶ contacts.xls
                         │
                         ▼
                    get_list_of_records
                         │
                         ▼
                    Ransack検索
                         │
                         ▼
                    @contacts (Collection)
                         │
                         ▼
                    index.xls.builder
                         │
                         ▼
                    XML生成 (Builder)
```

### 関連ファイル一覧

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