# 機能設計書 66-連絡先所属企業

## 概要

本ドキュメントは、Chatwoot Enterprise版における連絡先（Contact）と企業（Company）の関連付け機能の設計を記述する。B2B向けCRM機能として、連絡先を企業単位で管理するための機能を提供する。

### 本機能の処理概要

連絡先所属企業機能は、連絡先を企業（Company）に関連付けて管理するEnterprise版限定の機能である。B2Bビジネスにおいて、顧客を企業単位でグループ化し、企業情報と連絡先情報を紐付けて管理できる。

**業務上の目的・背景**：B2Bカスタマーサポートにおいて、同じ企業に属する複数の担当者を一元管理することは重要である。企業単位での契約情報や対応履歴を参照することで、組織的な顧客管理が可能になる。また、ドメイン名による企業の自動識別も支援する。

**機能の利用シーン**：
- 新規連絡先を既存企業に関連付ける場合
- 企業に属する連絡先一覧を確認する場合
- 企業の新規登録と連絡先の紐付けを行う場合
- 企業情報（名前、ドメイン、説明）を管理する場合

**主要な処理内容**：
1. 企業の作成・更新・削除（CRUD）
2. 連絡先と企業の関連付け（company_id設定）
3. 企業に属する連絡先のカウント管理
4. 企業の検索（名前、ドメイン）

**関連システム・外部連携**：
- CRM V2機能フラグとの連携
- Enterprise Edition限定機能

**権限による制御**：Enterprise版のみで利用可能。Company policyによるアクセス制御。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 25 | 企業一覧画面 | 主画面 | 企業の一覧表示と管理 |

## 機能種別

CRUD操作（Create/Read/Update/Delete）/ Enterprise機能

## 入力仕様

### 入力パラメータ

#### 企業API

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | String | Yes | 企業名 | presence: true, 最大長制限 |
| domain | String | No | ドメイン | 形式チェック、アカウント内ユニーク |
| description | String | No | 説明 | 最大長制限 |
| avatar | File | No | 企業ロゴ | ActiveStorage添付 |

#### 連絡先関連付け

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| company_id | Integer | No | 所属企業ID | 存在確認 |

### 入力データソース

- 画面入力：企業一覧画面、連絡先編集画面
- APIパラメータ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | Integer | 企業ID |
| name | String | 企業名 |
| domain | String | ドメイン |
| description | String | 説明 |
| contacts_count | Integer | 所属連絡先数 |
| created_at | DateTime | 作成日時 |
| updated_at | DateTime | 更新日時 |

### 出力先

- API レスポンス（JSON形式）
- フロントエンド企業一覧画面

## 処理フロー

### 処理シーケンス

```
1. Enterprise版チェック
   └─ ChatwootApp.enterprise? で確認
   └─ 非Enterprise版の場合は 403 Forbidden

2. 認証・認可チェック
   └─ authorize(Company) でポリシーチェック

3. 企業操作実行
   └─ index: 企業一覧取得（ページネーション、ソート）
   └─ search: 名前・ドメインで検索
   └─ show: 企業詳細取得
   └─ create: 新規企業作成
   └─ update: 企業情報更新
   └─ destroy: 企業削除（連絡先のcompany_idはnullify）

4. レスポンス生成
```

### フローチャート

```mermaid
flowchart TD
    A[APIリクエスト受信] --> B{Enterprise版?}
    B -->|No| C[403 Forbidden]
    B -->|Yes| D{認可チェック}
    D -->|NG| E[401 Unauthorized]
    D -->|OK| F{アクション判定}
    F -->|index| G[企業一覧取得]
    F -->|search| H[名前・ドメイン検索]
    F -->|create| I[企業作成]
    F -->|update| J[企業更新]
    F -->|destroy| K[企業削除]
    G --> L[JSONレスポンス]
    H --> L
    I --> L
    J --> L
    K --> M[200 OK]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | Enterprise限定 | ChatwootApp.enterprise? が true の場合のみ利用可能 | 全アクション |
| BR-02 | ドメインユニーク | 同一アカウント内でドメインは一意（空の場合は制約なし） | 作成・更新時 |
| BR-03 | 連絡先nullify | 企業削除時、関連する連絡先のcompany_idはnullに設定 | 削除時 |
| BR-04 | contacts_count | 企業に属する連絡先数はcontacts_countカラムで管理 | カウンターキャッシュ |
| BR-05 | ドメイン形式 | ドメインは正規表現でバリデーション | 作成・更新時 |

### 計算ロジック

特に計算ロジックはなし。contacts_countはRailsのカウンターキャッシュで自動管理。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| index | companies | SELECT | 企業一覧取得 |
| search | companies | SELECT | 名前・ドメインで検索 |
| create | companies | INSERT | 企業作成 |
| update | companies | UPDATE | 企業情報更新 |
| destroy | companies | DELETE | 企業削除 |
| destroy | contacts | UPDATE | company_id を NULL に |

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

#### companies

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | name | パラメータ値 | 必須 |
| INSERT | domain | パラメータ値 | オプション |
| INSERT | description | パラメータ値 | オプション |
| INSERT | account_id | Current.account.id | 自動設定 |
| SELECT | - | account_id = :account_id | アカウントスコープ |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | Unauthorized | 認証トークンが無効 | 再認証 |
| 403 | Forbidden | Enterprise版ではない | Enterprise版にアップグレード |
| 404 | Not Found | 企業が存在しない | 正しいIDを指定 |
| 422 | Unprocessable Entity | バリデーションエラー | 入力内容を修正 |

### リトライ仕様

特にリトライ仕様はなし。

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

- 企業削除時、連絡先のcompany_id nullify は dependent: :nullify で自動処理

## パフォーマンス要件

- name, account_id にインデックス
- account_id, domain にユニークインデックス（domainがNULLでない場合）
- Kaminari によるページネーション（25件/ページ）

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

- Enterprise版のみ利用可能
- Company policy によるアクセス制御
- アカウント単位でのスコープ制限

## 備考

- Enterprise Edition 限定機能
- Company モデルは enterprise/app/models/company.rb に定義
- Avatarable concern を含む（企業ロゴ対応）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | company.rb | `enterprise/app/models/company.rb` | Companyモデル定義、バリデーション |

**読解のコツ**: enterpriseディレクトリ配下はEnterprise Edition限定コード。

**主要処理フロー**:
- **20-33行目**: バリデーション定義
  - name: presence, 最大長制限
  - domain: 形式チェック、ユニーク制約（アカウント内）
  - description: 最大長制限
- **31行目**: `belongs_to :account`
- **32行目**: `has_many :contacts, dependent: :nullify` - 削除時に連絡先のcompany_idをnull化
- **34-44行目**: スコープ定義（ordered_by_name, search_by_name_or_domain, order_on_contacts_count）

#### Step 2: コントローラーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | companies_controller.rb | `enterprise/app/controllers/api/v1/accounts/companies_controller.rb` | 企業API実装 |

**主要処理フロー**:
- **10行目**: `RESULTS_PER_PAGE = 25` - ページネーション設定
- **14-17行目**: `index` - 企業一覧取得
- **19-28行目**: `search` - 名前・ドメイン検索
- **32-35行目**: `create` - 企業作成
- **37-39行目**: `update` - 企業更新
- **41-44行目**: `destroy` - 企業削除
- **62-66行目**: `check_authorization` - Enterprise版チェックと認可

#### Step 3: 連絡先との関連を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | contact.rb | `app/models/contact.rb` | company_id カラム |

**主要処理フロー**:
- **24行目（スキーマ）**: `company_id :bigint` - 企業への外部キー

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

```
Api::V1::Accounts::CompaniesController
    │
    ├─ check_authorization (before_action)
    │      ├─ ChatwootApp.enterprise? チェック
    │      └─ authorize(Company)
    │
    ├─ index アクション
    │      └─ fetch_companies(resolved_companies)
    │             └─ filtrate + page + per
    │
    ├─ search アクション
    │      └─ Company.search_by_name_or_domain(params[:q])
    │
    ├─ create アクション
    │      └─ Current.account.companies.build(company_params)
    │
    └─ destroy アクション
           └─ Company#destroy!
                  └─ has_many :contacts, dependent: :nullify
```

### データフロー図

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

Enterprise チェック ──▶ ChatwootApp.enterprise? ────▶ OK/NG

company_params ──────▶ CompaniesController ──────────▶ companies テーブル
 (POST body)                  │                        (INSERT/UPDATE)
                              │
                       Company モデル
                              │
                       バリデーション
                       - name: 必須
                       - domain: 形式チェック
                              │
                              ▼
                       JSON レスポンス

Company 削除 ────────▶ dependent: :nullify ──────────▶ contacts テーブル
                                                        (UPDATE company_id = NULL)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| company.rb | `enterprise/app/models/company.rb` | モデル | 企業モデル定義 |
| companies_controller.rb | `enterprise/app/controllers/api/v1/accounts/companies_controller.rb` | コントローラー | 企業API |
| company_policy.rb | `enterprise/app/policies/company_policy.rb` | ポリシー | 企業アクセス制御 |
| contact.rb | `app/models/contact.rb` | モデル | 連絡先（company_id外部キー） |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義 |
