# マスタードキュメント:TODO管理機能 > **作成**: 2026-04-10 > **最終更新**: 2026-04-10 > **対象機能**: TODO管理(作業指示・優先順位管理・実績連携導線) > **実装状況**: 設計完了・実装前 > **対象 Issue**: `akira/keinasystem#17` --- ## 概要 繁忙期に「どれから手を付けるか」を管理するための TODO 機能。 計画(施肥・田植え・運搬など)と実績の間に位置する「作業指示」レイヤー。 ### 機能スコープ(IN / OUT) | IN(MVP対象) | 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 ではなく FK(1 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 作成画面の実装時に具体的な受け渡し仕様を決める |