# 機能設計書 61-ユーザー編集

## 概要

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

### 本機能の処理概要

管理者がシステム内の既存ユーザー情報を編集・更新するための機能である。ユーザーの基本情報、認証情報、およびグループ所属を変更することができる。

**業務上の目的・背景**：CRM システムの運用において、ユーザーの所属部署変更、連絡先変更、権限変更などが発生することは日常的である。本機能により、管理者は個々のユーザー情報を柔軟に更新でき、組織変更や人事異動に迅速に対応することが可能となる。また、セキュリティ上の理由からパスワードのリセットや管理者権限の付与・剥奪も行える。

**機能の利用シーン**：
- 新入社員の基本情報入力後の追加情報更新
- 部署異動に伴う所属グループの変更
- 役職変更に伴う管理者権限の付与または剥奪
- ユーザーからの依頼によるパスワードリセット
- 連絡先情報（メール、電話番号等）の更新

**主要な処理内容**：
1. 編集対象ユーザーの特定と既存情報の取得
2. 編集フォームの表示（AJAX による部分レンダリング）
3. フォーム入力値のバリデーション
4. ユーザー情報の更新（基本情報、認証情報、グループ所属）
5. 更新結果の画面反映

**関連システム・外部連携**：Devise 認証システムと連携しており、パスワード変更時は Devise の暗号化機能を使用する。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 37 | ユーザー管理編集フォーム | 主画面 | 管理者によるユーザー情報編集 |
| 34 | ユーザー管理一覧画面 | 参照画面 | 編集リンクからの遷移元 |

## 機能種別

CRUD操作（Update）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | Integer | Yes | 編集対象ユーザーID | 存在するユーザーID |
| user[username] | String | Yes | ユーザー名 | 英数字・アンダースコア・ハイフンのみ、一意性 |
| user[email] | String | Yes | メールアドレス | 3-254文字、メール形式、一意性 |
| user[password] | String | No | パスワード | 入力時は確認と一致必須 |
| user[password_confirmation] | String | No | パスワード確認 | password と一致 |
| user[admin] | Boolean | No | 管理者権限 | true/false |
| user[first_name] | String | No | 名 | 32文字以内 |
| user[last_name] | String | No | 姓 | 32文字以内 |
| user[title] | String | No | 役職 | 64文字以内 |
| user[company] | String | No | 会社名 | 64文字以内 |
| user[alt_email] | String | No | 代替メール | メール形式 |
| user[phone] | String | No | 電話番号 | 32文字以内 |
| user[mobile] | String | No | 携帯番号 | 32文字以内 |
| user[aim] | String | No | AIM | 32文字以内 |
| user[yahoo] | String | No | Yahoo | 32文字以内 |
| user[google] | String | No | Google | 32文字以内 |
| user[group_ids] | Array | No | 所属グループID配列 | 存在するグループID |

### 入力データソース

- 画面入力（編集フォーム）
- URL パラメータ（ユーザーID）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| @user | User | 更新後のユーザーオブジェクト |
| errors | Array | バリデーションエラーメッセージ |

### 出力先

- 画面表示（AJAX レスポンスによる部分更新）
- users テーブル
- groups_users テーブル（グループ所属変更時）

## 処理フロー

### 処理シーケンス

```
1. 管理者がユーザー一覧画面で編集リンクをクリック
   └─ AJAX リクエスト: GET /admin/users/:id/edit

2. edit アクションで編集フォームを表示
   └─ @user にユーザー情報をロード
   └─ 編集フォーム HTML を返却

3. 管理者がフォームに情報を入力して送信
   └─ AJAX リクエスト: PUT /admin/users/:id

4. update アクションでユーザー情報を更新
   └─ user_params でパラメータを許可リストでフィルタリング
   └─ メールアドレスの前後空白を削除
   └─ ユーザー属性を設定して保存

5. 更新結果を返却
   └─ 成功時: ユーザー一覧の該当行を更新
   └─ 失敗時: エラーメッセージを表示
```

### フローチャート

```mermaid
flowchart TD
    A[編集リンククリック] --> B[GET /admin/users/:id/edit]
    B --> C[load_resource でユーザー取得]
    C --> D[編集フォーム表示]
    D --> E[フォーム入力・送信]
    E --> F[PUT /admin/users/:id]
    F --> G[user_params でフィルタリング]
    G --> H{バリデーション}
    H -->|成功| I[ユーザー情報更新]
    I --> J[画面更新 JS 返却]
    H -->|失敗| K[エラー表示]
    K --> D
    J --> L[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-61-01 | 管理者権限自己剥奪禁止 | 管理者は自分自身の admin 権限を false にできない | 編集対象が自分自身の場合 |
| BR-61-02 | メールアドレス一意性 | システム内でメールアドレスは一意である必要がある | 常時 |
| BR-61-03 | ユーザー名一意性 | システム内でユーザー名は一意である必要がある | 常時 |
| BR-61-04 | パスワード確認一致 | パスワード変更時は確認入力と一致が必要 | パスワード入力時 |
| BR-61-05 | メール前後空白除去 | email, alt_email の前後空白は自動削除 | 常時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ユーザー情報更新 | users | UPDATE | ユーザー基本情報・認証情報の更新 |
| グループ所属更新 | groups_users | DELETE/INSERT | グループ所属関係の再構築 |

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

#### users

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | username | フォーム入力値 | 一意性チェック |
| UPDATE | email | フォーム入力値（空白除去後） | 一意性チェック |
| UPDATE | first_name | フォーム入力値 | |
| UPDATE | last_name | フォーム入力値 | |
| UPDATE | title | フォーム入力値 | |
| UPDATE | company | フォーム入力値 | |
| UPDATE | alt_email | フォーム入力値（空白除去後） | |
| UPDATE | phone | フォーム入力値 | |
| UPDATE | mobile | フォーム入力値 | |
| UPDATE | aim | フォーム入力値 | |
| UPDATE | yahoo | フォーム入力値 | |
| UPDATE | google | フォーム入力値 | |
| UPDATE | admin | フォーム入力値 | 自己剥奪禁止 |
| UPDATE | encrypted_password | Devise により暗号化 | パスワード変更時のみ |
| UPDATE | updated_at | 現在時刻 | 自動更新 |

#### groups_users

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| DELETE/INSERT | user_id | 対象ユーザーID | HABTM により管理 |
| DELETE/INSERT | group_id | 選択されたグループID | HABTM により管理 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | バリデーションエラー | 必須項目未入力 | フォームにエラーメッセージを表示 |
| - | バリデーションエラー | メール形式不正 | フォームにエラーメッセージを表示 |
| - | バリデーションエラー | 一意性違反 | フォームにエラーメッセージを表示 |
| - | バリデーションエラー | パスワード不一致 | フォームにエラーメッセージを表示 |
| - | 認可エラー | 非管理者によるアクセス | ルート画面にリダイレクト |

### リトライ仕様

バリデーションエラー時は編集フォームを再表示し、ユーザーが修正後に再送信可能。

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

ActiveRecord のデフォルトトランザクションにより、save メソッド実行時に自動的にトランザクション管理される。HABTM 関連（groups_users）の更新も同一トランザクション内で処理される。

## パフォーマンス要件

- AJAX リクエストのため、レスポンス時間は 500ms 以内を目標
- 単一ユーザーの更新処理であり、特別なパフォーマンス考慮は不要

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

- 管理者権限チェック（before_action :require_admin_user）
- Strong Parameters によるパラメータフィルタリング
- パスワードは Devise により暗号化して保存
- CSRF トークンによるクロスサイトリクエストフォージェリ対策
- admin 権限の自己剥奪防止（UI レベルで無効化）

## 備考

- 編集フォームは AJAX で表示されるため、ページ遷移なしで操作可能
- previous パラメータにより、複数編集フォームの管理が可能

---

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

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

### 推奨読解順序

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

まず、ユーザーモデルのスキーマとバリデーションを理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | user.rb | `app/models/users/user.rb` | ユーザーモデルの定義、バリデーション、関連 |
| 1-2 | group.rb | `app/models/users/group.rb` | グループモデルと HABTM 関連 |
| 1-3 | schema.rb | `db/schema.rb` | users テーブル、groups_users テーブルのスキーマ |

**読解のコツ**: User モデルの `has_and_belongs_to_many :groups`（65行目）により、groups_users 中間テーブルを介してグループとの多対多関連が構築されている。

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

処理の起点となるコントローラーを特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | users_controller.rb | `app/controllers/admin/users_controller.rb` | edit, update アクションの実装 |
| 2-2 | application_controller.rb | `app/controllers/admin/application_controller.rb` | 管理者権限チェックの before_action |

**主要処理フロー**:
1. **37-41行目**: edit アクション - 編集フォーム表示用データ準備
2. **57-63行目**: update アクション - ユーザー情報更新処理
3. **104-129行目**: user_params メソッド - Strong Parameters によるパラメータフィルタリング

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | edit.js.haml | `app/views/admin/users/edit.js.haml` | AJAX レスポンス処理 |
| 3-2 | _edit.html.haml | `app/views/admin/users/_edit.html.haml` | 編集フォームの HTML 構造 |
| 3-3 | _profile.html.haml | `app/views/admin/users/_profile.html.haml` | プロファイル入力フィールド |
| 3-4 | update.js.haml | `app/views/admin/users/update.js.haml` | 更新完了後の JS 処理 |

**読解のコツ**: `_profile.html.haml` の24行目で `disabled: edit && @user == current_user` により、自分自身の管理者権限チェックボックスを無効化している。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | routes.rb | `config/routes.rb` | admin 名前空間のルート定義（165-177行目） |

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

```
[Admin::UsersController#edit]
    │
    ├─ load_resource (CanCanCan)
    │      └─ User.find(params[:id])
    │
    ├─ detect_previous_id (ApplicationController)
    │
    └─ respond_with(@user)
           └─ edit.js.haml
                  └─ _edit.html.haml
                         └─ _profile.html.haml

[Admin::UsersController#update]
    │
    ├─ User.find(params[:id])
    │
    ├─ user_params (Strong Parameters)
    │      ├─ params[:user][:email].strip!
    │      └─ params[:user][:alt_email].strip!
    │
    ├─ @user.attributes = user_params
    │
    ├─ @user.save
    │      ├─ User バリデーション
    │      └─ groups_users 関連更新
    │
    └─ respond_with(@user)
           └─ update.js.haml
```

### データフロー図

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

編集フォーム入力    ───▶ user_params              ───▶ users テーブル
                           │                              │
user[username]      ───▶ バリデーション           ───▶ groups_users テーブル
user[email]         ───▶ 空白除去                       │
user[password]      ───▶ Devise暗号化            ───▶ 画面更新（AJAX）
user[group_ids]     ───▶ HABTM更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| users_controller.rb | `app/controllers/admin/users_controller.rb` | コントローラー | edit/update アクションの実装 |
| application_controller.rb | `app/controllers/admin/application_controller.rb` | コントローラー | 管理者権限チェック |
| user.rb | `app/models/users/user.rb` | モデル | ユーザーモデル定義 |
| group.rb | `app/models/users/group.rb` | モデル | グループモデル定義 |
| edit.js.haml | `app/views/admin/users/edit.js.haml` | ビュー | 編集フォーム表示JS |
| _edit.html.haml | `app/views/admin/users/_edit.html.haml` | ビュー | 編集フォームHTML |
| _profile.html.haml | `app/views/admin/users/_profile.html.haml` | ビュー | プロファイル入力部分 |
| update.js.haml | `app/views/admin/users/update.js.haml` | ビュー | 更新完了後JS |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義 |
| schema.rb | `db/schema.rb` | 設定 | データベーススキーマ |
