# 機能設計書 38-タスク削除

## 概要

本ドキュメントは、Fat Free CRMシステムにおける「タスク削除」機能の設計を定義する。既存のタスクレコードを削除するための機能である。

### 本機能の処理概要

タスク削除機能は、不要になったタスクや誤って作成されたタスクをシステムから削除するための機能である。

**業務上の目的・背景**：営業活動において、完了したタスクの整理、キャンセルされた予定の削除、重複タスクの削除など、不要なタスクを整理することでタスク一覧の見通しを良くし、業務効率を向上させる。削除されたタスクはサイドバーの件数カウントからも除外される。

**機能の利用シーン**：不要になったタスクの削除、誤って作成したタスクの削除、完了後のタスク整理（完了状態で残すか削除するかはユーザー判断）。

**主要な処理内容**：
1. 削除対象タスクの特定と権限チェック
2. タスクレコードの削除実行
3. 空バケットの判定
4. サイドバー情報の更新（一覧画面からの呼び出し時）
5. UI更新（削除したタスクの非表示）

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

**権限による制御**：ユーザーは自分が作成したタスク、または自分に割り当てられたタスクのみ削除可能（tracked_byスコープ）。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 28 | タスク一覧画面 | 主画面 | 一覧からのタスク削除 |

## 機能種別

CRUD操作（Delete） / データ削除

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | Integer | Yes | 削除対象のタスクID | 存在チェック、アクセス権チェック |
| bucket | String | No | 削除元のバケット名 | 空バケット判定用 |
| view | String | No | 現在の表示ビュー | pending/assigned/completed |

### 入力データソース

- URLパラメータ（タスクID、bucket）
- セッション情報（current_user）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| @task | Task | 削除されたタスクオブジェクト |
| @view | String | 現在の表示ビュー |
| @empty_bucket | String | 空になったバケット名 |

### 出力先

- 画面表示（AJAX応答）
- データベース（レコード削除）

## 処理フロー

### 処理シーケンス

```
1. 削除リクエスト受信
   └─ DELETE /tasks/:id
2. タスクの取得
   └─ Task.tracked_by(current_user).find(params[:id])
3. タスクレコードの削除
   └─ @task.destroy
4. 空バケットの判定
   └─ Task.bucket_empty?(params[:bucket], current_user, @view)
5. サイドバーの更新（一覧画面からの呼び出し時）
   └─ update_sidebar
6. 応答の生成
   └─ JS形式での応答
```

### フローチャート

```mermaid
flowchart TD
    A[削除リクエスト] --> B[タスク取得]
    B --> C{追跡可能?}
    C -->|No| D[404エラー]
    C -->|Yes| E[タスク削除]
    E --> F{バケット指定あり?}
    F -->|Yes| G[空バケット判定]
    F -->|No| H[サイドバー更新判定]
    G --> H
    H --> I{一覧からの呼び出し?}
    I -->|Yes| J[サイドバー更新]
    I -->|No| K[応答生成]
    J --> K
    K --> L[UI更新]
    L --> M[完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-38-01 | 追跡可能条件 | user_id = current_user OR assigned_to = current_user | 常時 |
| BR-38-02 | 物理削除 | タスクは論理削除ではなく物理削除される | 常時 |
| BR-38-03 | 空バケット非表示 | 削除後にバケットが空になった場合は非表示 | bucket指定時 |

### 計算ロジック

特になし。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| タスク取得 | tasks | SELECT | 削除対象タスクの取得 |
| タスク削除 | tasks | DELETE | タスクレコードの物理削除 |
| 履歴削除 | versions | DELETE | PaperTrail履歴の削除 |

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

#### tasks

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | id = params[:id] AND (user_id = current_user OR assigned_to = current_user) | tracked_by |
| DELETE | - | id = 指定ID | 物理削除 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 404 | Not Found | タスクが存在しない、またはアクセス権がない | エラー画面表示 |

### リトライ仕様

削除処理は即座に完了するため、リトライ仕様は特になし。

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

- タスクの削除は暗黙的なトランザクション内で実行
- いずれかの処理が失敗した場合はロールバック

## パフォーマンス要件

- 削除処理: 500ミリ秒以内

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

- tracked_byスコープにより、ユーザーは自分に関連するタスクのみ削除可能
- CSRF対策（Railsデフォルト）
- 削除は物理削除のため、データの復元は不可

## 備考

- 削除されたタスクはPaperTrailの履歴からも削除される
- 削除前の確認ダイアログはUI側で実装

---

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

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

### 推奨読解順序

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

TasksControllerのdestroyアクションが処理の起点。

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

**主要処理フロー**:
1. **103-115行目**: destroyアクション全体
2. **106行目**: `@view = view` - ビューの設定
3. **107行目**: `Task.tracked_by(current_user).find(params[:id])` - タスク取得
4. **108行目**: `@task.destroy` - 削除処理
5. **111行目**: `Task.bucket_empty?(params[:bucket], current_user, @view)` - 空バケット判定
6. **113行目**: `update_sidebar if called_from_index_page?` - サイドバー更新

#### Step 2: バケット判定を理解する

タスクモデルのbucket_empty?メソッドを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | task.rb | `app/models/polymorphic/task.rb` | bucket_empty?メソッド |

**読解のコツ**:
- **189-199行目**: `bucket_empty?` メソッド
- ビューとバケットに応じてタスク件数をカウント
- 0件の場合はtrueを返す

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

削除後のUI処理を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | destroy.js.haml | `app/views/tasks/destroy.js.haml` | 削除後JS処理 |

**主要処理フロー**:
- **1行目**: `$('##{dom_id(@task)}').slideUp(250);` - タスク要素を非表示
- **3-5行目**: 空バケットの場合はバケット全体を非表示
- **6-7行目**: 一覧からの呼び出し時はサイドバー更新

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

```
TasksController#destroy
    │
    ├─ view (private method)
    │      └─ ALLOWED_VIEWS.include?(params[:view])
    │
    ├─ Task.tracked_by(current_user).find(params[:id])
    │      └─ includes(:assignee)
    │             .where('user_id = ? OR assigned_to = ?', user.id, user.id)
    │
    ├─ @task.destroy
    │      └─ DELETE FROM tasks WHERE id = ?
    │
    ├─ Task.bucket_empty?(params[:bucket], current_user, @view)
    │      └─ my(user).send(bucket).send(view).count == 0
    │
    ├─ update_sidebar (if called_from_index_page?)
    │      └─ Task.totals(current_user, @view)
    │
    └─ respond_with(@task)
           └─ render "destroy.js.haml"
                  ├─ $('#task_xxx').slideUp()
                  ├─ $('#list_bucket').fadeOut() (if empty)
                  └─ refresh_sidebar(:index)
```

### データフロー図

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

DELETE /tasks/:id      TasksController#destroy
                               │
                               ▼
                       tracked_by(current_user)
                               │
                               ▼
                       find(params[:id])
                               │
                               ▼
                       @task.destroy
                               │
                               ▼
                       DELETE tasks
                               │
                               ▼
                       bucket_empty?判定
                               │
                    ┌──────────┴──────────┐
                    ▼                     ▼
              空でない               空になった
                    │                     │
                    │                     ▼
                    │             @empty_bucket設定
                    │                     │
                    └──────────┬──────────┘
                               ▼
                       update_sidebar
                               │
                               ▼
                       UI更新（AJAX）
                       タスク要素非表示
                       (バケット非表示)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| tasks_controller.rb | `app/controllers/tasks_controller.rb` | コントローラ | destroyアクション |
| task.rb | `app/models/polymorphic/task.rb` | モデル | bucket_empty?メソッド |
| destroy.js.haml | `app/views/tasks/destroy.js.haml` | ビュー | 削除後AJAX処理 |
| tasks_helper.rb | `app/helpers/tasks_helper.rb` | ヘルパー | hide_task_and_possibly_bucket |
| routes.rb | `config/routes.rb` | 設定 | DELETE /tasks/:id ルーティング |
