# 機能設計書 75-カスタムフィールド編集

## 概要

本ドキュメントは、Fat Free CRM の管理者向けカスタムフィールド編集機能の設計を定義する。この機能により、システム管理者は既存のカスタムフィールドの設定を変更することができる。

### 本機能の処理概要

カスタムフィールド編集機能は、作成済みのカスタムフィールドのラベル、ヒント、必須設定などを変更する管理者専用機能である。フィールドタイプの変更は、データベースカラムの型変換が安全な場合のみ許可される。ペアフィールドの場合は、両方のフィールドが連動して更新される。

**業務上の目的・背景**：業務要件の変更に伴い、既存のカスタムフィールドの設定を調整する必要が生じる。例えば、必須項目を任意項目に変更したり、ヒントテキストを追加したり、ラベル名を変更したりする場合がある。この機能により、既存データを保持したままフィールド設定を柔軟に変更できる。

**機能の利用シーン**：
- フィールドのラベル名を変更する場合
- 必須/任意の設定を切り替える場合
- ヒントやプレースホルダーを追加・変更する場合
- 入力文字数の制限を変更する場合
- 安全な範囲でフィールドタイプを変更する場合

**主要な処理内容**：
1. 編集対象フィールドの取得（editアクション）
2. 編集フォームの表示（AJAXによるモーダル表示）
3. フィールド属性の更新処理（updateアクション）
4. フィールドタイプ変更時のデータベースカラム型変更
5. ペアフィールドの連動更新

**関連システム・外部連携**：本機能は外部システムとの連携はなく、Fat Free CRM 内部のデータベースを操作する。フィールドタイプの変更時には、データベースカラムの型変換が行われる場合がある。

**権限による制御**：この機能は管理者権限を持つユーザーのみが実行可能である。一般ユーザーは管理画面にアクセスできず、カスタムフィールドの編集は行えない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 43 | カスタムフィールド管理画面 | 主画面 | 編集ボタン押下元 |

## 機能種別

CRUD操作（Update）/ データ定義操作

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | Integer | Yes | 編集対象フィールドのID | 存在するFieldIDであること |
| field[as] | String | Yes | フィールドタイプ | 有効なフィールドタイプであること、安全な型変換であること |
| field[label] | String | Yes | フィールドラベル | 1〜64文字 |
| field[hint] | String | No | 入力ヒント | - |
| field[placeholder] | String | No | プレースホルダーテキスト | - |
| field[required] | Boolean | No | 必須項目フラグ | - |
| field[disabled] | Boolean | No | 無効化フラグ | - |
| field[minlength] | Integer | No | 最小文字数 | 0以上の整数 |
| field[maxlength] | Integer | No | 最大文字数 | minlength以上の正の整数 |
| field[collection_string] | String | No | 選択肢（パイプ区切り） | select, radio_buttons, check_boxes の場合に使用 |
| pair[0] | Hash | No | ペアフィールドの開始側設定 | datepair, datetimepair の場合 |
| pair[1] | Hash | No | ペアフィールドの終了側設定 | datepair, datetimepair の場合 |

### 入力データソース

- 画面入力（カスタムフィールド編集フォーム）
- URLパラメータ（フィールドID）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| @field | Field | 更新されたフィールドオブジェクト |

### 出力先

- 画面表示（AJAX応答によるフィールド一覧の更新）
- データベース（fieldsテーブル、必要に応じてエンティティテーブルのカラム型変更）

## 処理フロー

### 処理シーケンス

```
1. editアクション呼び出し
   └─ フィールドIDによる対象フィールドの取得
   └─ 編集フォームの表示
2. updateアクション呼び出し
   └─ パラメータ取得（field_params）
   └─ ペアフィールドの場合：CustomFieldPair.update_pair
   └─ 通常フィールドの場合：@field.update(field_params)
   └─ バリデーション実行
   └─ 型変更が安全な場合：データベースカラム型変更（update_column コールバック）
3. レスポンス返却
```

### フローチャート

```mermaid
flowchart TD
    A[編集ボタン押下] --> B[editアクション]
    B --> C[フィールド取得]
    C --> D{フィールド存在?}
    D -->|No| E[404エラー]
    D -->|Yes| F[編集フォーム表示]
    F --> G[フォーム入力]
    G --> H[更新ボタン押下]
    H --> I[updateアクション]
    I --> J{ペアフィールド?}
    J -->|Yes| K[CustomFieldPair.update_pair]
    J -->|No| L[@field.update]
    K --> M{バリデーション成功?}
    L --> M
    M -->|No| N[エラー表示]
    M -->|Yes| O{型変更あり?}
    O -->|Yes| P{安全な変換?}
    P -->|Yes| Q[カラム型変更]
    P -->|No| R[変更なし]
    O -->|No| R
    Q --> S[一覧更新]
    R --> S
    N --> F
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-75-01 | 管理者権限必須 | カスタムフィールド編集は管理者ユーザーのみ実行可能 | 常時 |
| BR-75-02 | 安全な型変換のみ | フィールドタイプの変更は安全な変換のみ許可 | フィールドタイプ変更時 |
| BR-75-03 | カラム名不変 | データベースカラム名は変更されない | 常時 |
| BR-75-04 | ペアフィールド連動 | ペアフィールドは両方が連動して更新される | datepair, datetimepair の場合 |
| BR-75-05 | ラベル必須 | フィールドラベルは必須入力 | フィールド更新時 |

### 計算ロジック

#### 安全な型変換判定ロジック（db_transition_safety）
```
SAFE_DB_TRANSITIONS = {
  any: [['date', 'time', 'timestamp'], ['integer', 'float']],
  one: { 'string' => 'text' }
}

判定結果:
- :null  => 型変更なし（同じ型）
- :safe  => 安全な変換（date⇔time⇔timestamp, integer⇔float, string→text）
- :unsafe => 危険な変換（その他）
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| フィールド取得 | fields | SELECT | IDによるフィールド検索 |
| フィールド更新 | fields | UPDATE | フィールド属性の更新 |
| カラム型変更 | accounts/contacts/leads/opportunities/campaigns | ALTER TABLE MODIFY COLUMN | 安全な場合のみ |

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

#### fields テーブル

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | label | フォーム入力値 | 表示ラベル |
| UPDATE | as | フォーム入力値 | フィールドタイプ |
| UPDATE | hint | フォーム入力値 | ヒントテキスト |
| UPDATE | placeholder | フォーム入力値 | プレースホルダー |
| UPDATE | required | フォーム入力値 | 必須フラグ |
| UPDATE | disabled | フォーム入力値 | 無効フラグ |
| UPDATE | minlength | フォーム入力値 | 最小文字数 |
| UPDATE | maxlength | フォーム入力値 | 最大文字数 |
| UPDATE | collection | パイプ区切りから配列変換 | 選択肢 |
| UPDATE | updated_at | 現在時刻 | 自動更新 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 404 | NotFound | 指定IDのフィールドが存在しない | エラーページ表示 |
| 403 | Forbidden | 管理者権限がない | ログインページへリダイレクト |
| 422 | ValidationError | ラベル未入力、バリデーション失敗 | フォームにエラーメッセージ表示 |

### リトライ仕様

リトライは不要（ユーザーがエラー修正後に再度更新ボタンを押下）

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

fieldsテーブルへのUPDATEとエンティティテーブルへのALTER TABLEは、after_validationコールバックで実行される。バリデーションが成功した場合のみカラム型変更が行われる。

## パフォーマンス要件

- レスポンス時間：3秒以内（カラム型変更を含む場合）
- ALTER TABLE はデータ量に応じて時間がかかる可能性あり

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

- 管理者認証必須（before_action :require_admin_user）
- Strong Parameters によるマスアサインメント防止
- CSRF トークン検証（Rails標準）
- 安全でない型変換は拒否

## 備考

- カスタムフィールドのカラム名は変更されない（データ保全のため）
- 危険な型変換を行いたい場合は、一度フィールドを削除して再作成する必要がある
- 削除後の孤立カラムはrakeタスクで削除可能

---

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

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

### 推奨読解順序

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

フィールド更新とカラム型変更のロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | custom_field.rb | `app/models/fields/custom_field.rb` | update_column、db_transition_safety、安全な型変換 |
| 1-2 | custom_field_pair.rb | `app/models/fields/custom_field_pair.rb` | update_pair、ペアフィールド更新ロジック |

**読解のコツ**: `db_transition_safety`メソッドが型変換の安全性を判定する。`:safe`が返された場合のみ、`update_column`メソッドでカラム型が変更される。

**主要処理フロー**:
- **custom_field.rb 55行目**: `after_validation :update_column` - バリデーション後にカラム型変更
- **custom_field.rb 59-62行目**: `SAFE_DB_TRANSITIONS` - 安全な型変換の定義
- **custom_field.rb 90-101行目**: `db_transition_safety` - 型変換安全性判定
- **custom_field.rb 143-149行目**: `update_column` - カラム型変更処理
- **custom_field_pair.rb 25-34行目**: `update_pair` - ペアフィールド更新

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | fields_controller.rb | `app/controllers/admin/fields_controller.rb` | edit, update アクションの実装 |

**主要処理フロー**:
- **35-38行目**: `edit` アクション - フィールド取得、フォーム表示
- **61-70行目**: `update` アクション - ペアフィールドと通常フィールドの分岐処理
- **115-117行目**: `field_params` - Strong Parameters
- **119-121行目**: `pair_params` - ペアフィールド用パラメータ

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

```
Admin::FieldsController
    │
    ├─ edit アクション
    │      └─ Field.find(params[:id])
    │
    └─ update アクション
           ├─ field_params（Strong Parameters）
           │
           ├─【ペアフィールドの場合】
           │      └─ CustomFieldPair.update_pair
           │             ├─ field1.update（開始側）
           │             │      └─ after_validation :update_column
           │             │             └─ db_transition_safety
           │             │             └─ change_column（安全な場合）
           │             │
           │             └─ field2.update（終了側）
           │
           └─【通常フィールドの場合】
                  └─ @field.update(field_params)
                         └─ after_validation :update_column
                                ├─ db_transition_safety
                                │      └─ :null / :safe / :unsafe 判定
                                │
                                └─ change_column（:safe の場合）
                                       ├─ connection.change_column
                                       ├─ klass.reset_column_information
                                       └─ klass.serialize_custom_fields!
```

### データフロー図

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

フィールドID(URL) ──▶ edit アクション ──────────▶ 編集フォーム(HTML)
                              │
                              ▼
                        Field.find
                              │
                              ▼
                        fields テーブル

field[*](Form) ──────▶ update アクション ────────▶ フィールド一覧更新
                              │
                              ▼
                        @field.update
                              │
                              ├─ fields テーブル UPDATE
                              │
                              └─ after_validation :update_column
                                     │
                                     ├─ db_transition_safety
                                     │      │
                                     │      ▼
                                     │  :null / :safe / :unsafe
                                     │
                                     └─【:safe の場合】
                                            │
                                            ▼
                                        エンティティテーブル ALTER TABLE
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| field.rb | `app/models/fields/field.rb` | モデル | Fieldモデル基底クラス |
| custom_field.rb | `app/models/fields/custom_field.rb` | モデル | CustomFieldモデル、カラム型変更 |
| custom_field_pair.rb | `app/models/fields/custom_field_pair.rb` | モデル | ペアフィールド更新 |
| fields_controller.rb | `app/controllers/admin/fields_controller.rb` | コントローラー | フィールドCRUD処理 |
| application_controller.rb | `app/controllers/admin/application_controller.rb` | コントローラー | 管理者基底コントローラー |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義（190-198行目） |
