Files
keinasystem/document/15_マスタードキュメント_トラクター作業編.md
akira cc6823b071 docs: levee_work をトラクター作業(tractor_work)に再設計
- doc/15 を畔塗作業編からトラクター作業編に改訂
  荒代掻き・植代掻き・耕耘を追加、TractorWorkSession モデル導入
- doc/19 TODO管理編: work_type の levee_work → tractor_work 置換、
  work_subtype フィールド追加、TodoCompletionLink に tractor_work 追記
- Issue #21(代掻き実績登録)の仕様策定に対応
2026-04-10 13:39:47 +09:00

412 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.
# マスタードキュメント:トラクター作業記録機能
> **作成**: 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` — 遷移先更新