# 機能設計書 77-カスタムフィールド並び替え

## 概要

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

### 本機能の処理概要

カスタムフィールド並び替え機能は、フィールドグループ内およびフィールドグループ間でカスタムフィールドの表示順序を変更する管理者専用機能である。ドラッグ&ドロップ操作により、直感的にフィールドの順序を変更できる。フィールドを別のフィールドグループに移動することも可能である。

**業務上の目的・背景**：業務プロセスの効率化のため、よく使用するフィールドを上部に配置したり、関連するフィールドをグループ化したりする必要がある。この機能により、管理者はユーザーの入力効率を考慮した最適なフィールド配置を実現できる。

**機能の利用シーン**：
- 重要なフィールドを上部に移動する場合
- 関連するフィールドを近くに配置する場合
- フィールドを別のフィールドグループに移動する場合
- 新規追加したフィールドの位置を調整する場合

**主要な処理内容**：
1. ドラッグ&ドロップによる並び順の変更（JavaScript）
2. 並び順情報のサーバー送信（sortアクション）
3. 各フィールドのposition更新
4. フィールドグループの変更（移動の場合）

**関連システム・外部連携**：本機能は外部システムとの連携はなく、Fat Free CRM 内部のデータベースを操作する。JavaScriptライブラリ（Sortable）を使用してドラッグ&ドロップを実現している。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 43 | カスタムフィールド管理画面 | 主画面 | ドラッグ&ドロップによるフィールド並び替え |

## 機能種別

CRUD操作（Update）/ UI操作

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| field_group_id | Integer | Yes | 対象フィールドグループのID | 存在するFieldGroupIDであること |
| fields_field_group_{id} | Array<Integer> | Yes | フィールドIDの配列（並び順） | 有効なフィールドIDの配列であること |

### 入力データソース

- JavaScript（ドラッグ&ドロップ操作）
- AJAXリクエスト（POSTデータ）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| - | - | レスポンスボディなし（200 OK のみ） |

### 出力先

- HTTPレスポンス（200 OK）
- データベース（fieldsテーブルのposition, field_group_id更新）

## 処理フロー

### 処理シーケンス

```
1. ドラッグ&ドロップ操作（JavaScript）
   └─ Sortableライブラリによるフィールド移動
2. sortアクション呼び出し（AJAX POST）
   └─ field_group_id の取得
   └─ フィールドIDの配列取得
3. 並び順更新処理
   └─ 各フィールドのposition, field_group_idを更新
4. レスポンス返却
   └─ render nothing: true（200 OK）
```

### フローチャート

```mermaid
flowchart TD
    A[フィールドをドラッグ] --> B[ドロップ位置を決定]
    B --> C[JavaScript: Sortable]
    C --> D[AJAX POST /admin/fields/sort]
    D --> E[sortアクション]
    E --> F[field_group_id取得]
    F --> G[フィールドID配列取得]
    G --> H[ループ: 各フィールド]
    H --> I[position更新]
    I --> J[field_group_id更新]
    J --> K{次のフィールドあり?}
    K -->|Yes| H
    K -->|No| L[200 OK返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-77-01 | 管理者権限必須 | フィールド並び替えは管理者ユーザーのみ実行可能 | 常時 |
| BR-77-02 | Position 1始まり | フィールドのpositionは1から始まる連番 | 並び替え時 |
| BR-77-03 | グループ間移動可 | フィールドは別のフィールドグループに移動可能 | ドラッグ&ドロップ時 |
| BR-77-04 | 即時反映 | 並び順の変更は即座にデータベースに反映 | 並び替え完了時 |

### 計算ロジック

#### Position 計算
```
各フィールドのposition = 配列のindex + 1
（0始まりのindexに1を加算して1始まりに変換）
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| フィールド更新 | fields | UPDATE | position, field_group_id の更新 |

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

#### fields テーブル

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | position | index + 1 | 表示順（1始まり） |
| UPDATE | field_group_id | パラメータ値 | 所属グループ |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 403 | Forbidden | 管理者権限がない | ログインページへリダイレクト |
| 400 | BadRequest | パラメータ不正 | エラーログ出力 |

### リトライ仕様

リトライは不要（失敗時は再度ドラッグ&ドロップ操作）

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

複数フィールドの更新は個別のUPDATE文で実行される。明示的なトランザクション制御は行っていない。並び替えが途中で失敗した場合、一部のpositionのみ更新される可能性がある。

## パフォーマンス要件

- レスポンス時間：1秒以内（AJAX応答）
- 更新対象は移動先グループのフィールドのみ

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

- 管理者認証必須（before_action :require_admin_user）
- CSRF トークン検証（Rails標準）
- パラメータはInteger型に変換されるため、SQLインジェクションリスクは低い

## 備考

- acts_as_list gemを使用しているが、sortアクションでは直接position更新を行っている
- フィールドグループの並び替え機能は別途 Admin::FieldGroupsController#sort で提供

---

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

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

### 推奨読解順序

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

Fieldモデルのposition管理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | field.rb | `app/models/fields/field.rb` | acts_as_list、position カラム |

**読解のコツ**: `acts_as_list` はpositionカラムによる並び順管理を提供するgemである。sortアクションでは直接UPDATEを行っている。

**主要処理フロー**:
- **field.rb 32行目**: `acts_as_list` - 並び順管理gemの使用宣言

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

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

**主要処理フロー**:
- **84-93行目**: `sort` アクション - 並び順更新処理
- **85行目**: `field_group_id = params["field_group_id"].to_i` - グループID取得
- **86行目**: `field_ids = params["fields_field_group_#{field_group_id}"]` - フィールドID配列取得
- **88-90行目**: `each_with_index` - position, field_group_idの更新
- **92行目**: `render nothing: true` - レスポンスなし返却

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

```
JavaScript (Sortable)
    │
    └─ AJAX POST /admin/fields/sort
           │
           ▼
Admin::FieldsController#sort
    │
    ├─ Admin::ApplicationController
    │      └─ require_admin_user（管理者権限チェック）
    │
    ├─ params["field_group_id"].to_i
    │
    ├─ params["fields_field_group_#{id}"]
    │
    └─ field_ids.each_with_index
           │
           └─ Field.where(id: id).update_all(
                  position: index + 1,
                  field_group_id: field_group_id
              )
```

### データフロー図

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

ドラッグ&ドロップ ────▶ Sortable.js ──────────▶ AJAX POST
                              │
                              ▼
                        sortアクション
                              │
                              ├─ field_group_id 取得
                              │
                              └─ field_ids 取得
                                     │
                                     ▼
                              each_with_index ループ
                                     │
                                     ▼
                              Field.update_all
                                     │
                                     ▼
                              fields テーブル UPDATE
                                     │
                                     ▼
                              200 OK
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| field.rb | `app/models/fields/field.rb` | モデル | Fieldモデル、acts_as_list |
| fields_controller.rb | `app/controllers/admin/fields_controller.rb` | コントローラー | sortアクション |
| application_controller.rb | `app/controllers/admin/application_controller.rb` | コントローラー | 管理者基底コントローラー |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義（195行目: post :sort） |
