# 機能設計書 29-カードを再オープン

## 概要

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

### 本機能の処理概要

カード再オープン機能は、クローズ状態のカードを再度オープン（アクティブ）状態に戻す機能です。Closureレコードを削除し、カードを再びアクティブなタスクとして扱えるようにします。

**業務上の目的・背景**：プロジェクト管理において、一度完了としたタスクを再度作業対象にする必要が生じることがあります。例えば、誤ってクローズしてしまった場合や、追加作業が必要になった場合などです。この機能により、柔軟なタスク管理が可能になります。

**機能の利用シーン**：
- 誤ってクローズしたカードを再開する場合
- 完了したと思ったタスクに追加作業が必要になった場合
- クローズしたタスクを参考に新たな作業を開始する場合

**主要な処理内容**：
1. Closureレコードを削除してクローズ状態を解除
2. 再オープンイベントの記録
3. 画面の動的更新（Turbo Stream）

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

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 19 | カード詳細画面 | 主画面 | 再オープンボタンの表示と実行 |
| 10 | ボード詳細画面 | 結果表示画面 | 再オープン後の画面更新 |

## 機能種別

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

## 入力仕様

### 入力パラメータ

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

### 入力データソース

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

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Turbo Stream | HTML | 画面の動的更新（HTML形式） |
| HTTP Status | Integer | 成功時: 204 No Content（JSON形式） |

### 出力先

- Turbo Stream形式：カード、カラム/ストリーム、クローズドセクションの動的更新
- JSON形式：HTTP 204 No Content

## 処理フロー

### 処理シーケンス

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

2. カード取得
   └─ CardScoped concernによりカードを取得

3. 再オープン処理実行
   └─ @card.reopen
       ├─ Closureレコードを削除
       └─ 再オープンイベントを記録

4. ストリーム更新
   └─ refresh_stream_after_reopen で必要に応じてストリームを更新

5. レスポンス返却
   └─ Turbo Stream: カードとカラム/ストリームを更新
   └─ JSON: HTTP 204 No Content
```

### フローチャート

```mermaid
flowchart TD
    A[再オープンリクエスト受信] --> B[カード取得]
    B --> C{クローズ済み?}
    C -->|No| D[何もしない]
    C -->|Yes| E[Closureレコード削除]
    E --> F[イベント記録]
    F --> G[ストリーム更新]
    D --> G
    G --> H{レスポンス形式}
    H -->|Turbo Stream| I[画面動的更新]
    H -->|JSON| J[204 No Content]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-29-01 | 冪等性 | 既にオープンの場合は何もしない | オープン状態カードへの操作 |
| BR-29-02 | トリアージ待ち復帰 | 再オープン後、カラム未割り当ての場合はストリームに表示 | カラム未設定のカードの場合 |
| BR-29-03 | イベント記録 | :reopened イベントが記録される | すべての再オープン操作 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 再オープン | closures | DELETE | Closureレコードの削除 |
| イベント記録 | events | INSERT | 再オープンイベントを記録 |

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

#### closures

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| DELETE | - | WHERE card_id = @card.id | Closureレコード削除 |

#### events

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | action | "reopened" | イベント種別 |
| INSERT | creator_id | Current.user.id | 再オープンしたユーザー |

## エラー処理

### エラーケース一覧

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

### リトライ仕様

冪等性あり（既にオープンの場合は状態を変えない）

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

reopen メソッド内で transaction ブロックを使用し、Closure削除、イベント記録を一括で処理します。いずれかが失敗した場合はロールバックされます。

## パフォーマンス要件

- 再オープン処理は500ms以内に完了すること

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

- 認証：ログインユーザーのみアクセス可能
- 認可：カードにアクセス可能なユーザーのみ操作可能
- CSRF対策：Rails標準のCSRFトークン検証を適用

## 備考

- 再オープン後のカードはカラム割り当てが維持される（クローズ前のカラムに戻る場合がある）
- カラム未設定の場合はストリーム（トリアージ待ち）に表示される

---

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

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

### 推奨読解順序

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

再オープン処理の仕組みを理解するために、Closeableモジュールを把握することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | closeable.rb | `app/models/card/closeable.rb` | reopenメソッドの定義 |
| 1-2 | closure.rb | `app/models/closure.rb` | Closureモデルの定義 |

**読解のコツ**: `reopen` メソッドは `close` の逆操作です。Closureレコードを削除することでオープン状態に戻します。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | closures_controller.rb | `app/controllers/cards/closures_controller.rb` | destroyアクションが再オープンのエントリーポイント |

**主要処理フロー**:
1. **16行目**: `@card.reopen` - 再オープン処理の実行
2. **17行目**: `refresh_stream_after_reopen` - ストリームの更新

#### Step 3: 再オープン処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | closeable.rb | `app/models/card/closeable.rb` | reopenメソッドの実装詳細 |

**主要処理フロー**:
- **41-48行目**: `reopen`メソッド
  - **42行目**: `if closed?` - 既にオープンなら何もしない
  - **43行目**: トランザクション開始
  - **44行目**: `closure&.destroy` - Closureレコードを削除
  - **45行目**: `track_event :reopened` - イベント記録

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

```
Cards::ClosuresController#destroy
    │
    ├─ CardScoped#set_card (before_action)
    │      └─ Current.user.accessible_cards.find_by!(number: params[:card_id])
    │
    ├─ @card.reopen
    │      └─ Card::Closeable#reopen
    │             ├─ if closed? (冪等性チェック)
    │             ├─ transaction
    │             │      ├─ closure&.destroy
    │             │      └─ track_event :reopened
    │             └─ end
    │
    └─ refresh_stream_after_reopen
           └─ awaiting_triage? の場合ストリーム更新
```

### データフロー図

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

DELETE /cards/:id/closure ──▶ ClosuresController#destroy ──▶ Turbo Stream/204
  │                               │
  │ card_id                       ├─ @card.reopen
  │                               │    ├─ closure 削除
  │                               │    └─ イベント記録
  │                               │
  │                               └─ refresh_stream_after_reopen
  │                                    └─ ストリーム更新（条件付き）
  │
  └─ closures (DELETE), events (INSERT)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| closures_controller.rb | `app/controllers/cards/closures_controller.rb` | コントローラー | 再オープンリクエストの処理 |
| card_scoped.rb | `app/controllers/concerns/card_scoped.rb` | コントローラー(concern) | カード取得 |
| closeable.rb | `app/models/card/closeable.rb` | モデル(concern) | 再オープン処理ロジック |
| closure.rb | `app/models/closure.rb` | モデル | Closureモデル定義 |
| destroy.turbo_stream.erb | `app/views/cards/closures/destroy.turbo_stream.erb` | ビュー | Turbo Streamレスポンス |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義（80行目） |
