# 機能設計書 108-タスク工数管理

## 概要

本ドキュメントは、プロジェクトプラグイン(projects)におけるタスク工数管理機能の設計仕様を定義する。

### 本機能の処理概要

タスクに紐付くタイムシートを管理し、タスク単位での作業時間の記録・集計・分析を支援する機能を提供する。

**業務上の目的・背景**：タスクごとの作業時間を正確に記録し、計画工数と実績工数の差異を把握する。これにより、プロジェクトの予算管理や今後の見積もり精度向上に貢献する。

**機能の利用シーン**：
- タスク担当者がタスク画面から直接作業時間を記録する
- プロジェクトマネージャーがタスクの工数消化状況を確認する
- 残り時間や進捗率を確認してリソース配分を調整する

**主要な処理内容**：
1. タスクに紐付くタイムシートの作成・編集・削除
2. 作業時間の集計表示（割当時間/実績時間/残り時間）
3. 進捗率の自動計算
4. サブタスクを含めた工数集計

**関連システム・外部連携**：Task、Timesheet、Project、Userと連携。

**権限による制御**：TimeSettings.enable_timesheetsで機能全体の有効/無効を制御。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| SCR-TIME-001 | TimesheetsRelationManager | 関連画面 | タスク詳細からのタイムシート管理 |
| SCR-TIME-002 | ManageTimesheets (Task) | 関連画面 | タスクのタイムシート管理ページ |

## 機能種別

CRUD操作 / リレーション管理 / 集計機能

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| date | date | Yes | 作業日 | 日付形式 |
| user_id | integer | Yes | 従業員ID | usersテーブルに存在するID |
| unit_amount | float | Yes | 作業時間(時間単位) | 数値、0以上 |
| name | string | No | 説明 | - |
| task_id | integer | Yes | タスクID | 自動設定（RelationManager経由） |
| project_id | integer | Yes | プロジェクトID | タスクから継承 |

### 入力データソース

画面フォーム入力、Timesheetモデル

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | integer | タイムシートID |
| date | date | 作業日 |
| user.name | string | 従業員名 |
| name | string | 説明 |
| unit_amount | float | 作業時間（時:分形式） |
| created_at | datetime | 作成日時 |

### 集計データ（タスク工数）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| allocated_hours | float | 割当時間 |
| effective_hours | float | 当該タスクの実績時間 |
| subtask_effective_hours | float | サブタスクの実績時間合計 |
| total_hours_spent | float | 総実績時間 |
| remaining_hours | float | 残り時間 |
| overtime | float | 超過時間 |
| progress | float | 進捗率(%) |

### 出力先

Filament RelationManager画面、タスク詳細Infolist、データベース

## 処理フロー

### 処理シーケンス

```
1. タイムシート一覧表示
   └─ タスク(task_id)でフィルタして表示
   └─ Sumで合計時間を集計表示
2. タイムシート作成
   └─ フォーム入力 → バリデーション → DBへ保存 → タスク工数自動更新
3. タイムシート編集
   └─ 既存データ取得 → フォーム表示 → 更新保存 → タスク工数自動更新
4. タイムシート削除
   └─ 削除確認 → 物理削除 → タスク工数自動更新
```

### フローチャート

```mermaid
flowchart TD
    A[タスク詳細画面] --> B{enable_timesheets?}
    B -->|No| C[機能非表示]
    B -->|Yes| D[タイムシート一覧表示]
    D --> E{操作選択}
    E -->|作成| F[作成フォーム表示]
    E -->|編集| G[編集フォーム表示]
    E -->|削除| H[削除確認]
    F --> I[入力バリデーション]
    G --> I
    I -->|OK| J[DB保存]
    I -->|NG| K[エラー表示]
    H --> L[物理削除]
    J --> M[タスク工数自動更新]
    L --> M
    M --> N[サブタスク存在確認]
    N -->|Yes| O[親タスク工数更新]
    N -->|No| P[完了]
    O --> P
    K --> E
    P --> D
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-108-01 | 機能有効化制御 | TimeSettings.enable_timesheets=trueの場合のみ表示 | 全画面 |
| BR-108-02 | 工数自動更新 | タイムシートCUD時にタスクの工数フィールドを自動更新 | タイムシート操作時 |
| BR-108-03 | サブタスク集計 | サブタスクのタイムシートも親タスクのtotal_hours_spentに集計 | 工数更新時 |
| BR-108-04 | 親タスク連動 | サブタスクの工数変更時に親タスクの工数も自動更新 | サブタスク工数更新時 |
| BR-108-05 | 時間表示形式 | unit_amountを時:分形式で表示 | 一覧表示時 |
| BR-108-06 | 進捗率上限なし | progress > 100%も許容（超過表示） | 進捗表示時 |

### 計算ロジック

タスク工数の自動更新（Timesheet::updateTaskTimes）:
```php
// タスクの工数計算
$effectiveHours = $hoursSpent = $task->timesheets()->sum('unit_amount');

// サブタスクがある場合
if ($task->subTasks->count()) {
    $hoursSpent += $task->subTasks->reduce(function ($carry, $subTask) {
        return $carry + $subTask->timesheets()->sum('unit_amount');
    }, 0);
}

$task->update([
    'total_hours_spent' => $hoursSpent,
    'effective_hours'   => $effectiveHours,
    'overtime'          => $hoursSpent > $task->allocated_hours
                            ? $hoursSpent - $task->allocated_hours : 0,
    'remaining_hours'   => $task->allocated_hours - $hoursSpent,
    'progress'          => $task->allocated_hours
                            ? ($hoursSpent / $task->allocated_hours) * 100 : 0,
]);

// 親タスクの更新
if ($parentTask = $task->parent) {
    $parentEffectiveHours = $parentHoursSpent = $parentTask->timesheets()->sum('unit_amount');
    $parentHoursSpent += $parentTask->subTasks->reduce(...);

    $parentTask->update([
        'total_hours_spent'       => $parentHoursSpent,
        'effective_hours'         => $parentEffectiveHours,
        'subtask_effective_hours' => $parentTask->subTasks->sum('effective_hours'),
        'overtime'                => ...,
        'remaining_hours'         => ...,
        'progress'                => ...,
    ]);
}
```

時間表示形式:
```php
$hours = floor($state);
$minutes = ($state - $hours) * 60;
return $hours.':'.$minutes;
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 一覧表示 | analytics_records | SELECT | task_id=タスクIDでフィルタ |
| 作成 | analytics_records | INSERT | 新規タイムシート登録 |
| 作成後 | projects_tasks | UPDATE | タスクの工数フィールド更新 |
| 編集 | analytics_records | UPDATE | タイムシート情報更新 |
| 編集後 | projects_tasks | UPDATE | タスクの工数フィールド更新 |
| 削除 | analytics_records | DELETE | 物理削除 |
| 削除後 | projects_tasks | UPDATE | タスクの工数フィールド更新 |

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

#### analytics_records (Timesheet)

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | date | ユーザー入力値 | 必須 |
| INSERT | user_id | 選択値 | 必須 |
| INSERT | unit_amount | ユーザー入力値 | 必須 |
| INSERT | name | ユーザー入力値 | 任意 |
| INSERT | task_id | RelationManagerで自動設定 | 必須 |
| INSERT | project_id | タスクから継承 | 自動設定 |

#### projects_tasks (自動更新)

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | effective_hours | タイムシート合計 | 計算値 |
| UPDATE | subtask_effective_hours | サブタスクeffective_hours合計 | 計算値 |
| UPDATE | total_hours_spent | effective + サブタスク | 計算値 |
| UPDATE | remaining_hours | allocated - total | 計算値 |
| UPDATE | overtime | 超過時間 | 計算値 |
| UPDATE | progress | 進捗率 | 計算値(%) |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| E-108-01 | バリデーションエラー | 必須項目未入力 | エラーメッセージ表示 |
| E-108-02 | バリデーションエラー | unit_amountが負の値 | エラーメッセージ表示 |

### リトライ仕様

特になし(画面操作による再実行)

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

タイムシートの作成・編集・削除とタスク工数更新は同一トランザクション内で実行（Eloquentイベント）。

## パフォーマンス要件

- 一覧表示: 2秒以内
- 作成/編集/削除: 1秒以内
- 集計計算: リアルタイム

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

- 認証済みユーザーのみアクセス可能
- TimeSettings.enable_timesheetsによる機能制御

## 備考

- TimesheetsRelationManagerとして実装
- タスクサブナビゲーションの「Timesheets」タブからアクセス
- Sum::make()で作業時間の合計をリアルタイム表示
- グループ化機能（日付/従業員/作成日）対応
- reorderable対応
