# 機能設計書 10-カスタム属性定義

## 概要

本ドキュメントは、Chatwootシステムにおけるカスタム属性定義機能の設計を記載する。会話・連絡先に対するカスタム属性の定義と管理を実現する。

### 本機能の処理概要

本機能は、アカウント単位でカスタム属性を定義し、会話（Conversation）や連絡先（Contact）に追加のメタデータを付与できる機能を提供する。

**業務上の目的・背景**：組織によって顧客管理や会話管理に必要な情報は異なる。本機能により、標準フィールドでは対応できない業界固有・企業固有の情報（契約ID、会員番号、購入履歴等）を柔軟に追加でき、CRMとの連携やレポーティングに活用できる。

**機能の利用シーン**：
- 管理者がカスタム属性を新規定義
- 会話や連絡先に対するカスタム属性値の入力
- カスタム属性に基づくフィルタリング
- ウィジェットのプリチャットフォームでのカスタムフィールド表示

**主要な処理内容**：
1. カスタム属性定義の作成（名前、キー、型、選択肢）
2. カスタム属性定義の表示・更新・削除
3. 属性モデル（会話/連絡先）の指定
4. 正規表現パターンによる入力値検証
5. ウィジェットプリチャットフィールドとの同期

**関連システム・外部連携**：
- Web Widget（プリチャットフォームカスタムフィールド）

**権限による制御**：カスタム属性定義の操作はログイン済みユーザーのみ可能。表示・検索は全ユーザー、作成・更新・削除は権限ポリシーに従う。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 73 | カスタム属性一覧画面 | 主画面 | カスタム属性の一覧と管理 |
| 45 | 一般設定画面 | 参照画面 | カスタム属性の設定 |

## 機能種別

CRUD操作 / マスタ管理

## 入力仕様

### 入力パラメータ

#### カスタム属性定義作成・更新

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| attribute_display_name | String | Yes | 表示名 | 必須 |
| attribute_key | String | Yes | 属性キー（英数字） | 必須、アカウント内で一意、標準属性と重複不可 |
| attribute_display_type | String | Yes | 表示タイプ | text/number/currency/percent/link/date/list/checkbox |
| attribute_model | String | Yes | 属性モデル | conversation_attribute/contact_attribute |
| attribute_description | String | No | 説明 | - |
| attribute_values | Array | No | 選択肢（listタイプ用） | - |
| regex_pattern | String | No | 入力値検証用正規表現 | - |
| regex_cue | String | No | 正規表現のヒント | - |
| default_value | Integer | No | デフォルト値（listのインデックス） | - |

### 入力データソース

- 画面入力（カスタム属性一覧画面）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | Integer | カスタム属性定義ID |
| attribute_display_name | String | 表示名 |
| attribute_key | String | 属性キー |
| attribute_display_type | String | 表示タイプ |
| attribute_model | String | 属性モデル |
| attribute_description | String | 説明 |
| attribute_values | Array | 選択肢 |
| regex_pattern | String | 正規表現パターン |
| regex_cue | String | 正規表現ヒント |
| default_value | Integer | デフォルト値 |

### 出力先

- データベース（custom_attribute_definitionsテーブル）
- HTTPレスポンスボディ（JSON形式）

## 処理フロー

### 処理シーケンス

```
【カスタム属性定義作成フロー】
1. 作成要求
   └─ CustomAttributeDefinitionsController#create
   └─ 認証確認

2. バリデーション
   └─ attribute_display_name必須チェック
   └─ attribute_key一意性チェック
   └─ 標準属性との重複チェック

3. レコード作成
   └─ Current.account.custom_attribute_definitions.create!

4. ウィジェット同期（更新時）
   └─ after_update :update_widget_pre_chat_custom_fields
   └─ UpdateWidgetPreChatCustomFieldsJob

【カスタム属性定義削除フロー】
5. 削除要求
   └─ CustomAttributeDefinitionsController#destroy
   └─ @custom_attribute_definition.destroy!

6. ウィジェット同期
   └─ after_destroy :sync_widget_pre_chat_custom_fields
   └─ SyncWidgetPreChatCustomFieldsJob
```

### フローチャート

```mermaid
flowchart TD
    A[カスタム属性定義作成要求] --> B[認証確認]
    B --> C{attribute_display_name存在?}
    C -->|No| D[422エラー]
    C -->|Yes| E{attribute_key一意?}
    E -->|No| F[422エラー: 重複]
    E -->|Yes| G{標準属性と重複?}
    G -->|Yes| H[422エラー: 標準属性と重複]
    G -->|No| I[create!]
    I --> J[200 OK + 定義情報]

    K[カスタム属性定義更新要求] --> L[update!]
    L --> M[after_update コールバック]
    M --> N[UpdateWidgetPreChatCustomFieldsJob]
    N --> O[200 OK]

    P[カスタム属性定義削除要求] --> Q[destroy!]
    Q --> R[after_destroy コールバック]
    R --> S[SyncWidgetPreChatCustomFieldsJob]
    S --> T[204 No Content]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 一意性 | attribute_keyはaccount_id + attribute_modelの組み合わせで一意 | 作成時 |
| BR-002 | 標準属性重複禁止 | 標準属性キーと同名のカスタム属性は作成不可 | 作成時 |
| BR-003 | 会話標準属性 | status, priority, assignee_id, inbox_id, team_id, display_id, campaign_id, labels, browser_language, country_code, referer, created_at, last_activity_at | 会話属性作成時 |
| BR-004 | 連絡先標準属性 | name, email, phone_number, identifier, country_code, city, created_at, last_activity_at, referer, blocked | 連絡先属性作成時 |
| BR-005 | ウィジェット同期 | カスタム属性の更新・削除時にウィジェットプリチャットフィールドを同期 | 更新・削除時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 定義作成 | custom_attribute_definitions | INSERT | 新規カスタム属性定義作成 |
| 定義一覧取得 | custom_attribute_definitions | SELECT | 属性モデルでフィルタリング |
| 定義取得 | custom_attribute_definitions | SELECT | ID条件で取得 |
| 定義更新 | custom_attribute_definitions | UPDATE | 定義情報更新 |
| 定義削除 | custom_attribute_definitions | DELETE | 定義レコード削除 |

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

#### custom_attribute_definitions

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | account_id | Current.account.id | 外部キー |
| INSERT/UPDATE | attribute_display_name | パラメータ値 | 必須 |
| INSERT/UPDATE | attribute_key | パラメータ値 | 必須、一意 |
| INSERT/UPDATE | attribute_display_type | パラメータ値 | enum (0-7) |
| INSERT/UPDATE | attribute_model | パラメータ値 | enum (0-1) |
| INSERT/UPDATE | attribute_description | パラメータ値 | - |
| INSERT/UPDATE | attribute_values | パラメータ値 | JSONB |
| INSERT/UPDATE | regex_pattern | パラメータ値 | - |
| INSERT/UPDATE | regex_cue | パラメータ値 | - |
| INSERT/UPDATE | default_value | パラメータ値 | Integer |
| SELECT | * | WHERE attribute_model = :model | モデルフィルタリング |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 422 | Unprocessable Entity | attribute_display_name未入力 | 表示名を入力 |
| 422 | Unprocessable Entity | attribute_key重複 | 別のキーを使用 |
| 422 | Unprocessable Entity | 標準属性と重複 | 別のキーを使用 |
| 422 | Unprocessable Entity | attribute_display_type未入力 | タイプを選択 |
| 422 | Unprocessable Entity | attribute_model未入力 | モデルを選択 |

### リトライ仕様

- バリデーションエラー時は入力値修正後再試行

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

- カスタム属性定義の作成・更新・削除は単一トランザクション
- ウィジェット同期は非同期ジョブで実行

## パフォーマンス要件

- 定義一覧取得: 100ms以内
- 定義作成・更新: 500ms以内
- attribute_key_model_indexによる高速検索

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

- 認証済みユーザーのみ操作可能
- アカウント内でのみ定義を管理（他アカウントの定義へアクセス不可）
- 正規表現パターンによる入力値検証が可能

## 備考

- 表示タイプ: text(0), number(1), currency(2), percent(3), link(4), date(5), list(6), checkbox(7)
- 属性モデル: conversation_attribute(0), contact_attribute(1)
- ウィジェットプリチャットフォームとの連携はcontact_attribute時に有効

---

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

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

### 推奨読解順序

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

カスタム属性定義のテーブル構造とモデル定義を把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | custom_attribute_definition.rb | `app/models/custom_attribute_definition.rb` | モデル構造、enum、バリデーション |

**読解のコツ**:
- 1-22行目: スキーマ定義（各カラムと型）
- 25-29行目: STANDARD_ATTRIBUTES（標準属性リスト）
- 31-40行目: バリデーション定義
- 42-43行目: enum定義（attribute_model, attribute_display_type）
- 46-47行目: after_update/after_destroyコールバック
- 59-64行目: attribute_must_not_conflict（標準属性重複チェック）

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

カスタム属性定義コントローラーの処理フローを把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | custom_attribute_definitions_controller.rb | `app/controllers/api/v1/accounts/custom_attribute_definitions_controller.rb` | CRUD アクション |

**主要処理フロー**:
1. **2-3行目**: before_actionでの前処理
2. **6行目**: indexアクションでフィルタリング
3. **10-13行目**: createアクションで定義作成
4. **15-17行目**: updateアクションで定義更新
5. **19-22行目**: destroyアクションで定義削除
6. **27-29行目**: fetch_custom_attributes_definitionsでモデルフィルタリング

#### Step 3: ウィジェット同期を理解する

カスタム属性更新時のウィジェットプリチャットフィールド同期を把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | sync_widget_pre_chat_custom_fields_job.rb | `app/jobs/inboxes/sync_widget_pre_chat_custom_fields_job.rb` | 削除時の同期ジョブ |
| 3-2 | update_widget_pre_chat_custom_fields_job.rb | `app/jobs/inboxes/update_widget_pre_chat_custom_fields_job.rb` | 更新時の同期ジョブ |

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

```
Api::V1::Accounts::CustomAttributeDefinitionsController
    │
    ├─ index
    │      └─ fetch_custom_attributes_definitions
    │             └─ with_attribute_model(attribute_model)
    │
    ├─ create
    │      └─ Current.account.custom_attribute_definitions.create!
    │             ├─ validates :attribute_display_name, presence: true
    │             ├─ validates :attribute_key, uniqueness: { scope: [:account_id, :attribute_model] }
    │             └─ validate :attribute_must_not_conflict
    │
    ├─ update
    │      └─ @custom_attribute_definition.update!
    │             └─ after_update :update_widget_pre_chat_custom_fields
    │                    └─ UpdateWidgetPreChatCustomFieldsJob.perform_later
    │
    └─ destroy
           └─ @custom_attribute_definition.destroy!
                  └─ after_destroy :sync_widget_pre_chat_custom_fields
                         └─ SyncWidgetPreChatCustomFieldsJob.perform_later
```

### データフロー図

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

【カスタム属性定義作成】
attribute_display_name ─▶ Controller#create
attribute_key                   │
attribute_display_type          ▼
attribute_model     ──────▶ バリデーション
                                │
                                ├─ 一意性チェック
                                │
                                ├─ 標準属性重複チェック
                                │
                                └─ create! ───────────────▶ custom_attribute_definitions (DB)
                                       │
                                       ▼
                                200 OK + 定義情報 ────────▶ JSON Response

【カスタム属性定義更新】
更新パラメータ ───────▶ Controller#update
                                │
                                ▼
                          update! ────────────────────────▶ custom_attribute_definitions (DB)
                                │
                                ▼
                          after_update
                                │
                                ▼
                          UpdateWidgetPreChatCustomFieldsJob
                                │
                                ▼
                          200 OK ─────────────────────────▶ JSON Response

【カスタム属性定義削除】
DELETE要求 ───────────▶ Controller#destroy
                                │
                                ▼
                          destroy! ───────────────────────▶ custom_attribute_definitions (DELETE)
                                │
                                ▼
                          after_destroy
                                │
                                ▼
                          SyncWidgetPreChatCustomFieldsJob
                                │
                                ▼
                          204 No Content ─────────────────▶ Response
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| custom_attribute_definition.rb | `app/models/custom_attribute_definition.rb` | モデル | カスタム属性定義エンティティ |
| custom_attribute_definitions_controller.rb | `app/controllers/api/v1/accounts/custom_attribute_definitions_controller.rb` | コントローラー | カスタム属性定義API |
| sync_widget_pre_chat_custom_fields_job.rb | `app/jobs/inboxes/sync_widget_pre_chat_custom_fields_job.rb` | ジョブ | ウィジェット同期（削除時） |
| update_widget_pre_chat_custom_fields_job.rb | `app/jobs/inboxes/update_widget_pre_chat_custom_fields_job.rb` | ジョブ | ウィジェット同期（更新時） |
| routes.rb | `config/routes.rb` | 設定 | カスタム属性定義ルート定義（211行目） |
| index.json.jbuilder | `app/views/api/v1/accounts/custom_attribute_definitions/index.json.jbuilder` | ビュー | 一覧レスポンス |
