# 機能設計書 17-連絡先作成

## 概要

本ドキュメントは、Fat Free CRMにおける連絡先作成機能の設計を記述する。新規連絡先を作成し、取引先との紐付けも同時に行える機能である。

### 本機能の処理概要

**業務上の目的・背景**：営業活動において、新規顧客担当者や見込み顧客の情報をシステムに登録する必要がある。連絡先は商談の主要な関係者として紐付けられ、顧客との関係性管理の基盤となる。取引先との同時登録により、組織と個人の関係性を一度に登録できる。

**機能の利用シーン**：新規顧客の担当者情報を登録する場合、展示会やセミナーで名刺交換した人の情報を登録する場合、リードから変換された連絡先以外に追加の連絡先を登録する場合に利用される。既存取引先の新しい担当者を追加する場合にも使用される。

**主要な処理内容**：
1. 新規連絡先フォームの表示（newアクション）
2. 入力された連絡先情報のバリデーション
3. 取引先の作成または選択（同時登録の場合）
4. 連絡先レコードのデータベースへの保存
5. 商談との紐付け（オプション）
6. コメントの同時追加（オプション）
7. 成功/失敗に応じたレスポンス生成

**関連システム・外部連携**：特になし。内部のCRMデータとして管理される。

**権限による制御**：ログインユーザーのみが連絡先を作成可能。作成された連絡先の所有者（user_id）は作成者となる。アクセス権限（Public/Private/Shared）を設定可能で、Sharedの場合は共有先ユーザーの指定が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 22 | 連絡先新規作成フォーム | 主画面 | 新規連絡先情報入力、取引先同時作成、作成実行 |
| 20 | 連絡先一覧画面 | 参照画面 | 作成完了後の遷移先 |

## 機能種別

CRUD操作（Create）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| contact[first_name] | String | 条件付 | 名（最大64文字） | Setting.require_first_namesで必須化 |
| contact[last_name] | String | 条件付 | 姓（最大64文字） | Setting.require_last_namesで必須化 |
| contact[title] | String | No | 役職（最大64文字） | 最大64文字 |
| contact[department] | String | No | 部門（最大64文字） | 最大64文字 |
| contact[email] | String | No | メールアドレス | 最大254文字 |
| contact[alt_email] | String | No | 代替メール | 最大254文字 |
| contact[phone] | String | No | 電話番号 | 最大32文字 |
| contact[mobile] | String | No | 携帯電話 | 最大32文字 |
| contact[fax] | String | No | FAX | 最大32文字 |
| contact[blog] | String | No | ブログURL | 最大128文字 |
| contact[linkedin] | String | No | LinkedIn URL | 最大128文字 |
| contact[facebook] | String | No | Facebook URL | 最大128文字 |
| contact[twitter] | String | No | Twitter | 最大128文字 |
| contact[born_on] | Date | No | 生年月日 | 日付形式 |
| contact[do_not_call] | Boolean | No | 電話連絡禁止フラグ | - |
| contact[access] | String | No | アクセス権限 | Public/Private/Shared |
| contact[assigned_to] | Integer | No | 担当者ID | 存在するユーザーID |
| account[id] | Integer | No | 既存取引先ID | 存在する取引先ID |
| account[name] | String | No | 新規取引先名 | - |
| opportunity | Integer | No | 紐付け商談ID | 存在する商談ID |
| comment_body | Text | No | コメント | テキスト |

### 入力データソース

- フォーム入力（AJAX POST）
- セッション情報（現在のユーザー情報）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| @contact | Contact | 作成された連絡先オブジェクト |
| @account | Account | 作成または選択された取引先オブジェクト |
| @accounts | Array | 取引先選択リスト |
| @contacts | Array | 連絡先一覧（インデックスページからの呼び出し時） |

### 出力先

- AJAX（JS）レスポンス
- JSON/XMLレスポンス（API経由）

## 処理フロー

### 処理シーケンス

```
1. ユーザー認証確認
   └─ authenticate_user!でログイン状態を確認
2. 取引先一覧取得
   └─ get_accountsでユーザーがアクセス可能な取引先リストを取得
3. 新規連絡先オブジェクト生成（newアクション）
   └─ デフォルト値（user, access, assigned_to）を設定
4. フォーム入力受付
   └─ パラメータの受け取り
5. 連絡先保存（createアクション）
   └─ save_with_account_and_permissionsで取引先紐付けを含む保存
6. コメント追加（オプション）
   └─ comment_bodyがある場合、コメントを追加
7. レスポンス生成
   └─ 成功/失敗に応じたJSレスポンス
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B[ユーザー認証]
    B --> C{認証OK?}
    C -->|No| D[ログインページへリダイレクト]
    C -->|Yes| E[取引先一覧取得]
    E --> F{アクション判定}
    F -->|new| G[新規連絡先オブジェクト生成]
    G --> H[デフォルト値設定]
    H --> I[フォーム表示]
    F -->|create| J[パラメータ取得]
    J --> K[取引先処理]
    K --> L{取引先指定あり?}
    L -->|新規| M[取引先作成]
    L -->|既存| N[取引先選択]
    L -->|なし| O[取引先なし]
    M --> P[連絡先保存]
    N --> P
    O --> P
    P --> Q{保存成功?}
    Q -->|No| R[エラーメッセージ表示]
    Q -->|Yes| S{商談紐付け?}
    S -->|Yes| T[商談紐付け]
    S -->|No| U{コメントあり?}
    T --> U
    U -->|Yes| V[コメント追加]
    U -->|No| W[成功レスポンス]
    V --> W
    R --> X[失敗レスポンス]
    W --> Y[終了]
    X --> Y
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-17-01 | 名前必須設定 | Setting.require_first_names/last_namesに基づき必須化 | 設定による |
| BR-17-02 | 共有時のユーザー指定 | access=Sharedの場合、共有先ユーザー必須 | Shared選択時 |
| BR-17-03 | 取引先同時作成 | 取引先名が指定され、IDがない場合は新規作成 | 取引先入力時 |
| BR-17-04 | 取引先選択 | 取引先IDが指定されている場合は既存を使用 | 取引先ID指定時 |
| BR-17-05 | 商談紐付け | opportunityパラメータで商談と連絡先を紐付け | 商談ID指定時 |

### 計算ロジック

取引先処理ロジック：
```ruby
def save_account(params)
  account_params = params[:account]
  valid_account = account_params && (account_params[:id].present? || account_params[:name].present?)
  self.account = if valid_account
                   Account.create_or_select_for(self, account_params)
                 else
                   nil
                 end
end
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 連絡先作成 | contacts | INSERT | 新規連絡先レコード挿入 |
| 取引先作成 | accounts | INSERT | 新規取引先レコード挿入（同時作成時） |
| 連絡先-取引先紐付け | account_contacts | INSERT | 紐付けレコード挿入 |
| 連絡先-商談紐付け | contact_opportunities | INSERT | 紐付けレコード挿入（商談指定時） |
| コメント追加 | comments | INSERT | 連絡先へのコメント追加 |
| 権限設定 | permissions | INSERT | Shared時の権限レコード作成 |
| アクティビティ記録 | versions | INSERT | 作成イベント記録（PaperTrail） |

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

#### contacts

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | user_id | 現在のユーザーID | |
| INSERT | assigned_to | フォーム入力値またはNULL | |
| INSERT | first_name | フォーム入力値 | 設定で必須可 |
| INSERT | last_name | フォーム入力値 | 設定で必須可 |
| INSERT | access | フォーム入力値（デフォルト: Setting.default_access） | |
| INSERT | title | フォーム入力値またはNULL | |
| INSERT | department | フォーム入力値またはNULL | |
| INSERT | email | フォーム入力値またはNULL | |
| INSERT | phone | フォーム入力値またはNULL | |
| INSERT | created_at | 現在日時 | 自動設定 |
| INSERT | updated_at | 現在日時 | 自動設定 |

#### account_contacts

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | account_id | 取引先ID | |
| INSERT | contact_id | 作成された連絡先ID | |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | 認証エラー | 未ログイン状態でアクセス | ログインページへリダイレクト |
| 422 | バリデーションエラー | 必須項目未入力等 | エラーメッセージ表示 |
| 422 | 共有設定エラー | Shared選択時に共有先未指定 | エラーメッセージ表示 |

### リトライ仕様

本機能にリトライ処理は実装されていない。バリデーションエラー時はフォームを再表示し、ユーザーに修正を促す。

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

連絡先保存、取引先処理、商談紐付けは可能な限り同一トランザクションで実行される。

## パフォーマンス要件

- フォーム表示は1秒以内を目標
- 保存処理は2秒以内を目標

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

- CanCanによるアクセス権限チェック実施
- CSRF対策（protect_from_forgery）
- Strong Parametersによる許可パラメータ制限
- SQLインジェクション対策（ActiveRecord使用）
- XSS対策（ERB::Util使用）

## 備考

- タグ付け機能は別機能（No.83）として管理
- アクセス権限管理は別機能（No.84）として管理

---

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

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

### 推奨読解順序

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | contact.rb | `app/models/entities/contact.rb` | Contactモデルの属性、バリデーション、save_with_account_and_permissions |

**読解のコツ**:
- **96-98行目**: バリデーション定義（名前の必須化設定）
- **136-141行目**: `save_with_account_and_permissions`メソッドで取引先紐付けと保存
- **217-225行目**: `save_account`メソッドで取引先処理

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

処理の起点となるコントローラーのnew/createアクションを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | contacts_controller.rb | `app/controllers/entities/contacts_controller.rb` | new/createアクションの処理内容 |

**主要処理フロー**:
1. **9行目**: `before_action :get_accounts`で取引先リスト取得
2. **36-50行目**: newアクション - デフォルト値設定、関連エンティティ処理
3. **63-74行目**: createアクション - save_with_account_and_permissions呼び出し

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | _new.html.haml | `app/views/contacts/_new.html.haml` | 新規作成フォームの構造 |
| 3-2 | _top_section.html.haml | `app/views/contacts/_top_section.html.haml` | フォーム上部セクション |
| 3-3 | _extra.html.haml | `app/views/contacts/_extra.html.haml` | 追加情報入力セクション |

**主要処理フロー**:
- **1行目**: simple_form_forでフォーム生成
- **4行目**: 商談IDの隠しフィールド
- **8-14行目**: 各セクションのパーシャルレンダリング
- **18行目**: `crm.create_contact()`でフォーム送信

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

```
ContactsController#new
    │
    ├─ get_accounts
    │      └─ Account.my(current_user).order('name')
    │
    └─ デフォルト値設定

ContactsController#create
    │
    ├─ Contact#save_with_account_and_permissions
    │      ├─ save_account
    │      │      └─ Account.create_or_select_for
    │      │
    │      ├─ Contact#save
    │      │      └─ バリデーション実行
    │      │
    │      └─ opportunities紐付け
    │
    ├─ Contact#add_comment_by_user
    │
    └─ get_contacts（インデックスページからの場合）
```

### データフロー図

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

フォーム入力 ───────▶ ContactsController#create ───▶ JSレスポンス
                          │
account入力               ├─▶ save_account
                          │     └─▶ Account作成または選択
                          │
contact入力               ├─▶ Contact保存
                          │     └─▶ contacts INSERT
                          │
opportunity               ├─▶ 商談紐付け
                          │     └─▶ contact_opportunities INSERT
                          │
comment_body              └─▶ コメント追加
                                └─▶ comments INSERT
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| contacts_controller.rb | `app/controllers/entities/contacts_controller.rb` | コントローラー | 連絡先関連アクションの処理 |
| entities_controller.rb | `app/controllers/entities_controller.rb` | コントローラー | エンティティ共通処理 |
| contact.rb | `app/models/entities/contact.rb` | モデル | 連絡先エンティティ定義 |
| _new.html.haml | `app/views/contacts/_new.html.haml` | ビュー | 新規作成フォームテンプレート |
| new.js.haml | `app/views/contacts/new.js.haml` | ビュー | 新規フォーム表示用JSテンプレート |
| create.js.haml | `app/views/contacts/create.js.haml` | ビュー | 作成完了用JSテンプレート |
| _top_section.html.haml | `app/views/contacts/_top_section.html.haml` | ビュー | フォーム上部パーシャル |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義 |
