# マスタードキュメント:トラクター作業記録機能 > **作成**: 2026-04-04 > **最終更新**: 2026-04-10 > **対象機能**: トラクター作業記録(畔塗・荒代掻き・植代掻き・耕耘) > **実装状況**: 畔塗のみ実装済み。荒代掻き・植代掻き・耕耘は設計中(Issue #21) > **対象 Issue**: `akira/keinasystem#21` --- ## 概要 農業生産者が、水稲作付け圃場に対して実施したトラクター作業を日付単位で記録する機能。 対象圃場をまとめて選択し、保存時に作業記録一覧へ自動反映する。 対象作業種別: | 種別 | 日本語名 | 説明 | |---|---|---| | `levee_work` | 畔塗 | 畦畔の補修・造成 | | `rough_harrowing` | 荒代掻き | 田植え前の粗い代掻き | | `transplant_harrowing` | 植代掻き | 田植え直前の仕上げ代掻き | | `cultivation` | 耕耘 | 土起こし・耕起 | これらはいずれも**トラクターを用いた資材なし作業**であり、同一のデータモデルで管理する。 本機能は、施肥計画の散布実績と同様に 「作業本体を専用テーブルで持ち、作業記録一覧には索引を自動生成する」 という設計方針を採用する。 ### 機能スコープ(IN / OUT) | IN(本機能で扱う) | OUT(本機能では扱わない) | |---|---| | 日付単位の作業記録作成(全4種別) | 作業の工程管理 | | 水稲作付け圃場の候補抽出 | 作業者別の工数集計 | | 複数圃場の一括選択・保存 | 機械・資材の在庫管理 | | 作業記録一覧(WorkRecord)への自動反映 | 写真添付 | | 記録の編集・削除 | GPS軌跡連携 | | 対象圃場一覧の参照画面 | 汎用作業日誌への完全統合 | --- ## 背景と目的 現状システムには畔塗の記録機能があるが、同じトラクター作業である荒代掻き・植代掻き・耕耘は登録できない。 これらは以下の共通点を持つため、統一モデルで管理する。 - 1日で複数圃場をまとめて実施することが多い - 対象圃場は当年の作付け計画と密接に関係する - 後から「いつ、どの圃場を実施したか」を一覧で見返したい - 使用資材がない(施肥・農薬とは区別される) --- ## データモデル ### TractorWorkSession(トラクター作業記録本体) 日付単位のトラクター作業記録。 | フィールド | 型 | 制約 | 説明 | |---|---|---|---| | id | int | PK | | | work_type | varchar(30) | required | 作業種別(下記参照) | | year | int | required | 年度フィルタ用。原則 `date.year` と一致させる | | date | DateField | required | 作業日 | | title | varchar(100) | required | 一覧表示タイトル。未指定時はサーバー側で work_type に応じたデフォルト値を補完する | | notes | text | blank | 備考 | | created_at | datetime | auto | | | updated_at | datetime | auto | | #### work_type の値とデフォルトタイトル | work_type | 表示名 | デフォルトタイトル | |---|---|---| | `levee_work` | 畔塗 | 水稲畔塗 | | `rough_harrowing` | 荒代掻き | 水稲荒代掻き | | `transplant_harrowing` | 植代掻き | 水稲植代掻き | | `cultivation` | 耕耘 | 水稲耕耘 | - `year + date` の一意制約は付けない - 同日に種別違い・地区違いで複数記録を持てるようにする ### TractorWorkSessionItem(対象圃場明細) トラクター作業記録に紐づく対象圃場一覧。 | フィールド | 型 | 制約 | 説明 | |---|---|---|---| | id | int | PK | | | session | FK(TractorWorkSession) | CASCADE | 親の作業記録 | | field | FK(fields.Field) | PROTECT | 対象圃場 | | plan | FK(plans.Plan) | SET_NULL, nullable | 保存時点の作付け計画参照 | | crop_name_snapshot | varchar(100) | required | 保存時点の作物名 | | variety_name_snapshot | varchar(100) | blank | 保存時点の品種名 | | created_at | datetime | auto | | | updated_at | datetime | auto | | - `unique_together = ['session', 'field']` - 圃場名は `Field` を参照して表示する - 作物・品種は履歴保全のためスナップショット保持 ### WorkRecord(作業記録索引) 既存 `apps/workrecords` の `WorkRecord` をトラクター作業に対応させる。 | 変更点 | 内容 | |---|---| | `work_type` enum | `TRACTOR_WORK = 'tractor_work'` を追加(`LEVEE_WORK` を置換) | | FK | `tractor_work_session = OneToOneField('tractor_work.TractorWorkSession', ...)` に改名 | 制約: - `on_delete=CASCADE` - `null=True`, `blank=True` - `related_name='work_record'` 一覧表示時の想定値: | 項目 | 値 | |---|---| | 作業日 | 作業記録の日付 | | 種別 | トラクター作業(work_type の日本語表示) | | タイトル | session.title | | 参照先 | 対象圃場一覧画面 | --- ## 候補圃場抽出ルール 候補は作付け計画 `Plan` から抽出する。 ### 基本条件 - 指定年度の `Plan` であること - `crop.name = "水稲"` の圃場であること ### 補足 - 品種未設定でも `crop=水稲` なら候補に含める - 並び順は `field.display_order`, `field.id` ### 候補レスポンスで返す情報 | 項目 | 説明 | |---|---| | field_id | 圃場ID | | field_name | 圃場名 | | field_area_tan | 面積(反) | | group_name | グループ名 | | plan_id | 対応する作付け計画ID | | crop_name | 作物名 | | variety_name | 品種名 | | selected | 初期選択状態(原則 `true`) | --- ## 画面仕様 ### 画面の位置づけ 日付と作業種別を先に決めて対象圃場を選ぶ「日報型UI」。 1回の保存で複数圃場をまとめて記録する。 ### 主要画面 #### 1. トラクター作業記録一覧画面(`/tractor-work`) - 年度内の記録を一覧する - 作業種別でフィルター可能 - 新規作成・既存記録の編集・削除 表示項目: 作業日 / 作業種別 / タイトル / 対象圃場数 / 面積合計 / 備考 #### 2. 作成・編集画面 入力項目: - **作業種別**(畔塗 / 荒代掻き / 植代掻き / 耕耘)← 新規追加 - 日付 - タイトル(work_type に連動したデフォルト値を自動セット) - 備考 - 対象圃場一覧(チェックボックス) ### 推奨UIイメージ ```text トラクター作業記録作成 [作業種別 荒代掻き ▼] [日付 2026-04-20] [タイトル 水稲荒代掻き] [備考 __________________ ] 対象圃場一覧 [全選択] [全解除] ☑ 田中上 1.2反 上エリア コシヒカリ ☑ 田中下 0.8反 上エリア あきたこまち ☐ 山の前 1.5反 南エリア (未設定) [保存] ``` --- ## API エンドポイント すべて JWT 認証必須。 ### トラクター作業記録 | メソッド | URL | 説明 | |---|---|---| | GET | `/api/tractor-work/sessions/?year={year}` | 年度別一覧 | | POST | `/api/tractor-work/sessions/` | 新規作成 | | GET | `/api/tractor-work/sessions/{id}/` | 詳細取得 | | PUT/PATCH | `/api/tractor-work/sessions/{id}/` | 更新 | | DELETE | `/api/tractor-work/sessions/{id}/` | 削除 | ### 候補圃場取得 | メソッド | URL | 説明 | |---|---|---| | GET | `/api/tractor-work/candidates/?year={year}` | 水稲作付け圃場候補を返す | ### リクエスト例(新規作成) ```json { "work_type": "rough_harrowing", "year": 2026, "date": "2026-04-20", "title": "水稲荒代掻き", "notes": "", "items": [ { "field": 5, "plan": 12 }, { "field": 6, "plan": 13 } ] } ``` - `crop_name_snapshot` / `variety_name_snapshot` はクライアント送信不要。サーバーが `plan` から自動設定する - `plan` が `null` の場合は `field` に対応する当年 `Plan` から補完を試みる ### レスポンス例(詳細) ```json { "id": 3, "work_type": "rough_harrowing", "year": 2026, "date": "2026-04-20", "title": "水稲荒代掻き", "notes": "", "work_record_id": 15, "item_count": 2, "total_area_tan": "2.0000", "items": [ { "id": 11, "field": 5, "field_name": "田中上", "plan": 12, "crop_name_snapshot": "水稲", "variety_name_snapshot": "コシヒカリ" } ], "created_at": "2026-04-20T08:00:00Z", "updated_at": "2026-04-20T08:00:00Z" } ``` --- ## 業務フロー ### 1. 新規作成 1. ユーザーが作業種別・年度・日付を選ぶ 2. システムが当年の水稲作付け圃場を候補表示する 3. ユーザーが対象圃場を選択する 4. 保存時に `TractorWorkSession` を作成する 5. 明細として `TractorWorkSessionItem` を一括作成する 6. 各明細の `crop_name_snapshot` / `variety_name_snapshot` をサーバー側で自動設定する 7. `WorkRecord` を自動生成する(`update_or_create`) ### 2. 編集 1. ユーザーが既存の作業記録を開く 2. 作業種別・日付・タイトル・備考・対象圃場を変更する 3. 保存時に明細を再構成する 4. `WorkRecord` 側の作業日・タイトルも同期更新する ### 3. 削除 1. ユーザーが作業記録を削除する 2. 紐づく `TractorWorkSessionItem` は `CASCADE` で削除される 3. 紐づく `WorkRecord` は `tractor_work_session` の `on_delete=CASCADE` により削除される --- ## 作業記録連携仕様 ### 追加する種別 | enum値 | 表示名 | |---|---| | `tractor_work` | トラクター作業 | ### 自動生成ルール - `work_date` = `session.date` - `work_type` = `tractor_work` - `title` = `session.title`(work_type 別デフォルトで補完済み) - `year` = `session.year` - `auto_created` = `True` - `tractor_work_session` = 対応する作業記録 ### 同期タイミング - 作成時・更新時: `update_or_create` - 削除時: `on_delete=CASCADE` により自動削除 --- ## バリデーションルール ### 必須 - `work_type` - `year` - `date` - `items`(1件以上) ### 保存時チェック - 選択圃場が0件の保存を禁止する - 同一セッション内で同じ圃場を重複登録しない - `year` は原則 `date.year` と一致しなければならない - `plan` が指定されている場合、`plan.field` と `field` は一致しなければならない - `plan.year` は `session.year` と一致しなければならない ### 業務上の許容 - 品種未設定の水稲圃場は保存可 - 同日に別種別・別地区で複数記録を持てる - 一度作業した圃場を別日に再度記録することは可 --- ## 実装方針 ### 移行方針(levee_work → tractor_work) 既存 `apps/levee_work` を `apps/tractor_work` にアプリごと改名する。 - Django の `RenameModel` migration でテーブルを改名する - `work_type` フィールドを追加し、既存レコードは `levee_work` で埋める - `workrecords` の FK名・enum値も migration で更新する - API パスを `/levee-work/` → `/tractor-work/` に変更する - フロントエンドの `app/levee-work/` → `app/tractor-work/` に移動する ### バックエンド - `Session` / `SessionItem` 構成を維持する - Serializer は `read` と `write` を分離する - 候補取得 API は `Plan` を起点に組み立てる - `sync_tractor_work_record(session)` で `WorkRecord` と同期する ### フロントエンド - 既存の levee-work ページを tractor-work に移植する - 作業種別セレクタを追加し、選択に応じてデフォルトタイトルを自動セットする --- ## ソースファイル構成 ### バックエンド ``` backend/apps/tractor_work/ ├── models.py # TractorWorkSession, TractorWorkSessionItem ├── serializers.py ├── views.py ├── urls.py ├── admin.py └── migrations/ ├── 0001_initial.py # (levee_work から移行) └── 0002_rename_and_add_work_type.py ``` 変更ファイル: - `backend/apps/workrecords/models.py` — FK名・enum更新 - `backend/apps/workrecords/services.py` — sync関数改名 - `backend/keinasystem/settings.py` — INSTALLED_APPS更新 - `backend/keinasystem/urls.py` — URLパス更新 ### フロントエンド ``` frontend/src/app/tractor-work/ └── page.tsx ``` 変更ファイル: - `frontend/src/types/index.ts` — 型定義更新 - `frontend/src/components/Navbar.tsx` — リンク更新 - `frontend/src/app/workrecords/page.tsx` — 遷移先更新