Files
keinasystem/document/19_マスタードキュメント_TODO管理編.md
akira b7b5ce3943 docs: TODO管理機能のマスタードキュメントを作成し仕様書案を更新
- 論点1-5の決定を仕様書案に反映
- document/19_マスタードキュメント_TODO管理編.md を新規作成
- CLAUDE.md のマスタードキュメント一覧に TODO管理を追加
2026-04-10 13:14:07 +09:00

368 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# マスタードキュメントTODO管理機能
> **作成**: 2026-04-10
> **最終更新**: 2026-04-10
> **対象機能**: TODO管理作業指示・優先順位管理・実績連携導線
> **実装状況**: 設計完了・実装前
> **対象 Issue**: `akira/keinasystem#17`
---
## 概要
繁忙期に「どれから手を付けるか」を管理するための TODO 機能。
計画(施肥・田植え・運搬など)と実績の間に位置する「作業指示」レイヤー。
### 機能スコープIN / OUT
| INMVP対象 | OUT対象外 |
|---|---|
| TODO の作成・編集・削除 | 期日通知・リマインダー |
| ステータス管理todo / doing / done / canceled | 複数ユーザー割り当て |
| 優先順位管理(ドラッグ&ドロップ / 矢印移動) | コメント・添付ファイル |
| 圃場単位の対象紐づけ | 工数見積・実績時間記録 |
| 計画との紐づけ(施肥・田植え・運搬) | 完全な汎用ワークフローエンジン化 |
| 計画画面からの TODO 生成 | 実績アプリ未実装領域の詳細実績入力 UI |
| 完了時の実績入力画面への導線生成 | |
| 完了済み・キャンセル済みの表示切り替え | |
### TODO の位置づけ
```
計画(年間設計情報)
↓ 生成
TODO実際に動く作業単位
↓ 完了
実績(完了した事実)
```
---
## データモデル
### Todo本体
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
| id | bigint | ✓ | PK |
| year | integer | ✓ | 年度 |
| title | varchar(200) | ✓ | タイトル |
| description | text | | 説明 |
| status | enum | ✓ | `todo / doing / done / canceled` |
| priority | integer | ✓ | 小さいほど上位1000刻み |
| due_date | date | | 期日 |
| work_type | enum | ✓ | 作業種別(下記参照) |
| should_link_record | boolean | ✓ | 完了時に実績連携導線を有効にするか |
| completed_at | datetime | | 完了日時(差し戻し後も保持) |
| canceled_at | datetime | | キャンセル日時 |
| created_at | datetime | ✓ | |
| updated_at | datetime | ✓ | |
#### ステータス遷移
- `todo``doing``done`complete/ 専用エンドポイント経由)
- `done``todo / doing`差し戻し許可。completed_at は履歴として保持)
- `canceled` への遷移は任意のタイミングで可
#### 作業種別work_type
MVP で採用する種別:
| 値 | 意味 |
|---|---|
| `general` | 一般(どれにも当てはまらない作業) |
| `fertilization` | 施肥 |
| `rice_transplant` | 田植え |
| `delivery` | 運搬 |
| `levee_work` | 畔塗 |
将来追加(農薬散布管理アプリ実装時):
| 値 | 意味 |
|---|---|
| `pesticide` | 防除 |
#### 並び順
- 基本は FILO新規作成時は最上位へ
- 初回作成時:最上位 TODO の `priority - 1000` を割り当て
- 一覧表示:`priority` 昇順
- 並び替え後:表示対象全体を 1000, 2000, 3000... と再採番して保存
- 完了・キャンセル済みも `priority` を保持
### TodoTargetField対象圃場
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
| id | bigint | ✓ | PK |
| todo | FK(Todo) | ✓ | CASCADE |
| field | FK(fields.Field) | ✓ | PROTECT |
| field_name_snapshot | varchar(100) | ✓ | 保存時点の圃場名 |
| group_name_snapshot | varchar(50) | | 保存時点の group_name |
| created_at | datetime | ✓ | |
- `unique_together = ['todo', 'field']`
- 圃場グループは独立モデル化しない(`Field.group_name` を参照するのみ)
- 計画に含まれる圃場の一部だけを対象にすることを許可する
### TodoCrop分類補助作物
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
| id | bigint | ✓ | PK |
| todo | FK(Todo) | ✓ | CASCADE |
| crop | FK(plans.Crop) | ✓ | PROTECT |
| created_at | datetime | ✓ | |
- `unique_together = ['todo', 'crop']`
### TodoVariety分類補助品種
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
| id | bigint | ✓ | PK |
| todo | FK(Todo) | ✓ | CASCADE |
| variety | FK(plans.Variety) | ✓ | PROTECT |
| created_at | datetime | ✓ | |
- `unique_together = ['todo', 'variety']`
- 圃場が 0 件でも Crop / Variety だけの紐づけは許可(圃場未確定の準備作業など)
### TodoPlanLink計画との紐づけ
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
| id | bigint | ✓ | PK |
| todo | FK(Todo) | ✓ | CASCADE |
| plan_type | enum | ✓ | `fertilization / rice_transplant / delivery` |
| fertilization_plan | FK(fertilizer.FertilizationPlan) | | |
| rice_transplant_plan | FK(plans.RiceTransplantPlan) | | |
| delivery_plan | FK(分配計画モデル) | | |
| created_at | datetime | ✓ | |
- 1 行に 1 種別のリンクのみ保持
- `plan_type` に応じて対応 FK だけを埋める
- `levee_work` は MVP では「計画リンクなしで持てる work_type」として扱う
- 作付け計画Planは TODO 生成元としては対象外
### TodoCompletionLink完了時の実績連携索引
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
| id | bigint | ✓ | PK |
| todo | FK(Todo) | ✓ | |
| record_type | enum | ✓ | 実績種別 |
| work_record | FK(workrecords.WorkRecord) | | 共通索引 |
| spreading_session | FK(fertilizer.SpreadingSession) | | 施肥実績 |
| created_at | datetime | ✓ | |
- `todo` は OneToOne ではなく FK1 TODO から複数実績への分割を許容)
- 実績アプリが未実装の種別は空でよい
---
## API 仕様
### エンドポイント一覧
| メソッド | パス | 説明 |
|---|---|---|
| GET | `/api/todos/` | 一覧取得 |
| POST | `/api/todos/` | 作成 |
| GET | `/api/todos/{id}/` | 詳細取得 |
| PATCH | `/api/todos/{id}/` | 更新status=done への変更は不可) |
| DELETE | `/api/todos/{id}/` | 削除 |
| PATCH | `/api/todos/reorder/` | 並び替え |
| POST | `/api/todos/from-plan/` | 計画から TODO 生成 |
| POST | `/api/todos/{id}/complete/` | 完了処理(実績連携導線を返す) |
### 重要な設計ルール
**完了処理の一本化**
- `PATCH` での `status=done` 変更はバックエンドが拒否する
- 完了は必ず `POST /api/todos/{id}/complete/` を通る
- 理由実績連携導線の生成を確実にするため。AI 実装者がセッションをまたいで実装する際のブレを防ぐ
**差し戻し時の挙動**
- `done → todo/doing` は許可
- `TodoCompletionLink` が存在する場合は、差し戻しを許可しつつ API レスポンスに警告と各実績レコードへの直リンクを返す
- 実績レコード自体の削除は行わない(各実績アプリ側の責務)
**削除時の挙動**
- `TodoCompletionLink` が存在する TODO を削除しようとした場合、警告と各実績レコードへの直リンクを返す
- ユーザーが確認した上で削除を実行した場合は物理削除を許可する
- `TodoCompletionLink` は TODO と一緒に削除CASCADE
- 実績レコード自体は削除しない
### 一覧 GET `/api/todos/`
主なクエリパラメータ:
| パラメータ | デフォルト | 説明 |
|---|---|---|
| `status` | `todo,doing` | カンマ区切りで複数指定可 |
| `include_closed` | `false` | true で完了・キャンセルも含む |
| `work_type` | - | 作業種別フィルター |
| `due` | - | `overdue / today / upcoming` |
| `year` | - | 年度フィルター |
### 作成 POST `/api/todos/`
```json
{
"title": "西田エリアの追肥",
"description": "週内に先行実施",
"status": "todo",
"year": 2026,
"due_date": "2026-04-12",
"work_type": "fertilization",
"should_link_record": true,
"field_ids": [12, 18, 21],
"crop_ids": [1],
"variety_ids": [4],
"plan_links": [
{"plan_type": "fertilization", "plan_id": 8}
]
}
```
`plan_links` の変換API 入力は `plan_type + plan_id` の組で受け、Serializer で対応 FK へ変換する。
### 並び替え PATCH `/api/todos/reorder/`
```json
{
"items": [
{"id": 31, "priority": 1000},
{"id": 27, "priority": 2000},
{"id": 42, "priority": 3000}
]
}
```
### 計画から TODO 生成 POST `/api/todos/from-plan/`
```json
{
"plan_type": "fertilization",
"plan_id": 8,
"title": "2026春肥の散布",
"field_ids": [12, 18],
"due_date": "2026-04-15",
"should_link_record": true
}
```
- `field_ids` 未指定時は計画内の全圃場を初期対象にする
- `work_type``plan_type` から自動補完する
### 完了処理 POST `/api/todos/{id}/complete/`
- `status=done` にする
- `should_link_record=true` かつ対応実績アプリがある場合、関連画面へ遷移するための情報を返す
- MVP では実績レコードの自動生成は行わず、導線情報の返却にとどめる
---
## バリデーション
- `done` 遷移時に `completed_at` を自動設定
- `canceled` 遷移時に `canceled_at` を自動設定
- `PATCH``status=done` を指定した場合は 400 エラーを返す
- `field_ids` が計画外圃場を含む場合は `plan_links` が 1 件以上あるときのみエラーにする
- `should_link_record=true` でも対応実績アプリが無い場合は保存を許可する
- `TodoTargetField.field``PROTECT`(過去 TODO の対象圃場履歴を保全するため)
---
## UI 仕様
### 一覧画面 `/todos`
- デフォルト表示todo / doing を priority 昇順で表示
- 完了済み・キャンセル済みはフィルターで表示切り替え
- 期限超過は赤系で強調、当日期限も強調表示
- ドラッグ&ドロップで並び替え(難しければ矢印ボタンで代替)
表示カラム:タイトル / ステータス / 期日 / 作業種別 / 対象圃場数 / 紐づき計画
### 詳細画面 `/todos/{id}`
表示・編集:タイトル / 説明 / ステータス / 期日 / 作業種別 / 実績連携フラグ / 対象圃場 / 分類作物・品種 / 計画リンク
下部表示:実績連携先 / 完了日時 / 更新日時
### 作成導線
1. TODO 一覧から新規作成
2. 計画詳細または一覧から TODO 生成(施肥・田植え・運搬の各計画画面)
---
## 実装ファイル構成
### Backend
```
apps/todos/
├── models.py # Todo, TodoTargetField, TodoCrop, TodoVariety, TodoPlanLink, TodoCompletionLink
├── admin.py
├── serializers.py
├── views.py
├── urls.py
└── migrations/
```
- `keinasystem/settings.py``apps.todos` を追加
- `keinasystem/urls.py``/api/todos/` を追加
### Frontend
```
frontend/src/app/todos/
├── page.tsx # 一覧
├── [id]/page.tsx # 詳細
└── new/page.tsx # 作成
```
### 実装順
1. モデル・admin・migration
2. TODO CRUD API一覧・詳細・作成・更新・削除
3. TODO 一覧・詳細 UI
4. 並び替え API と UI
5. 計画から TODO 生成from-plan API + 各計画画面への導線)
6. 完了処理 API と実績連携導線 UI
---
## 実績連携の考え方
### 施肥
`施肥計画 → 施肥TODO → 施肥実績`SpreadingSessionの流れ。
完了時は `SpreadingSession` 作成画面への導線を返す。対象圃場は `TodoTargetField` を初期値として渡す。
### 田植え
田植え実績アプリは今後実装予定。MVP では:
- `rice_transplant` 種別の TODO を持てる
- 完了時は「完了済みだが実績アプリ未接続」の状態も許容する
- 将来の田植え実績導入時に `TodoCompletionLink` を拡張する
### 実績アプリが無い作業
`general` など、実績アプリに紐づかない TODO は `status=done` のみで完了とする。
---
## 未決定(実装時に判断)
以下は MVP 着手後に実装者が判断しながら決めてよい事項。
| 事項 | 方針 |
|---|---|
| 複数計画リンクの初回 UI | 内部構造は複数可。UI はまず 1 件中心で実装し、必要なら拡張する |
| 並び替え対象の範囲 | フィルター中todo/doing のみ)を再採番対象とするのが自然 |
| 施肥完了時に渡す初期値の粒度 | SpreadingSession 作成画面の実装時に具体的な受け渡し仕様を決める |