# 画面設計書 22-連絡先新規作成フォーム

## 概要

本ドキュメントは、Fat Free CRMシステムにおける連絡先新規作成フォームの設計を記述する。新規連絡先の登録、取引先との紐付け、カスタムフィールド入力、アクセス権限設定などの機能を提供するモーダル/Ajax形式のフォームである。

### 本画面の処理概要

連絡先新規作成フォームは、営業活動において発生する新規顧客連絡先を効率的にシステムに登録するためのフォームである。

**業務上の目的・背景**：営業担当者が展示会、セミナー、問い合わせなどで獲得した連絡先情報を迅速にシステムへ登録し、後続の営業活動（商談作成、タスク設定）の基盤とする必要がある。本フォームにより、連絡先情報の一元管理と営業プロセスの効率化を実現する。

**画面へのアクセス方法**：
- 連絡先一覧画面の「新規作成」ボタンをクリック
- 取引先詳細画面の「連絡先を追加」リンクをクリック
- 商談詳細画面の「連絡先を追加」リンクをクリック
- ナビゲーションバーの「+」メニューから選択

**主要な操作・処理内容**：
1. 連絡先基本情報（氏名、メールアドレス、電話番号）の入力
2. 所属取引先の選択または新規作成
3. 担当者（割り当て先）の選択
4. 追加情報（役職、部署、住所、携帯電話、FAX）の入力
5. Web presence情報（ブログ、Twitter、LinkedIn、Facebook）の入力
6. カスタムフィールドの入力
7. タグの設定
8. アクセス権限（Public/Private/Shared）の設定
9. 初期コメントの入力
10. 連絡先の保存

**画面遷移**：
- 遷移元：連絡先一覧画面、取引先詳細画面、商談詳細画面
- 遷移先（成功時）：連絡先一覧画面（一覧からの場合）、連絡先詳細画面（詳細画面からの場合）

**権限による表示制御**：ログイン済みユーザーのみアクセス可能。作成時のアクセス権限設定により、他ユーザーからの閲覧・編集可否が決定される。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 17 | 連絡先作成 | 主機能 | 新規連絡先情報入力、取引先同時作成、作成実行 |
| 83 | タグ付け | 補助機能 | 連絡先へのタグ付け |
| 84 | アクセス権限管理 | 補助機能 | 連絡先のアクセス権限設定 |

## 画面種別

登録フォーム（モーダル/Ajax）

## URL/ルーティング

### 表示
- URL: `/contacts/new`
- HTTPメソッド: GET
- コントローラ: `ContactsController#new`

### 登録実行
- URL: `/contacts`
- HTTPメソッド: POST
- コントローラ: `ContactsController#create`

## 入出力項目

### 入力パラメータ（GET /contacts/new）

| パラメータ名 | 型 | 必須 | 説明 |
|-------------|-----|------|------|
| related | String | 任意 | 関連エンティティ（例: "opportunity_123"） |

### 入力パラメータ（POST /contacts）

| パラメータ名 | 型 | 必須 | 説明 |
|-------------|-----|------|------|
| contact[first_name] | String | 条件付き必須 | 名（Setting.require_first_namesがtrueの場合必須） |
| contact[last_name] | String | 条件付き必須 | 姓（Setting.require_last_namesがtrueの場合必須） |
| contact[email] | String | 任意 | メールアドレス |
| contact[phone] | String | 任意 | 電話番号 |
| contact[title] | String | 任意 | 役職 |
| contact[department] | String | 任意 | 部署 |
| contact[alt_email] | String | 任意 | 代替メールアドレス |
| contact[mobile] | String | 任意 | 携帯電話 |
| contact[fax] | String | 任意 | FAX番号 |
| contact[blog] | String | 任意 | ブログURL |
| contact[twitter] | String | 任意 | Twitterアカウント |
| contact[linkedin] | String | 任意 | LinkedInプロフィール |
| contact[facebook] | String | 任意 | Facebookプロフィール |
| contact[do_not_call] | Boolean | 任意 | 電話不可フラグ |
| contact[background_info] | String | 任意 | 背景情報 |
| contact[assigned_to] | Integer | 任意 | 担当者ユーザーID |
| contact[access] | String | 必須 | アクセス権限（Public/Private/Shared） |
| contact[user_ids] | Array | 条件付き | 共有先ユーザーID（accessがSharedの場合） |
| contact[tag_list] | String | 任意 | カンマ区切りタグリスト |
| contact[business_address_attributes] | Hash | 任意 | 勤務先住所 |
| account[id] | Integer | 任意 | 既存取引先ID |
| account[name] | String | 任意 | 新規取引先名 |
| opportunity | Integer | 任意 | 関連商談ID |
| comment_body | String | 任意 | 初期コメント |

### 出力データ（インスタンス変数）

| 変数名 | 型 | 説明 |
|--------|-----|------|
| @contact | Contact | 新規連絡先オブジェクト |
| @account | Account | 新規取引先オブジェクト |
| @accounts | Array | 選択可能取引先一覧 |
| @opportunity | Opportunity | 関連商談（relatedパラメータ指定時） |

## 表示項目

### 基本情報セクション

| 項目名 | フィールド名 | 入力形式 | 必須 | 最大長 | 説明 |
|--------|------------|---------|------|-------|------|
| 名 | first_name | テキスト | 条件付き | 64文字 | Setting.require_first_namesで制御 |
| 姓 | last_name | テキスト | 条件付き | 64文字 | Setting.require_last_namesで制御 |
| メールアドレス | email | メール | - | 254文字 | |
| 電話番号 | phone | 電話 | - | 32文字 | |
| 取引先 | account | 選択/入力 | - | - | 既存選択または新規作成 |
| 担当者 | assigned_to | 選択 | - | - | ユーザー選択 |
| 背景情報 | background_info | テキストエリア | - | 255文字 | Setting.background_infoで表示制御 |
| タグ | tag_list | タグ入力 | - | - | カンマ区切り |

### 追加情報セクション（Extra Info）

| 項目名 | フィールド名 | 入力形式 | 必須 | 最大長 |
|--------|------------|---------|------|-------|
| 役職 | title | テキスト | - | 64文字 |
| 部署 | department | テキスト | - | 64文字 |
| 代替メール | alt_email | メール | - | 254文字 |
| 携帯電話 | mobile | 電話 | - | 32文字 |
| 住所 | business_address | 住所フォーム | - | - |
| FAX | fax | 電話 | - | 32文字 |
| 電話不可 | do_not_call | チェックボックス | - | - |

### Web Presenceセクション

| 項目名 | フィールド名 | 入力形式 | 最大長 |
|--------|------------|---------|-------|
| ブログ | blog | URL | 128文字 |
| Twitter | twitter | テキスト | 128文字 |
| LinkedIn | linkedin | テキスト | 128文字 |
| Facebook | facebook | テキスト | 128文字 |

### アクセス権限セクション

| 項目名 | フィールド名 | 入力形式 | 説明 |
|--------|------------|---------|------|
| アクセス権限 | access | ラジオボタン | Public/Private/Shared |
| 共有先 | user_ids | マルチセレクト | accessがSharedの場合のみ表示 |

## イベント仕様

### 1-作成ボタンクリック

- **トリガー**: 「連絡先を作成」ボタンをクリック
- **処理**:
  1. `crm.create_contact()` JavaScript関数が呼び出される
  2. フォームがAjaxでPOST送信される
  3. サーバー側でバリデーション実行
  4. 取引先の作成または選択処理
  5. 連絡先レコードの作成
  6. 商談との紐付け（指定時）
  7. 初期コメントの追加（入力時）
- **成功時**: 一覧画面更新または詳細画面表示
- **失敗時**: エラーメッセージ表示、フォーム再表示

### 2-キャンセルリンククリック

- **トリガー**: 「キャンセル」リンクをクリック
- **処理**: Ajax経由でフォームを閉じる
- **遷移先**: フォーム表示前の状態に戻る

### 3-取引先選択/新規作成切替

- **トリガー**: 取引先ドロップダウンまたはテキスト入力
- **処理**:
  - 既存取引先選択時: account[id]にIDがセット
  - 新規入力時: account[name]に入力値がセット
- **備考**: account_select_or_createヘルパーによる動的切替

### 4-追加情報セクション展開/折りたたみ

- **トリガー**: セクションヘッダーをクリック
- **処理**: セクションの展開/折りたたみ状態を切り替え
- **備考**: セッションに状態を保持

### 5-Web Presenceセクション展開/折りたたみ

- **トリガー**: セクションヘッダーをクリック
- **処理**: セクションの展開/折りたたみ状態を切り替え

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 連絡先作成 | contacts | INSERT | 連絡先レコード作成 |
| 取引先新規作成 | accounts | INSERT | 新規取引先レコード作成（指定時） |
| 取引先紐付け | account_contacts | INSERT | 連絡先-取引先関連レコード作成 |
| 商談紐付け | contact_opportunities | INSERT | 連絡先-商談関連レコード作成（指定時） |
| コメント作成 | comments | INSERT | 初期コメント作成（入力時） |
| 住所作成 | addresses | INSERT | 勤務先住所作成（入力時） |
| タグ付与 | taggings | INSERT | タグ関連レコード作成 |
| バージョン記録 | versions | INSERT | 作成履歴記録（PaperTrail） |

### テーブル別更新項目詳細

#### contacts

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | user_id | current_user.id | 作成者 |
| INSERT | assigned_to | フォーム入力値 | 担当者 |
| INSERT | first_name | フォーム入力値 | 名 |
| INSERT | last_name | フォーム入力値 | 姓 |
| INSERT | email | フォーム入力値 | メールアドレス |
| INSERT | phone | フォーム入力値 | 電話番号 |
| INSERT | title | フォーム入力値 | 役職 |
| INSERT | department | フォーム入力値 | 部署 |
| INSERT | access | フォーム入力値 | アクセス権限 |
| INSERT | created_at | 現在日時 | 作成日時 |
| INSERT | updated_at | 現在日時 | 更新日時 |

#### accounts（新規作成時）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | user_id | current_user.id | 作成者 |
| INSERT | name | フォーム入力値 | 取引先名 |
| INSERT | access | Setting.default_access | デフォルトアクセス権限 |

#### account_contacts

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

## メッセージ仕様

| 種別 | メッセージキー | 表示条件 |
|------|---------------|---------|
| エラー | :missing_first_name | 名が未入力（必須設定時） |
| エラー | :missing_last_name | 姓が未入力（必須設定時） |
| エラー | :share_contact | Shared選択時に共有先未指定 |
| エラー | バリデーションエラー | 各フィールドの最大長超過等 |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| 関連エンティティが見つからない | respond_to_related_not_foundでエラーレスポンス |
| バリデーションエラー | フォーム再表示、エラーメッセージ表示 |
| 取引先作成失敗 | 連絡先作成も中止、エラー表示 |

## 備考

- フォームはremote: trueでAjax送信される
- one_submit_only属性により二重送信防止
- 取引先は既存選択と新規作成の両方に対応
- カスタムフィールドはFieldGroupの設定に応じて動的表示
- セクションの展開/折りたたみ状態はセッションに保持

---

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

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

### 推奨読解順序

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

まず、連絡先作成時に関連するデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | contact.rb | `app/models/entities/contact.rb` | バリデーション（96-112行目）、save_with_account_and_permissions（136-141行目） |
| 1-2 | account.rb | `app/models/entities/account.rb` | create_or_select_forメソッド |
| 1-3 | account_contact.rb | `app/models/relationships/account_contact.rb` | 連絡先-取引先関連モデル |

**読解のコツ**: validates_presence_of, validates_length_ofによるバリデーションルールを確認。save_with_account_and_permissionsメソッドで取引先との紐付け処理を理解する。

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

コントローラーのnewとcreateアクションを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | contacts_controller.rb | `app/controllers/entities/contacts_controller.rb` | newアクション（36-50行目）、createアクション（63-74行目） |
| 2-2 | entities_controller.rb | `app/controllers/entities_controller.rb` | 基底クラスの共通処理 |

**主要処理フロー（new）**:
1. **37行目**: デフォルト属性の設定（user, access, assigned_to）
2. **38行目**: 新規取引先オブジェクト生成
3. **40-46行目**: 関連エンティティの取得（relatedパラメータ処理）

**主要処理フロー（create）**:
1. **64行目**: コメント本文の取得
2. **66行目**: save_with_account_and_permissionsによる保存
3. **67行目**: コメント追加
4. **68行目**: 一覧画面からの呼び出し時の連絡先再取得

#### 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` | 追加情報セクション |
| 3-4 | _web.html.haml | `app/views/contacts/_web.html.haml` | Web Presenceセクション |

**主要処理フロー**:
- **1行目**: simple_form_forでフォーム開始、remote: true
- **8-14行目**: 各セクションのパーシャルをレンダリング
- **18行目**: 作成ボタン、onclick="crm.create_contact()"

#### Step 4: モデルの保存ロジックを理解する

取引先との紐付けを含む保存処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | contact.rb | `app/models/entities/contact.rb` | save_with_account_and_permissions（136-141行目）、save_account（217-225行目） |

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

```
ContactsController#new
    │
    ├─ before_action :get_accounts
    │      └─ Account.my(current_user).order('name')
    │
    ├─ Contact.new (CanCanによる自動生成)
    │
    ├─ @contact.attributes = {...} (デフォルト値設定)
    │
    ├─ Account.new(user: current_user)
    │
    └─ respond_with(@contact)
           └─ format.js → new.js.erb → _new.html.haml

ContactsController#create
    │
    ├─ before_action :get_accounts
    │
    ├─ @contact.save_with_account_and_permissions(params)
    │      │
    │      ├─ save_account(params)
    │      │      └─ Account.create_or_select_for(self, account_params)
    │      │
    │      ├─ contact.save
    │      │
    │      └─ opportunities << Opportunity.find(...)
    │
    ├─ @contact.add_comment_by_user(@comment_body, current_user)
    │
    └─ respond_with(@contact)
           ├─ format.js (成功時) → create.js.erb
           └─ format.js (失敗時) → create.js.erb (エラー表示)
```

### データフロー図

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

フォーム入力
    │
    ├── contact[*] ──────▶ ContactsController#create
    │                           │
    ├── account[id/name] ──────┤
    │                           │
    ├── opportunity ────────────┤
    │                           │
    └── comment_body ───────────┤
                                │
                                ├──▶ Account.create_or_select_for
                                │         │
                                │         └──▶ [accounts] INSERT/SELECT
                                │
                                ├──▶ Contact#save
                                │         │
                                │         └──▶ [contacts] INSERT
                                │
                                ├──▶ AccountContact.create
                                │         │
                                │         └──▶ [account_contacts] INSERT
                                │
                                ├──▶ Contact#add_comment_by_user
                                │         │
                                │         └──▶ [comments] INSERT
                                │
                                └────────────────────────▶ JS Response
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| contact.rb | `app/models/entities/contact.rb` | モデル | 連絡先エンティティ、保存ロジック |
| contacts_controller.rb | `app/controllers/entities/contacts_controller.rb` | コントローラ | new/createアクション |
| _new.html.haml | `app/views/contacts/_new.html.haml` | テンプレート | 新規作成フォーム |
| _top_section.html.haml | `app/views/contacts/_top_section.html.haml` | テンプレート | 基本情報セクション |
| _extra.html.haml | `app/views/contacts/_extra.html.haml` | テンプレート | 追加情報セクション |
| _web.html.haml | `app/views/contacts/_web.html.haml` | テンプレート | Web Presenceセクション |
| _permissions.html.haml | `app/views/entities/_permissions.html.haml` | テンプレート | アクセス権限セクション |
| _add_comment.html.haml | `app/views/shared/_add_comment.html.haml` | テンプレート | コメント入力 |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義 |
| contacts.js | `app/assets/javascripts/crm_contacts.js.coffee` | JavaScript | crm.create_contact()等 |
