# 機能設計書 27-カードを別カラムに移動

## 概要

本ドキュメントは、Fizzyアプリケーションにおけるカードを別カラムに移動する機能の設計を記載するものです。

### 本機能の処理概要

カードを別カラムに移動する機能は、同一ボード内でカードを別のカラムに移動する機能です。カラム選択UIでカードの所属カラムを変更できます。

**業務上の目的・背景**：カンバンボードでは、タスクの進捗に応じてカードを異なるカラム（例：To Do → In Progress → Done）に移動することが基本的なワークフローです。この機能により、ドラッグ＆ドロップやカラム選択UIを通じて、タスクの状態を直感的に更新できます。

**機能の利用シーン**：
- タスクの作業を開始し、「In Progress」カラムに移動する場合
- タスクが完了し、「Done」カラムに移動する場合
- タスクの優先順位を変更してカラムを切り替える場合

**主要な処理内容**：
1. カラム選択UIの表示
2. 選択されたカラムへのカード移動（triage_into メソッドを使用）
3. イベントの記録

**関連システム・外部連携**：Webhookが設定されている場合、カラム変更イベントが通知されます。

**権限による制御**：カードにアクセス可能なユーザーであれば誰でもカラム移動操作を実行できます。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 24 | カードカラム変更画面 | 主画面 | カラム選択UIの表示 |
| 19 | カード詳細画面 | 参照画面 | カラム変更オプションへのリンク |
| 10 | ボード詳細画面 | 参照画面 | ドラッグ＆ドロップによるカラム移動 |

## 機能種別

CRUD操作（UPDATE） / ステータス変更

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| card_id | String | Yes | カード番号（number） | 存在するカードであること |

### 入力データソース

- URL パラメータからカード番号を取得

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| @card | Card | 対象カード |
| @columns | Array | ボード内のカラム一覧（ソート済み） |

### 出力先

- HTML形式：カラム選択UI画面

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信（GET /cards/:card_id/column/edit）
   └─ カード番号をURLパラメータから取得

2. カード取得
   └─ Current.user.accessible_cards からカードを取得

3. カラム一覧取得
   └─ @card.board.columns.sorted でボード内のカラムを取得

4. キャッシュ制御
   └─ fresh_when etag: [ @card, @columns ] でキャッシュ判定

5. レスポンス返却
   └─ カラム選択UIを描画
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B[カード取得]
    B --> C[カラム一覧取得]
    C --> D[キャッシュ判定]
    D --> E{変更あり?}
    E -->|No| F[304 Not Modified]
    E -->|Yes| G[カラム選択UI描画]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-27-01 | 同一ボード制約 | カラムは同じボード内のカラムのみ選択可能 | すべてのカラム選択 |
| BR-27-02 | カラムソート | カラムは position 順にソートして表示 | カラム選択UI表示時 |
| BR-27-03 | 特殊ステータス | Not Now、Maybe?（トリアージ）、Done の選択も可能 | カラム選択UI |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| カード取得 | cards | SELECT | 対象カードの取得 |
| カラム取得 | columns | SELECT | ボード内カラム一覧の取得 |

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

#### columns

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | 全カラム | WHERE board_id = @card.board_id ORDER BY position | ソート済み |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 404 | RecordNotFound | 指定されたカードが存在しない | 適切なエラー画面を表示 |

### リトライ仕様

読み取り専用のためリトライ不要

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

読み取り専用のためトランザクション管理不要（カラム変更の実際の処理はトリアージ機能で行う）

## パフォーマンス要件

- カラム選択UIの表示は300ms以内に完了すること

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

- 認証：ログインユーザーのみアクセス可能
- 認可：カードにアクセス可能なユーザーのみ操作可能
- キャッシュ制御：ETagを使用した条件付きGETに対応

## 備考

- 実際のカラム変更処理はトリアージ機能（No.24）で行う
- カラム選択UIでは Not Now、Maybe?（トリアージ戻し）、Done も選択可能
- この機能はカラム選択UIの表示のみを担当し、更新処理は行わない

---

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

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

### 推奨読解順序

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

カラムの構造とボードとの関連を理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | column.rb | `app/models/column.rb` | Columnモデルの定義、sortedスコープ |
| 1-2 | card.rb | `app/models/card.rb` | belongs_to :board, belongs_to :column の関連 |

**読解のコツ**: Column モデルには `sorted` スコープがあり、position 順にカラムをソートします。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | columns_controller.rb | `app/controllers/cards/columns_controller.rb` | editアクションがカラム選択UIのエントリーポイント |

**主要処理フロー**:
1. **3行目**: `@card = Current.user.accessible_cards.find_by!(number: params[:card_id])` - カード取得
2. **4行目**: `@columns = @card.board.columns.sorted` - カラム一覧取得
3. **6行目**: `fresh_when etag: [ @card, @columns ]` - キャッシュ制御

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | edit.html.erb | `app/views/cards/columns/edit.html.erb` | カラム選択UIの実装 |

**主要処理フロー**:
- **5-12行目**: Not Now ボタン（延期機能へのリンク）
- **14-21行目**: Maybe? ボタン（トリアージ戻しへのリンク）
- **23-25行目**: 各カラムボタン（トリアージ機能へのリンク）
- **27-34行目**: Done ボタン（クローズ機能へのリンク）

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

```
Cards::ColumnsController#edit
    │
    ├─ @card = Current.user.accessible_cards.find_by!
    │
    ├─ @columns = @card.board.columns.sorted
    │      └─ Column.sorted スコープ
    │
    └─ fresh_when etag: [ @card, @columns ]
           └─ キャッシュ判定

（実際のカラム変更は以下の機能で処理）
├─ Not Now → Cards::NotNowsController#create
├─ Maybe? → Cards::TriagesController#destroy
├─ カラム選択 → Cards::TriagesController#create
└─ Done → Cards::ClosuresController#create
```

### データフロー図

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

GET /cards/:id/column/edit ──▶ ColumnsController#edit ──▶ カラム選択UI
  │                               │
  │ card_id                       ├─ カード取得
  │                               │    └─ cards (SELECT)
  │                               │
  │                               └─ カラム一覧取得
  │                                    └─ columns (SELECT)
  │
  └─ HTML: カラム選択UI描画
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| columns_controller.rb | `app/controllers/cards/columns_controller.rb` | コントローラー | カラム選択UIの表示 |
| column.rb | `app/models/column.rb` | モデル | カラムモデル定義 |
| card.rb | `app/models/card.rb` | モデル | カードモデル定義 |
| edit.html.erb | `app/views/cards/columns/edit.html.erb` | ビュー | カラム選択UI |
| _column.html.erb | `app/views/cards/columns/_column.html.erb` | ビュー | カラムボタンのパーシャル |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義（81行目） |
