- doc/15 を畔塗作業編からトラクター作業編に改訂 荒代掻き・植代掻き・耕耘を追加、TractorWorkSession モデル導入 - doc/19 TODO管理編: work_type の levee_work → tractor_work 置換、 work_subtype フィールド追加、TodoCompletionLink に tractor_work 追記 - Issue #21(代掻き実績登録)の仕様策定に対応
412 lines
12 KiB
Markdown
412 lines
12 KiB
Markdown
# マスタードキュメント:トラクター作業記録機能
|
||
|
||
> **作成**: 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` — 遷移先更新
|