# 機能設計書 39-タスク完了

## 概要

本ドキュメントは、Fat Free CRMシステムにおける「タスク完了」機能の設計を定義する。タスクを完了済み状態に設定するための機能である。

### 本機能の処理概要

タスク完了機能は、実行済みのタスクを完了状態にマークするための機能である。

**業務上の目的・背景**：営業活動において、タスクの完了を記録することで進捗を可視化し、達成感を得るとともに、未完了タスクと区別することでタスク管理の精度を向上させる。完了したタスクは pendingビューから非表示となり、completedビューで確認可能となる。また、完了者と完了日時を記録することで、タスク実行の履歴を追跡できる。

**機能の利用シーン**：タスクを実行完了した場合、フォローアップが完了した場合、会議やミーティングが終了した場合。

**主要な処理内容**：
1. タスクのcompleted_at（完了日時）を現在日時に設定
2. タスクのcompleted_by（完了者）を現在ユーザーIDに設定
3. 空バケットの判定（pendingビューから消えた場合）
4. サイドバー情報の更新
5. UI更新（タスクのフェードアウトまたは完了表示への変更）

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

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 28 | タスク一覧画面 | 主画面 | 一覧からのタスク完了操作 |
| 30 | タスク編集フォーム | 補助画面 | 編集フォームからの完了操作 |

## 機能種別

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

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | Integer | Yes | 完了対象のタスクID | 存在チェック、アクセス権チェック |
| bucket | String | No | 完了元のバケット名 | 空バケット判定用 |

### 入力データソース

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

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| @task | Task | 完了したタスクオブジェクト |
| @empty_bucket | String | 空になったバケット名 |

### 出力先

- 画面表示（AJAX応答）
- データベース（tasksテーブル）

## 処理フロー

### 処理シーケンス

```
1. 完了リクエスト受信
   └─ PUT /tasks/:id/complete
2. タスクの取得
   └─ Task.tracked_by(current_user).find(params[:id])
3. 完了情報の更新
   └─ @task.update(completed_at: Time.now, completed_by: current_user.id)
4. 空バケットの判定
   └─ Task.bucket_empty?(params[:bucket], current_user)
5. サイドバーの更新
   └─ update_sidebar
6. 応答の生成
   └─ JS形式での応答
```

### フローチャート

```mermaid
flowchart TD
    A[完了リクエスト] --> B[タスク取得]
    B --> C{追跡可能?}
    C -->|No| D[404エラー]
    C -->|Yes| E[完了情報更新]
    E --> F[completed_at = 現在日時]
    F --> G[completed_by = current_user.id]
    G --> H{バケット指定あり?}
    H -->|Yes| I[空バケット判定]
    H -->|No| J[サイドバー更新判定]
    I --> J
    J --> K[サイドバー更新]
    K --> L{呼び出し元判定}
    L -->|一覧| M[タスクフェードアウト]
    L -->|詳細| N[完了表示に変更]
    M --> O[完了]
    N --> O
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-39-01 | 追跡可能条件 | user_id = current_user OR assigned_to = current_user | 常時 |
| BR-39-02 | 完了日時記録 | completed_at に現在日時を設定 | 常時 |
| BR-39-03 | 完了者記録 | completed_by に現在ユーザーIDを設定 | 常時 |
| BR-39-04 | 空バケット非表示 | 完了後にバケットが空になった場合は非表示 | bucket指定時 |
| BR-39-05 | ビュー移動 | 完了タスクはpendingビューからcompletedビューへ移動 | 一覧表示時 |

### 計算ロジック

特になし。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| タスク取得 | tasks | SELECT | 完了対象タスクの取得 |
| タスク更新 | tasks | UPDATE | 完了情報の設定 |
| 履歴記録 | versions | INSERT | PaperTrailによる履歴記録 |

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

#### tasks

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | id = params[:id] AND (user_id = current_user OR assigned_to = current_user) | tracked_by |
| UPDATE | completed_at | Time.now | 完了日時 |
| UPDATE | completed_by | current_user.id | 完了者ID |
| UPDATE | updated_at | 現在日時 | 自動設定 |

## エラー処理

### エラーケース一覧

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

### リトライ仕様

特になし。

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

- タスクの更新は暗黙的なトランザクション内で実行
- PaperTrailによる履歴記録も同一トランザクション内で実行

## パフォーマンス要件

- 完了処理: 500ミリ秒以内

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

- tracked_byスコープにより、ユーザーは自分に関連するタスクのみ完了可能
- CSRF対策（Railsデフォルト）

## 備考

- 完了したタスクはcompletedスコープ（completed_at IS NOT NULL）で取得可能
- 完了者はcompletorアソシエーションで取得可能
- 完了後もuncompleteアクションで未完了に戻すことが可能

---

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

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

### 推奨読解順序

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

タスクモデルの完了関連フィールドとスコープを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | task.rb | `app/models/polymorphic/task.rb` | completed関連スコープとアソシエーション |

**読解のコツ**:
- **39行目**: `belongs_to :completor, class_name: "User", foreign_key: :completed_by` - 完了者
- **86行目**: `scope :completed, -> { where('completed_at IS NOT NULL') }` - 完了タスク
- **138-140行目**: `completed?` メソッド - 完了判定

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

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

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

**主要処理フロー**:
1. **117-128行目**: completeアクション全体
2. **120行目**: `Task.tracked_by(current_user).find(params[:id])` - タスク取得
3. **121行目**: `@task.update(completed_at: Time.now, completed_by: current_user.id)` - 完了情報設定
4. **124行目**: `Task.bucket_empty?(params[:bucket], current_user)` - 空バケット判定
5. **126行目**: `update_sidebar unless params[:bucket].blank?` - サイドバー更新

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

完了後のUI処理を確認。

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

**主要処理フロー**:
- **1-7行目**: 一覧からの呼び出し時
  - ツールバーを非表示
  - タスクをフェードアウト
  - 空バケットの場合はバケット全体をフェードアウト
  - サイドバー更新
- **9-14行目**: 詳細画面からの呼び出し時
  - タスク表示を完了スタイルに変更
  - recently表示を更新

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

```
TasksController#complete
    │
    ├─ Task.tracked_by(current_user).find(params[:id])
    │      └─ includes(:assignee)
    │             .where('user_id = ? OR assigned_to = ?', user.id, user.id)
    │
    ├─ @task.update(completed_at: Time.now, completed_by: current_user.id)
    │      └─ UPDATE tasks SET completed_at = ?, completed_by = ? WHERE id = ?
    │
    ├─ Task.bucket_empty?(params[:bucket], current_user)
    │      └─ my(user).send(bucket).pending.count == 0
    │
    ├─ update_sidebar
    │      └─ Task.totals(current_user, @view)
    │
    └─ respond_with(@task)
           └─ render "complete.js.haml"
                  ├─ (一覧) タスクフェードアウト、サイドバー更新
                  └─ (詳細) 完了表示、recently更新
```

### データフロー図

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

PUT /tasks/:id/complete    TasksController#complete
                               │
                               ▼
                       tracked_by(current_user)
                               │
                               ▼
                       find(params[:id])
                               │
                               ▼
                       @task.update(
                         completed_at: Time.now,
                         completed_by: current_user.id
                       )
                               │
                               ▼
                       UPDATE tasks
                               │
                               ▼
                       bucket_empty?判定
                               │
                    ┌──────────┴──────────┐
                    ▼                     ▼
              空でない               空になった
                    │                     │
                    │                     ▼
                    │             @empty_bucket設定
                    │                     │
                    └──────────┬──────────┘
                               ▼
                       update_sidebar
                               │
                               ▼
                       UI更新（AJAX）
                    ┌──────────┴──────────┐
                    ▼                     ▼
              一覧画面               詳細画面
              フェードアウト          完了表示変更
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| tasks_controller.rb | `app/controllers/tasks_controller.rb` | コントローラ | completeアクション |
| task.rb | `app/models/polymorphic/task.rb` | モデル | completedスコープ、completed?メソッド |
| complete.js.haml | `app/views/tasks/complete.js.haml` | ビュー | 完了後AJAX処理 |
| _completed.html.haml | `app/views/tasks/_completed.html.haml` | ビュー | 完了タスク表示パーシャル |
| tasks_helper.rb | `app/helpers/tasks_helper.rb` | ヘルパー | link_to_task_complete |
| routes.rb | `config/routes.rb` | 設定 | PUT /tasks/:id/complete ルーティング（150行目） |
