# 機能設計書 4-取引先一覧表示

## 概要

本ドキュメントは、Fat Free CRMシステムにおける取引先一覧表示機能の設計を定義する。この機能は、自分がアクセス可能な取引先を一覧表示し、カテゴリでのフィルタリングとCSV/XLS形式でのエクスポートを提供する。

### 本機能の処理概要

取引先一覧表示機能は、CRMシステムにおける取引先（Account）データの一覧表示・検索・フィルタリングを提供する中核機能である。営業活動の基盤となる顧客企業情報を効率的に管理・閲覧できる。

**業務上の目的・背景**：営業活動において、取引先（顧客企業）の情報管理は最も重要な業務の一つである。営業担当者は自分が担当する取引先を素早く確認し、必要に応じてフィルタリングや検索を行う必要がある。また、外部システムや報告書作成のためにデータをエクスポートするニーズも存在する。本機能はこれらの要件を満たすために設計されている。

**機能の利用シーン**：
- 営業担当者が担当取引先の一覧を確認する場面
- マネージャーがカテゴリ別に取引先を絞り込んで分析する場面
- 取引先データをExcelで集計・報告するためにエクスポートする場面
- 特定のキーワードで取引先を検索する場面

**主要な処理内容**：
1. ユーザーがアクセス可能な取引先の取得
2. カテゴリフィルタリングの適用
3. テキスト検索・高度な検索（Ransack）の適用
4. ページネーションの適用
5. CSV/XLS形式でのエクスポート

**関連システム・外部連携**：CSV/XLSエクスポート機能によりExcel等の外部ツールとのデータ連携が可能。

**権限による制御**：`uses_user_permissions`によりアクセス権限が制御される。ユーザーは自分が所有する取引先、割り当てられた取引先、または共有された取引先のみを閲覧可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 7 | 取引先一覧画面 | 主画面 | 取引先の一覧表示、カテゴリフィルタリング、CSV/XLSエクスポート |

## 機能種別

データ表示・検索・エクスポート（READ操作）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| page | Integer | No | ページ番号 | 正の整数 |
| per_page | Integer | No | 1ページあたりの件数 | 1-200の範囲 |
| query | String | No | 検索キーワード | - |
| category | String | No | カテゴリフィルタ | 設定で定義されたカテゴリ値 |
| q | Hash | No | Ransack検索条件 | - |
| sort_by | String | No | ソート条件 | Account.sort_by_mapで定義された値 |

### 入力データソース

- URLパラメータ
- セッション情報（current_user、フィルタ設定）
- ユーザー設定（Preference）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| @accounts | ActiveRecord::Relation<Account> | 取引先一覧 |
| @account_category_total | HashWithIndifferentAccess | カテゴリ別件数 |
| @per_page | Integer | 1ページあたりの件数 |
| @sort_by | String | 現在のソート条件 |
| @search_results_count | Integer | 検索結果総件数 |

### 出力先

- HTML画面表示（デフォルト）
- CSV形式（format: csv）
- XLS形式（format: xls）

## 処理フロー

### 処理シーケンス

```
1. before_action実行
   ├─ set_current_tab: タブ状態設定
   ├─ set_view: 表示形式設定
   ├─ set_options: ページング・ソート設定
   ├─ load_ransack_search: 高度な検索条件ロード
   ├─ load_and_authorize_resource: リソース認可
   └─ get_data_for_sidebar: サイドバーデータ取得
2. get_accounts実行（get_list_of_recordsのエイリアス）
   ├─ ransack_searchによる検索条件適用
   ├─ sessionからフィルタ取得・適用
   ├─ text_search実行（query指定時）
   ├─ タグ検索実行（#タグ指定時）
   ├─ ソート適用
   └─ ページネーション適用
3. respond_with実行
   ├─ HTML: index.html.hamlレンダリング
   ├─ CSV: CSVデータ生成
   └─ XLS: index.xls.builderレンダリング
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B[認証・認可チェック]
    B --> C[サイドバーデータ取得]
    C --> D[検索条件構築]
    D --> E{フィルタあり?}
    E -->|Yes| F[カテゴリフィルタ適用]
    E -->|No| G[検索実行]
    F --> G
    G --> H{query指定?}
    H -->|Yes| I[テキスト検索適用]
    H -->|No| J[ソート適用]
    I --> J
    J --> K[ページネーション適用]
    K --> L{フォーマット判定}
    L -->|HTML| M[HTML画面レンダリング]
    L -->|CSV| N[CSVエクスポート]
    L -->|XLS| O[XLSエクスポート]
    M --> P[終了]
    N --> P
    O --> P
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | デフォルトページサイズ | 1ページあたり20件 | per_page未指定時 |
| BR-002 | 最大ページサイズ | 1ページあたり最大200件 | per_page指定時 |
| BR-003 | デフォルトソート | created_at DESC（作成日降順） | sort_by未指定時 |
| BR-004 | アクセス権限 | 所有・割当・共有された取引先のみ表示 | 常時 |
| BR-005 | エクスポート時ページング無効 | CSV/XLSエクスポート時はページネーション無効 | format: csv/xls |

### 計算ロジック

**カテゴリ別件数計算**：
```ruby
@account_category_total[:all] = Account.my(current_user).count
@account_category_total[:other] = @account_category_total[:all] - categorized
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 取引先取得 | accounts | SELECT | ユーザーがアクセス可能な取引先を取得 |
| カテゴリ集計 | accounts | SELECT | カテゴリ別の件数を集計 |
| 関連データ | users, tags, opportunities | SELECT | includesによる関連データ取得 |

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

#### accounts

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | my(current_user)スコープ | アクセス権限フィルタリング |
| SELECT | category, COUNT(*) | GROUP BY category | サイドバー集計用 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | 認証エラー | 未ログイン状態でアクセス | ログイン画面へリダイレクト |
| 403 | 認可エラー | アクセス権限がない | エラーメッセージ表示 |

### リトライ仕様

本機能ではリトライ処理は実装されていない。

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

本機能はSELECT操作のみであり、トランザクション制御は不要。

## パフォーマンス要件

- 画面表示時間: 3秒以内を目標
- includesによるN+1クエリの回避
- ページネーションによるデータ量制限

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

- 認証: Deviseによるユーザー認証が必須
- 認可: CanCanによるアクセス権限制御
- SQLインジェクション: Ransackによる安全なクエリ構築

## 備考

- 表示形式（長形式/短形式）はユーザー設定で切り替え可能
- フィルタ設定はセッションに保存され、ページ遷移後も維持される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | account.rb | `app/models/entities/account.rb` | Accountモデルの属性・関連・スコープ |
| 1-2 | ability.rb | `app/models/users/ability.rb` | CanCanによる権限定義 |
| 1-3 | permission.rb | `app/models/users/permission.rb` | 共有権限の構造 |

**読解のコツ**:
- スキーマ情報（8-28行目）でカラム構成を確認
- `uses_user_permissions`、`acts_as_taggable_on`等のマクロに注目
- `scope :my`の定義（lib/fat_free_crm/permissions.rb）を確認

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | routes.rb | `config/routes.rb` | /accountsルート（44-62行目） |
| 2-2 | accounts_controller.rb | `app/controllers/entities/accounts_controller.rb` | indexアクション |

**主要処理フロー**:
1. **13-20行目**: indexアクション - 取引先取得とレスポンス
2. **14行目**: get_accounts呼び出し（get_list_of_recordsのエイリアス）
3. **17-18行目**: XLS/CSV形式のレンダリング

#### Step 3: 基底クラスのロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | entities_controller.rb | `app/controllers/entities_controller.rb` | get_list_of_recordsメソッド |

**主要処理フロー**:
- **138-178行目**: get_list_of_records - メインの取得ロジック
- **146行目**: ransack_search.resultによる検索条件適用
- **154行目**: text_searchによるキーワード検索
- **172行目**: paginateによるページネーション

#### Step 4: サイドバーデータ取得を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | accounts_controller.rb | `app/controllers/entities/accounts_controller.rb` | get_data_for_sidebarメソッド |

**主要処理フロー**:
- **153-162行目**: get_data_for_sidebar - カテゴリ別件数集計

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | index.html.haml | `app/views/accounts/index.html.haml` | 一覧画面レイアウト |
| 5-2 | _account.html.haml | `app/views/accounts/_account.html.haml` | 取引先行表示パーシャル |
| 5-3 | _sidebar_index.html.haml | `app/views/accounts/_sidebar_index.html.haml` | サイドバー |
| 5-4 | index.xls.builder | `app/views/accounts/index.xls.builder` | XLSエクスポート |

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

```
AccountsController#index
    │
    ├─ before_action
    │   ├─ set_current_tab (ApplicationController)
    │   ├─ set_view (EntitiesController)
    │   ├─ set_options (EntitiesController)
    │   ├─ load_ransack_search (EntitiesController)
    │   ├─ load_and_authorize_resource (CanCan)
    │   └─ get_data_for_sidebar
    │
    ├─ get_accounts (alias: get_list_of_records)
    │   ├─ ransack_search.result
    │   ├─ scope.state(filter)
    │   ├─ scope.text_search(query)
    │   ├─ scope.tagged_with(tags)
    │   ├─ order_by_attributes
    │   ├─ scope.paginate
    │   └─ scope.includes(*list_includes)
    │
    └─ respond_with(@accounts)
        ├─ format.html
        ├─ format.csv
        └─ format.xls
```

### データフロー図

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

URLパラメータ ───▶ AccountsController#index ───▶ @accounts
(page,query,         │                          @account_category_total
 category)           │                               │
                     ▼                               ▼
              get_list_of_records              index.html.haml
                     │                         index.xls.builder
                     ├─ ransack_search         CSV出力
                     ├─ state filter               │
                     ├─ text_search               │
                     ├─ order                     │
                     └─ paginate                  │
                     │                            │
                     ▼                            ▼
              データベース                   ブラウザ/ファイル
              (accounts, users,
               tags, opportunities)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| accounts_controller.rb | `app/controllers/entities/accounts_controller.rb` | ソース | メインコントローラー |
| entities_controller.rb | `app/controllers/entities_controller.rb` | ソース | 基底コントローラー |
| account.rb | `app/models/entities/account.rb` | ソース | 取引先モデル |
| ability.rb | `app/models/users/ability.rb` | ソース | 権限定義 |
| index.html.haml | `app/views/accounts/index.html.haml` | テンプレート | 一覧画面 |
| _account.html.haml | `app/views/accounts/_account.html.haml` | テンプレート | 取引先行 |
| _sidebar_index.html.haml | `app/views/accounts/_sidebar_index.html.haml` | テンプレート | サイドバー |
| _index_long.html.haml | `app/views/accounts/_index_long.html.haml` | テンプレート | 長形式表示 |
| _index_brief.html.haml | `app/views/accounts/_index_brief.html.haml` | テンプレート | 短形式表示 |
| index.xls.builder | `app/views/accounts/index.xls.builder` | テンプレート | XLSエクスポート |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義 |
