# 画面設計書 23-連絡先編集フォーム

## 概要

本ドキュメントは、Fat Free CRMシステムにおける連絡先編集フォームの設計を記述する。既存連絡先の情報更新、取引先の変更、アクセス権限の変更などの機能を提供するモーダル/Ajax形式のフォームである。

### 本画面の処理概要

連絡先編集フォームは、既存の連絡先情報を更新するためのフォームである。新規作成フォームと同様の入力項目を持ちながら、既存データの表示と更新処理を行う。

**業務上の目的・背景**：営業活動において、連絡先情報は頻繁に変更される（役職変更、部署異動、連絡先変更等）。本フォームにより、最新の連絡先情報を維持し、営業活動の精度向上と顧客対応の品質確保を実現する。

**画面へのアクセス方法**：
- 連絡先詳細画面の「編集」リンクをクリック
- 連絡先一覧画面の各連絡先の編集アイコンをクリック

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

**画面遷移**：
- 遷移元：連絡先詳細画面、連絡先一覧画面
- 遷移先（成功時）：遷移元画面（更新反映）

**権限による表示制御**：連絡先の編集権限を持つユーザーのみアクセス可能。作成者、担当者、または共有設定されたユーザーが編集可能。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 18 | 連絡先編集 | 主機能 | 連絡先情報更新 |
| 83 | タグ付け | 補助機能 | 連絡先へのタグ付け |
| 84 | アクセス権限管理 | 補助機能 | 連絡先のアクセス権限設定 |

## 画面種別

編集フォーム（モーダル/Ajax）

## URL/ルーティング

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

### 更新実行
- URL: `/contacts/:id`
- HTTPメソッド: PATCH/PUT
- コントローラ: `ContactsController#update`

## 入出力項目

### 入力パラメータ（GET /contacts/:id/edit）

| パラメータ名 | 型 | 必須 | 説明 |
|-------------|-----|------|------|
| id | Integer | 必須 | 連絡先ID |
| previous | Integer | 任意 | 前の連絡先ID（一覧からの連続編集時） |

### 入力パラメータ（PATCH /contacts/:id）

| パラメータ名 | 型 | 必須 | 説明 |
|-------------|-----|------|------|
| id | Integer | 必須 | 連絡先ID |
| contact[first_name] | String | 条件付き必須 | 名 |
| contact[last_name] | String | 条件付き必須 | 姓 |
| 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 | 必須 | アクセス権限 |
| contact[user_ids] | Array | 条件付き | 共有先ユーザーID |
| contact[tag_list] | String | 任意 | タグリスト |
| contact[business_address_attributes] | Hash | 任意 | 勤務先住所 |
| account[id] | Integer | 任意 | 既存取引先ID |
| account[name] | String | 任意 | 新規取引先名 |

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

| 変数名 | 型 | 説明 |
|--------|-----|------|
| @contact | Contact | 編集対象連絡先オブジェクト |
| @account | Account | 関連取引先（既存または新規） |
| @accounts | Array | 選択可能取引先一覧 |
| @previous | Contact/Integer | 前の連絡先（一覧からの連続編集時） |

## 表示項目

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

| 項目名 | フィールド名 | 入力形式 | 必須 | 最大長 | 初期値 |
|--------|------------|---------|------|-------|-------|
| 名 | first_name | テキスト | 条件付き | 64文字 | @contact.first_name |
| 姓 | last_name | テキスト | 条件付き | 64文字 | @contact.last_name |
| メールアドレス | email | メール | - | 254文字 | @contact.email |
| 電話番号 | phone | 電話 | - | 32文字 | @contact.phone |
| 取引先 | account | 選択/入力 | - | - | @contact.account |
| 担当者 | assigned_to | 選択 | - | - | @contact.assigned_to |
| 背景情報 | background_info | テキストエリア | - | 255文字 | @contact.background_info |
| タグ | tag_list | タグ入力 | - | - | @contact.tag_list |

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

| 項目名 | フィールド名 | 入力形式 | 初期値 |
|--------|------------|---------|-------|
| 役職 | title | テキスト | @contact.title |
| 部署 | department | テキスト | @contact.department |
| 代替メール | alt_email | メール | @contact.alt_email |
| 携帯電話 | mobile | 電話 | @contact.mobile |
| 住所 | business_address | 住所フォーム | @contact.business_address |
| FAX | fax | 電話 | @contact.fax |
| 電話不可 | do_not_call | チェックボックス | @contact.do_not_call |

### Web Presenceセクション

| 項目名 | フィールド名 | 入力形式 | 初期値 |
|--------|------------|---------|-------|
| ブログ | blog | URL | @contact.blog |
| Twitter | twitter | テキスト | @contact.twitter |
| LinkedIn | linkedin | テキスト | @contact.linkedin |
| Facebook | facebook | テキスト | @contact.facebook |

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

| 項目名 | フィールド名 | 入力形式 | 初期値 |
|--------|------------|---------|-------|
| アクセス権限 | access | ラジオボタン | @contact.access |
| 共有先 | user_ids | マルチセレクト | @contact.user_ids |

## イベント仕様

### 1-保存ボタンクリック

- **トリガー**: 「連絡先を保存」ボタンをクリック
- **処理**:
  1. `crm.save_contact()` JavaScript関数が呼び出される
  2. フォームがAjaxでPATCH送信される
  3. サーバー側でバリデーション実行
  4. 取引先の変更処理（必要に応じて）
  5. 連絡先レコードの更新
- **成功時**: 遷移元画面更新
- **失敗時**: エラーメッセージ表示、フォーム再表示

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

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

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

- **トリガー**: 取引先ドロップダウンまたはテキスト入力
- **処理**:
  - 既存取引先選択時: account[id]にIDがセット
  - 新規入力時: account[name]に入力値がセット
  - 「-- None --」選択時: 取引先との紐付け解除
- **備考**: 編集時は「-- None --」オプションが追加される

### 4-セクション展開/折りたたみ

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

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 連絡先更新 | contacts | UPDATE | 連絡先レコード更新 |
| 取引先新規作成 | accounts | INSERT | 新規取引先作成（指定時） |
| 取引先紐付け変更 | account_contacts | INSERT/DELETE | 関連レコード変更 |
| 住所更新 | addresses | UPDATE/INSERT/DELETE | 住所レコード更新 |
| タグ変更 | taggings | INSERT/DELETE | タグ関連更新 |
| バージョン記録 | versions | INSERT | 更新履歴記録 |

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

#### contacts

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | first_name | フォーム入力値 | |
| UPDATE | last_name | フォーム入力値 | |
| UPDATE | email | フォーム入力値 | |
| UPDATE | phone | フォーム入力値 | |
| UPDATE | title | フォーム入力値 | |
| UPDATE | department | フォーム入力値 | |
| UPDATE | assigned_to | フォーム入力値 | |
| UPDATE | access | フォーム入力値 | |
| UPDATE | updated_at | 現在日時 | |

#### account_contacts

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| DELETE | - | 取引先変更時に既存関連を削除 | |
| INSERT | account_id | 新しい取引先ID | |
| INSERT | contact_id | @contact.id | |

## メッセージ仕様

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

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| 連絡先が存在しない | 404エラー |
| 編集権限なし | CanCanによるアクセス拒否 |
| バリデーションエラー | フォーム再表示、エラーメッセージ表示 |
| 取引先作成失敗 | エラー表示 |

## 備考

- フォームはremote: trueでAjax送信される
- one_submit_only属性により二重送信防止
- 編集時は取引先選択に「-- None --」オプションが追加される
- edit: true フラグにより、新規作成と編集で表示が微妙に異なる
- カスタムフィールドはFieldGroupの設定に応じて動的表示

---

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

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

### 推奨読解順序

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

連絡先更新時に関連するデータ構造と更新ロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | contact.rb | `app/models/entities/contact.rb` | update_with_account_and_permissions（144-151行目） |
| 1-2 | account_contact.rb | `app/models/relationships/account_contact.rb` | 連絡先-取引先関連 |

**読解のコツ**: update_with_account_and_permissionsメソッドで、accessを先に設定してからattributesを更新する順序に注目。

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | contacts_controller.rb | `app/controllers/entities/contacts_controller.rb` | editアクション（54-59行目）、updateアクション（78-82行目） |

**主要処理フロー（edit）**:
1. **55行目**: @accountに既存取引先または新規Accountをセット
2. **56行目**: @previousで前の連絡先を取得（連続編集時）

**主要処理フロー（update）**:
1. **80行目**: update_with_account_and_permissionsによる更新
2. 失敗時: @accountを再設定してフォーム再表示

#### Step 3: ビューテンプレートを理解する

編集フォームの構造と新規作成フォームとの差異を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | _edit.html.haml | `app/views/contacts/_edit.html.haml` | フォーム構造、edit: trueフラグ |
| 3-2 | _top_section.html.haml | `app/views/contacts/_top_section.html.haml` | include_blank: ""（編集時のNoneオプション） |

**主要処理フロー**:
- **2行目**: simple_form_forで@contactをバインド、remote: true
- **8-13行目**: 各セクションにedit: trueを渡してレンダリング
- **17行目**: 保存ボタン、onclick="crm.save_contact()"

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

```
ContactsController#edit
    │
    ├─ before_action :get_accounts
    │      └─ Account.my(current_user).order('name')
    │
    ├─ load_and_authorize_resource (CanCan)
    │      └─ Contact.find(params[:id])
    │
    ├─ @account = @contact.account || Account.new
    │
    ├─ @previous = Contact.my.find_by_id(...)
    │
    └─ respond_with(@contact)
           └─ format.js → edit.js.erb → _edit.html.haml

ContactsController#update
    │
    ├─ load_and_authorize_resource (CanCan)
    │
    ├─ @contact.update_with_account_and_permissions(params)
    │      │
    │      ├─ save_account(params)
    │      │      └─ Account.create_or_select_for / nil
    │      │
    │      ├─ self.access = params[:contact][:access]
    │      │
    │      ├─ self.attributes = params[:contact]
    │      │
    │      └─ save
    │             └─ [contacts] UPDATE
    │
    └─ respond_with(@contact)
           ├─ format.js (成功時) → update.js.erb
           └─ format.js (失敗時) → update.js.erb (エラー表示)
```

### データフロー図

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

フォーム入力
    │
    ├── contact[*] ──────▶ ContactsController#update
    │                           │
    ├── account[id/name] ──────┤
    │                           │
                                │
                                ├──▶ Account.create_or_select_for (or nil)
                                │         │
                                │         └──▶ [accounts] SELECT/INSERT
                                │
                                ├──▶ Contact#save_account
                                │         │
                                │         └──▶ [account_contacts] DELETE/INSERT
                                │
                                ├──▶ Contact#save
                                │         │
                                │         ├──▶ [contacts] UPDATE
                                │         └──▶ [versions] INSERT (PaperTrail)
                                │
                                └────────────────────────▶ JS Response
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| contact.rb | `app/models/entities/contact.rb` | モデル | 更新ロジック |
| contacts_controller.rb | `app/controllers/entities/contacts_controller.rb` | コントローラ | edit/updateアクション |
| _edit.html.haml | `app/views/contacts/_edit.html.haml` | テンプレート | 編集フォーム |
| _top_section.html.haml | `app/views/contacts/_top_section.html.haml` | テンプレート | 基本情報（edit:true対応） |
| _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` | テンプレート | アクセス権限 |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義 |
