# 機能設計書 62-ユーザー削除

## 概要

本ドキュメントは、Fat Free CRM の管理機能における「ユーザー削除」機能の設計を記述する。

### 本機能の処理概要

管理者がシステムからユーザーを削除するための機能である。ただし、関連する業務データ（取引先、商談、タスク等）を持つユーザーは削除できないという安全機構が実装されている。

**業務上の目的・背景**：退職者や不要になったアカウントをシステムから除去することで、セキュリティリスクを低減し、ユーザー一覧の見通しを良くする。一方で、業務データの整合性を保つため、関連データを持つユーザーの誤削除を防止する必要がある。

**機能の利用シーン**：
- 退職者アカウントの削除
- 誤って作成されたテストユーザーの削除
- 重複アカウントの整理
- セキュリティ監査後の不要アカウント削除

**主要な処理内容**：
1. 削除確認ダイアログの表示
2. 削除可能性チェック（自分自身でないこと、関連アセットがないこと）
3. ユーザーレコードの削除
4. 削除結果の画面反映

**関連システム・外部連携**：なし

**権限による制御**：本機能は管理者権限（admin: true）を持つユーザーのみが実行可能。また、自分自身を削除することはできない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 34 | ユーザー管理一覧画面 | 主画面 | 管理者によるユーザー削除 |
| 35 | ユーザー管理詳細画面 | 参照画面 | 削除リンクの表示元 |

## 機能種別

CRUD操作（Delete）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | Integer | Yes | 削除対象ユーザーID | 存在するユーザーID、自分自身でないこと |

### 入力データソース

- URL パラメータ（ユーザーID）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| @user | User | 削除対象のユーザーオブジェクト |
| flash[:warning] | String | 削除失敗時の警告メッセージ |

### 出力先

- 画面表示（AJAX レスポンスによる部分更新）
- users テーブル（レコード削除）

## 処理フロー

### 処理シーケンス

```
1. 管理者がユーザー一覧画面で削除リンクをクリック
   └─ 確認ダイアログ表示（confirm アクション）

2. 確認後、削除実行
   └─ AJAX リクエスト: DELETE /admin/users/:id

3. destroy アクションで削除可能性をチェック
   └─ destroyable?(current_user) メソッドで検証
   └─ 自分自身でないかチェック
   └─ 関連アセットがないかチェック

4. 削除処理の実行
   └─ 成功時: ユーザーレコードを削除
   └─ 失敗時: flash[:warning] にエラーメッセージを設定

5. 結果を返却
   └─ 成功時: 一覧から該当行をスライドアウトで削除
   └─ 失敗時: 警告メッセージを表示
```

### フローチャート

```mermaid
flowchart TD
    A[削除リンククリック] --> B[確認ダイアログ表示]
    B --> C{削除確認}
    C -->|キャンセル| D[終了]
    C -->|OK| E[DELETE /admin/users/:id]
    E --> F{destroyable?}
    F -->|No: 自分自身| G[警告メッセージ表示]
    F -->|No: 関連データあり| G
    F -->|Yes| H[@user.destroy]
    H --> I{削除成功?}
    I -->|Yes| J[一覧から行を削除]
    I -->|No| G
    G --> D
    J --> D
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-62-01 | 自己削除禁止 | 管理者は自分自身を削除できない | 削除対象が current_user の場合 |
| BR-62-02 | 関連アセット保護 | 関連する業務データ（Account, Campaign, Lead, Contact, Opportunity, Comment, Task）を持つユーザーは削除不可 | has_related_assets? が true の場合 |
| BR-62-03 | 担当者チェック | assigned_to として紐付いているデータがある場合も削除不可 | 各エンティティの assigned_to が対象ユーザーの場合 |

### 計算ロジック

**has_related_assets? メソッド**：
```ruby
%w[Account Campaign Lead Contact Opportunity Comment Task].detect do |asset|
  klass = asset.constantize
  asset != "Comment" && klass.assigned_to(self).exists? || klass.created_by(self).exists?
end
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ユーザー削除 | users | DELETE | ユーザーレコードの削除 |
| 権限削除 | permissions | DELETE | dependent: :destroy により自動削除 |
| 設定削除 | preferences | DELETE | dependent: :destroy により自動削除 |
| グループ関連削除 | groups_users | DELETE | HABTM により自動削除 |

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

#### users

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| DELETE | - | id = params[:id] | destroyable? が true の場合のみ |

#### permissions

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| DELETE | - | user_id = 削除ユーザーID | dependent: :destroy |

#### preferences

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| DELETE | - | user_id = 削除ユーザーID | dependent: :destroy |

#### groups_users

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| DELETE | - | user_id = 削除ユーザーID | HABTM 関連 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 業務エラー | 自分自身を削除しようとした場合 | flash[:warning] で警告表示 |
| - | 業務エラー | 関連アセットが存在する場合 | flash[:warning] で警告表示 |
| - | 認可エラー | 非管理者によるアクセス | ルート画面にリダイレクト |

### リトライ仕様

削除失敗時は警告メッセージを表示し、ユーザーは別の方法（関連データの削除・移行後）で再試行可能。

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

ActiveRecord の destroy メソッドにより、関連レコード（permissions, preferences）の削除も含めてトランザクション内で処理される。

## パフォーマンス要件

- AJAX リクエストのため、レスポンス時間は 500ms 以内を目標
- has_related_assets? のチェックで複数テーブルへのクエリが発行されるが、EXISTS クエリのため高速

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

- 管理者権限チェック（before_action :require_admin_user）
- 自己削除の防止（destroyable? メソッド）
- CSRF トークンによるクロスサイトリクエストフォージェリ対策

## 備考

- 削除は物理削除であり、論理削除（deleted_at）ではない
- 関連アセットを持つユーザーを削除する場合は、先に関連データを他のユーザーに移行するか削除する必要がある

---

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

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

### 推奨読解順序

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

まず、ユーザーモデルの削除関連メソッドを理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | user.rb | `app/models/users/user.rb` | destroyable? メソッド、has_related_assets? メソッド |
| 1-2 | schema.rb | `db/schema.rb` | users テーブル、関連テーブルの構造 |

**読解のコツ**:
- **176-178行目**: `destroyable?(current_user)` で自分自身でないことと関連アセットがないことをチェック
- **188-195行目**: `has_related_assets?` で7種類のエンティティとの関連をチェック

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | users_controller.rb | `app/controllers/admin/users_controller.rb` | destroy アクション、confirm アクション |
| 2-2 | application_controller.rb | `app/controllers/admin/application_controller.rb` | 管理者権限チェック |

**主要処理フロー**:
1. **67-69行目**: confirm アクション - 削除確認ダイアログ表示
2. **74-78行目**: destroy アクション - 削除実行

```ruby
def destroy
  flash[:warning] = t(:msg_cant_delete_user, @user.full_name) unless @user.destroyable?(current_user) && @user.destroy
  respond_with(@user)
end
```

#### Step 3: ビュー層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | _user.html.haml | `app/views/admin/users/_user.html.haml` | 削除リンクの表示条件 |
| 3-2 | confirm.js.haml | `app/views/admin/users/confirm.js.haml` | 確認ダイアログの JS |
| 3-3 | _confirm.html.haml | `app/views/admin/users/_confirm.html.haml` | 確認ダイアログの HTML |
| 3-4 | destroy.js.haml | `app/views/admin/users/destroy.js.haml` | 削除後の画面更新処理 |

**読解のコツ**:
- `_user.html.haml` の26-29行目で、自分自身の場合は削除リンクがグレーアウト表示される
- `destroy.js.haml` の3-8行目で、削除成功時はスライドアウト、失敗時は警告表示

#### Step 4: ルーティングを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | routes.rb | `config/routes.rb` | admin/users の destroy, confirm ルート |

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

```
[Admin::UsersController#confirm]
    │
    └─ respond_with(@user)
           └─ confirm.js.haml
                  └─ _confirm.html.haml

[Admin::UsersController#destroy]
    │
    ├─ @user.destroyable?(current_user)
    │      ├─ current_user != self
    │      └─ !has_related_assets?
    │             ├─ Account.assigned_to(self).exists?
    │             ├─ Account.created_by(self).exists?
    │             ├─ Campaign.assigned_to(self).exists?
    │             ├─ ... (他のエンティティ)
    │             └─ Task.created_by(self).exists?
    │
    ├─ @user.destroy (条件を満たす場合)
    │      ├─ permissions.destroy_all (dependent)
    │      ├─ preferences.destroy_all (dependent)
    │      └─ groups_users DELETE (HABTM)
    │
    └─ respond_with(@user)
           └─ destroy.js.haml
```

### データフロー図

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

ユーザーID         ───▶ load_resource             ───▶ 確認ダイアログ
                           │
                   ───▶ destroyable? チェック
                           │
                           ├─ 失敗時 ──────────────▶ flash[:warning]
                           │                              │
                           └─ 成功時                      ▼
                                  │                  警告メッセージ表示
                                  ▼
                              @user.destroy
                                  │
                                  ├─ permissions 削除
                                  ├─ preferences 削除
                                  └─ groups_users 削除
                                        │
                                        ▼
                                  一覧から行削除
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| users_controller.rb | `app/controllers/admin/users_controller.rb` | コントローラー | confirm/destroy アクションの実装 |
| application_controller.rb | `app/controllers/admin/application_controller.rb` | コントローラー | 管理者権限チェック |
| user.rb | `app/models/users/user.rb` | モデル | destroyable?, has_related_assets? メソッド |
| _user.html.haml | `app/views/admin/users/_user.html.haml` | ビュー | ユーザー行の表示、削除リンク |
| confirm.js.haml | `app/views/admin/users/confirm.js.haml` | ビュー | 確認ダイアログ表示 JS |
| _confirm.html.haml | `app/views/admin/users/_confirm.html.haml` | ビュー | 確認ダイアログ HTML |
| destroy.js.haml | `app/views/admin/users/destroy.js.haml` | ビュー | 削除後の画面更新 JS |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義 |
| schema.rb | `db/schema.rb` | 設定 | データベーススキーマ |
