作成: 2026-04-24
最終更新: 2026-06-01
対象機能: 田植え実績(田植え作業の実施記録・苗箱実績の管理)
実装状況: MVP実装済み /#67方針で再設計中
対象 Issue:akira/keinasystem#42,akira/keinasystem#67,akira/keinasystem#73,akira/keinasystem#78
関連方針:docs/00_設計ポリシー_生産履歴中心設計.md
農業生産者が田植えを実施した際の作業実績を記録する機能。
田植え計画(RiceTransplantPlan)を参考にしつつ、「実際にいつ・どの圃場に・どの品種の苗を・何箱植えたか」を記録し、計画との差異を可視化する。
設計パターンは docs/15(トラクター作業)に準拠し、
「作業本体を専用テーブルで持ち、作業記録一覧には索引を自動生成する」方式を採用する。
田植え計画(RiceTransplantPlan) ← スタート地点
↓
農薬散布(SprayEvent) ← 種子消毒
↓
浸種(SeedSoaking) ← 自家育苗の場合。計画への FK あり
↓
籾蒔き(SeedSowing) ← 自家育苗の場合
↓
田植え実績(RiceTransplantSession) ← 本機能
育苗を委託した場合・苗を購入した場合は、SeedSoaking/SeedSowing が存在しない、または自家育苗と同等の情報が一部しか無い状態で田植え実績を記録する場合がある。
| IN(本機能で扱う) | OUT(本機能では扱わない) |
|---|---|
| 田植え実績の作成・編集・削除 | 育苗工程の管理 |
| 圃場ごとの実苗箱数の記録 | 田植え機の燃料・稼働記録 |
| 田植え計画との紐づけ | 作業者別工数の集計 |
| 計画箱数 vs 実績箱数の差異表示 | 写真添付 |
| 作業記録一覧(WorkRecord)への自動反映 | 田植え後の圃場状態管理 |
| 水稲作付け圃場の候補自動取得 | |
| 田植えと同時に行った施肥・農薬散布の一括入力 |
Issue #61 #64 #65 #67 の検討を踏まえ、本機能では以下を重視する。
緊急だったか ではなく、何の苗をどのほ場へいつ植えたか を記録するアプリ: apps/rice_transplant_records(新設)
テーブル名: rice_transplant_records_ricetransplantsession
| フィールド | 型 | 制約 | 説明 |
|---|---|---|---|
| id | int | PK | |
| year | int | required | 年度(date.year から自動設定) |
| date | DateField | required | 田植え実施日 |
| title | varchar(100) | required | 一覧表示タイトル。未指定時は「水稲田植え」を補完 |
| notes | text | blank | 備考 |
| created_at | datetime | auto | |
| updated_at | datetime | auto |
year + date の一意制約は付けない(同日複数セッション許容)SessionItem として展開するテーブル名: rice_transplant_records_ricetransplantsessionitem
| フィールド | 型 | 制約 | 説明 |
|---|---|---|---|
| id | int | PK | |
| session | FK(RiceTransplantSession) | CASCADE | 親の田植え実績 |
| field | FK(fields.Field) | PROTECT | 対象圃場 |
| plan | FK(plans.Plan) | SET_NULL, null=True | 保存時点の作付け計画参照(任意) |
| rice_transplant_entry | FK(plans.RiceTransplantEntry) | SET_NULL, null=True | 対応する田植え計画エントリ(計画指定時に自動設定) |
| actual_seedling_boxes | DecimalField(8,1) | required | 実際に使用した苗箱数 |
| crop_name_snapshot | varchar(100) | required | 保存時点の作物名 |
| variety_name_snapshot | varchar(100) | blank | 保存時点の品種名 |
| created_at | datetime | auto | |
| updated_at | datetime | auto |
unique_together = ['session', 'field']rice_transplant_entry は UI ブロックの田植え計画と field から自動特定して設定する。計画未指定時(計画なしブロック)は null。rice_transplant_entry を持つ(計画外ほ場を計画ブロックに追加した場合は計画を拡張して RiceTransplantEntry を自動作成するため)。rice_transplant_entry = null になるのは計画なしブロック由来の item のみ。planned_boxes は rice_transplant_entry.installed_seedling_boxes を返す。rice_transplant_entry が null の場合は planned_boxes も null。plan を null のまま保存できる。crop_name_snapshot / variety_name_snapshot は、明細 plan が null の場合でも当年 Plan から補完して保存する。購入苗フラグ のような単純な boolean では、生産履歴に必要な説明力が不足する。
そのため、今後は 苗バッチ または同等の追跡単位を導入し、少なくとも次を扱えるようにする。
取得できない項目は空欄のまま扱い、分からないこと自体を自然に表現できるようにする。
現行実装には session_type emergency_flow has_purchased_seedlings などの移行途上フィールドが存在するが、
本ドキュメントではそれらを最終形の中心構造とは見なさない。
必要な移行期間を経た上で、#67 方針に沿って段階的に整理する。
既存 apps/workrecords の WorkRecord にトラクター作業と同様の連携を追加する。
| 変更点 | 内容 |
|---|---|
work_type enum |
RICE_TRANSPLANT = 'rice_transplant' を追加 |
| FK | rice_transplant_session = OneToOneField('rice_transplant_records.RiceTransplantSession', ...) を追加 |
制約:
on_delete=CASCADEnull=True, blank=Truerelated_name='work_record'自動生成時の値:
| 項目 | 値 |
|---|---|
work_date |
session.date |
work_type |
rice_transplant |
title |
session.title |
year |
session.year |
auto_created |
True |
rice_transplant_session |
対応するセッション |
田植え実績画面で同時入力した施肥・農薬は、田植え実績の子データとして埋め込まず、
既存の施肥実績・農薬散布実績として独立保存したうえで、RiceTransplantSession と関連付ける。
| フィールド | 型 | 制約 | 説明 |
|---|---|---|---|
| rice_transplant_session | FK(rice_transplant_records.RiceTransplantSession) | null=True, SET_NULL | 同時入力元の田植え実績(任意) |
| フィールド | 型 | 制約 | 説明 |
|---|---|---|---|
| rice_transplant_session | FK(rice_transplant_records.RiceTransplantSession) | null=True, SET_NULL | 同時入力元の田植え実績(任意) |
SpreadingSession にまとめる。どのほ場にどの肥料を何袋散布したかは SpreadingSessionItem の明細として保持する。施肥計画の違いはデフォルト値取得やロック判定に使ってよいが、ユーザー向けの実績行分割理由にはしないSprayEvent にまとめる。使用した農薬ごとに SprayEventPesticide を作成し、ほ場別使用量は SprayEventFieldPesticideUsage として明細に持つ。同時農薬散布の usage_source は unknown(在庫連動なし)で保存する。在庫を連動させる場合は農薬散布専用画面から後で更新する候補圃場の抽出は、rice_transplant_plan(田植え計画)の指定有無で 2 モードに分かれる。
| モード | 条件 | 候補ルール |
|---|---|---|
| 計画選択時 | rice_transplant_plan あり |
計画の RiceTransplantEntry に含まれる圃場を初期選択状態で表示 |
| 計画未選択時 | rice_transplant_plan なし |
当年の Plan(crop.name="水稲")が存在する圃場を全件表示 |
Plan であることcrop.name = "水稲" の圃場であること| 項目 | 説明 |
|---|---|
| field_id | 圃場ID |
| field_name | 圃場名 |
| field_area_tan | 面積(反) |
| group_name | グループ名 |
| plan_id | 対応する作付け計画ID |
| crop_name | 作物名 |
| variety_name | 品種名 |
| planned_boxes | 田植え計画の苗箱数(計画と紐づいている場合) |
| is_transplanted | 当年の田植え実績が既に存在する圃場かどうか |
| selected | 初期選択状態 |
すべて JWT 認証(Authorization: Bearer <token>)が必要。
| メソッド | URL | 説明 |
|---|---|---|
| GET | /api/rice-transplant-records/sessions/?year={year} |
年度別一覧 |
| POST | /api/rice-transplant-records/sessions/ |
新規作成(同時施肥・同時農薬散布を含めて保存可) |
| GET | /api/rice-transplant-records/sessions/{id}/ |
詳細取得 |
| PUT/PATCH | /api/rice-transplant-records/sessions/{id}/ |
更新(新規同時入力分の施肥・農薬も再編集可) |
| DELETE | /api/rice-transplant-records/sessions/{id}/ |
削除 |
| メソッド | URL | 説明 |
|---|---|---|
| GET | /api/rice-transplant-records/candidates/?year={year} |
水稲作付け圃場候補 |
| GET | /api/rice-transplant-records/candidates/?year={year}&plan_id={id} |
計画指定時は計画圃場を優先 |
blocks形式が正式 API。従来のitemsフラット形式は廃止し、既存入力画面も移行する。
{
"date": "2026-05-15",
"title": "2026年 田植え 第1回",
"notes": "",
"blocks": [
{
"rice_transplant_plan": 1,
"items": [
{ "field": 5, "actual_seedling_boxes": 12.0, "seedling_batches": [] },
{ "field": 6, "actual_seedling_boxes": 9.0, "seedling_batches": [] }
],
"spreading_items": [
{
"field_id": 5,
"fertilizer_id": 1,
"actual_bags": "1.5",
"planned_bags_snapshot": "2.0",
"delivered_bags_snapshot": "2.0"
}
],
"spray_pesticides": [
{
"pesticide": 3,
"applied_limit": 1,
"amount_used": "5袋",
"field_usages": [
{ "field": 5, "amount_used": "3袋" },
{ "field": 6, "amount_used": "2袋" }
]
}
]
}
]
}
RiceTransplantSession / SpreadingSession / SprayEvent を独立実績として作成する{
"id": 5,
"year": 2026,
"date": "2026-05-15",
"title": "2026年 にこまる 田植え 第1回",
"rice_transplant_plan": 1,
"rice_transplant_plan_name": "2026年度 にこまる種もみ 田植え計画",
"notes": "天候良好",
"work_record_id": 42,
"item_count": 2,
"total_area_tan": "2.0000",
"total_actual_boxes": "27.5",
"total_planned_boxes": "28.2",
"blocks": [
{
"block_key": "plan:1",
"block_label": "2026年度 にこまる種もみ 田植え計画",
"rice_transplant_plan": 1,
"item_count": 2,
"total_area_tan": "2.0000",
"total_actual_boxes": "27.5",
"total_planned_boxes": "28.2"
}
],
"items": [
{
"id": 20,
"field": 5,
"field_name": "田中上",
"field_area_tan": "1.2000",
"plan": 12,
"actual_seedling_boxes": "14.0",
"planned_boxes": "14.4",
"crop_name_snapshot": "水稲",
"variety_name_snapshot": "にこまる"
}
],
"created_at": "2026-05-15T08:00:00Z",
"updated_at": "2026-05-15T08:00:00Z"
}
/rice-transplant/records)表示項目: 作業日 / タイトル / 紐づく計画 / 圃場数 / 苗箱数合計(実績 vs 計画) / 備考
補助表示:
行アクション: 編集 / 削除
田植え計画はセッション共通で選ぶのではなく、ブロック単位で選ぶ(後述)。
1日の田植え作業が複数の田植え計画にまたがることがあるため、田植え計画単位のブロックを縦に並べる構造をとる。
ブロックの追加方法:
制約:
installed_seedling_boxes)は入力した実績箱数と同じ値で設定する#78)items.field に対応する field_area_tan の単純合算とするitems の field_area_tan を合算した値とするブロックのヘッダー部で施肥・農薬・デフォルト苗由来を設定する。
RiceTransplantPlan.variety)に紐づく当年度の播種記録(SeedSowing)を候補として絞り込む。1件だけ一致する場合は自動的にデフォルトとしてセットする圃場名セルは2行化(横幅節約):
列の並び順(左 → 右):
| 列 | 内容 | 備考 |
|---|---|---|
| 圃場名 / 面積・グループ | 上述の2行形式 | 横スクロール時も左固定(position: sticky; left: 0) |
| 実績箱数 | 必須入力 | 最重要のため最左に配置 |
| 農薬列(選択した農薬ごと) | ほ場ごとの使用量入力 | ほぼ毎回発生するため施肥より左 |
| 施肥列(選択した肥料ごと) | ほ場ごとの袋数入力 | 対象外ほ場もあるため右寄り |
| 苗由来 | ブロックデフォルトを引き継ぎ、行単位で上書き可能 | 補助情報のため最右(横スクロールで確認可) |
施肥・農薬の各入力セルは、計画から取得した参照値をグレーで薄く表示し、その値を入力欄の初期値としてセットする(施肥計画編集画面と同じパターン)。
参照値(グレー・小) [ 入力値(白枠) ]
1.5 [ 1.5 ]
新規同時入力分を再表示する際、DB にはブロックが保存されていないため、バックエンドが以下のルールで仮想ブロックを再構成してレスポンスを返す。
Step 1: SessionItem からブロックを再構成する
RiceTransplantSessionItem.rice_transplant_entry.rice_transplant_plan が同じ item を同一ブロックとしてグループ化するrice_transplant_entry が null の item は「計画なし」ブロックとしてまとめるrice_transplant_entry を持つ(計画外ほ場は計画拡張により RiceTransplantEntry が作成済みのため)。rice_transplant_entry = null の item が計画ブロックに混在することはないStep 2: SpreadingSession の明細を各ブロックへ再配分する
SpreadingSession は rice_transplant_session FK でセッション全体と紐付く(ブロックとは紐付かない)SpreadingSessionItem には field_id と fertilizer_id があるSpreadingSessionItem.field_id が属するブロックに、その明細を割り当てるStep 3: SprayEvent の明細を各ブロックへ再配分する
SprayEvent も rice_transplant_session FK でセッション全体と紐付くSprayEventFieldPesticideUsage には field があるfield が属するブロックに、その使用量明細を割り当てる田植え実績作成
[日付 2026-05-15 ]
[タイトル 2026年 田植え 第1回 ]
[備考 ________________ ]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ブロック① 田植え計画A(にこまる 第1回) [✕ 削除]
面積合計: 2.0反
施肥: [BB555 ▼] [+追加]
農薬: [箱剤A ▼] 適用作物: [稲 / 2回 ▼] [+追加]
┌──────────────────┬──────┬───────┬──────┬──────┐
│ 圃場名 │実績箱│ 箱剤A │BB555 │苗由来│
│ 面積(反)・グループ│ │ │ │ │
├──────────────────┼──────┼───────┼──────┼──────┤
│ ほ場1 1.2反 │[12] │ [3袋] │[1.5] │ 自家 │
│ 田中・田中GRP │ │ 1.5 │ 1.5 │ │ ← グレーが計画参照値
├──────────────────┼──────┼───────┼──────┼──────┤
│ ほ場2 0.8反 │ [9] │ [2袋] │[1.0] │ 自家 │
│ 田中・田中GRP │ │ │ 1.0 │ │
└──────────────────┴──────┴───────┴──────┴──────┘
[+ このブロックにほ場を追加]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ブロック② 田植え計画B(ひのひかり 第1回) [✕ 削除]
面積合計: 1.5反
施肥: [電気炉さい ▼] [+追加]
農薬: なし [+追加]
┌──────────────────┬──────┬──────────┬──────┐
│ 圃場名 │実績箱│ 電気炉さい│苗由来│
│ 面積(反)・グループ│ │ │ │
├──────────────────┼──────┼──────────┼──────┤
│ ほ場3 1.5反 │[18] │ [1.5] │ 自家 │
│ 北・北GRP │ │ 1.5 │ │
└──────────────────┴──────┴──────────┴──────┘
[+ このブロックにほ場を追加]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ブロック③ 計画なし [✕ 削除]
面積合計: 0.0反
施肥: なし [+追加]
農薬: なし [+追加]
┌──────────────────┬──────┬──────┐
│ 圃場名 │実績箱│苗由来│
│ 面積(反)・グループ│ │ │
└──────────────────┴──────┴──────┘
[+ このブロックにほ場を追加]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
田植え計画を選択: [計画を選択... ▼] ← 選択した瞬間にブロック追加
[+ 計画なしブロックを追加]
全体面積合計: 3.5反
[保存]
datetitle(未入力時はサーバーが「水稲田植え」を補完)blocks(リクエスト上の入力グループ、1件以上)block 内に items(1件以上)year は date.year と一致させるrice_transplant_plan を指定した場合、rice_transplant_plan.year と session.year が一致することfield に対応する RiceTransplantEntry が存在しない場合: 新規 RiceTransplantEntry を作成して計画に追加する(計画拡張)。計画箱数は入力した実績箱数と同じ値で設定するRiceTransplantEntry として存在する場合: UI 上で警告を表示してユーザー確認を得る。確認後は元の計画から RiceTransplantEntry を削除し、新しい計画に追加する(サーバー側でも競合チェックを行い、確認フラグなしのリクエストはエラーを返す)pesticide_id)を複数ブロックで使用する場合、applied_limit(適用回数枠)は全ブロックで同じ値でなければならない。ブロックをまたいで同じ農薬に異なる applied_limit を設定した場合は保存時エラーとなる(同一 SprayEvent に統合されるため適用回数枠を1つに定める必要があるため)plan を null のまま保存可RiceTransplantSession と RiceTransplantSessionItem(全ブロック分フラット)を一括作成する。計画ブロックに計画外ほ場が含まれている場合は、事前に RiceTransplantEntry を作成して計画を拡張する(計画箱数 = 実績箱数で設定)SpreadingSession / SprayEvent も独立実績として作成し、rice_transplant_session で関連付けるWorkRecord を自動生成する(update_or_create)RiceTransplantSessionItem を全置換する。WorkRecord の日付・タイトルも同期更新する。同時入力分の SpreadingSession / SprayEvent は いったん削除して再作成する(SET_NULL ではなく DELETE)。これにより、同じセッションを編集するたびに旧実績が孤立して残る問題を防ぐ。削除された SpreadingSession / SprayEvent は独立した実績としては残らないCASCADE で SessionItem を削除。WorkRecord も on_delete=CASCADE で自動削除。紐付いた SpreadingSession / SprayEvent は SET_NULL で独立した正式実績として残す(DELETE しない)田植え実績と田植え計画を対比して表示することで、苗の余剰・不足を可視化する。
| 項目 | 説明 |
|---|---|
| 計画箱数 | RiceTransplantEntry.installed_seedling_boxes |
| 実績箱数 | RiceTransplantSessionItem.actual_seedling_boxes(複数セッション合算) |
| 差異 | 実績 − 計画(マイナスは不足、プラスは余剰) |
RiceTransplantSessionItem.rice_transplant_entry → RiceTransplantEntry.rice_transplant_plan の順に辿るIssue #42 のスコープとして、nursery.SeedSoaking に以下のフィールドを追加する。
rice_transplant_plan = models.ForeignKey(
'plans.RiceTransplantPlan',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='seed_soakings',
verbose_name='田植え計画',
)
docs/20_マスタードキュメント_浸種籾蒔き管理編.md を参照| 種別 | パス |
|---|---|
| モデル | backend/apps/rice_transplant_records/models.py |
| シリアライザ | backend/apps/rice_transplant_records/serializers.py |
| ViewSet | backend/apps/rice_transplant_records/views.py |
| URL | backend/apps/rice_transplant_records/urls.py |
| 作業記録連携 | backend/apps/workrecords/models.py(work_type・FK追加) |
| 作業記録連携 | backend/apps/workrecords/services.py(WorkRecord 自動生成ロジック追加) |
| 作業記録連携 | backend/apps/workrecords/serializers.py(rice_transplant_session の select_related 対応) |
| 作業記録連携 | backend/apps/workrecords/views.py(work_type フィルタへの rice_transplant 追加) |
| 浸種FK追加 | backend/apps/nursery/models.py(SeedSoaking.rice_transplant_plan) |
| マイグレーション | backend/apps/rice_transplant_records/migrations/ |
| 一覧・編集画面 | frontend/src/app/transplant-records/page.tsx(URL要確定) |
| ナビゲーション | frontend/src/components/Navbar.tsx |
| 型定義 | frontend/src/lib/types.ts |
/rice-transplant/records に確定