Files
keinasystem/改善案/施肥散布実績連携変更実装仕様.md
Akira 865d53ed9a 仕様書に追記しました。更新先は 施肥散布実績連携変更実装仕様.md です。
今回追加した内容は主にこの5点です。

FertilizationEntry.actual_bags の追加
SpreadingSessionItem 保存・更新・削除時の actual_bags 再集計ルール
copy_from_previous_year で actual_bags があれば次年度 bags 初期値に使う方針
施肥計画一覧・編集画面での 計画値 / 実績値 併記
RESERVE = bags、USE = actual_bags の併存整理
受け入れ条件にも、

actual_bags が再集計されること
計画値と実績値の両方が見えること
前年度コピーで actual_bags を使えること
を追加しています。
今回は仕様書更新のみで、コード変更やテストはしていません。必要なら次に、この内容をマスタードキュメント側へ反映します
2026-03-17 17:24:25 +09:00

18 KiB
Raw Permalink Blame History

施肥散布実績連携変更実装仕様

作成日: 2026-03-17 改訂日: 2026-03-17 対象プロジェクト: keinasystem_t02 位置づけ: 実運用反映版・MVP仕様


1. 結論

今回の変更では、施肥データの流れを以下の一気通貫に揃える。

  1. 施肥計画を作る
  2. 施肥計画を元に運搬計画を作る
  3. 運搬済み肥料を元に散布実績を記録する
  4. 散布実績を元に作業記録として参照できる
  5. 将来、必要な相手先提出資料へ変換できる

MVPの方針は以下とする。

  • 施肥計画の 散布確定 ボタンは廃止する
  • 散布実績は SpreadingSessionSpreadingSessionItem で管理する
  • SpreadingAllocation は作らない
  • 在庫の USE は散布実績保存時に発生させる
  • WorkRecord は作るが、明細の二重管理はしない
  • WorkRecordDeliveryTripSpreadingSession への索引として使う
  • FertilizationEntry.bags は計画値として維持し、actual_bags を実績集計値として別保持する
  • 客先提出PDFは今回実装しない
  • ただし、将来どの様式にも変換できるよう、元データを引き出せる構造にする

2. 背景

現状実装では、FertilizationPlan に「散布確定」ボタンがあり、施肥計画単位で一括確定する流れになっている。

しかし実運用では、実際に必要なのは以下である。

  • どの圃場に
  • どの肥料を
  • どれだけ散布したか
  • それがいつ行われたか

また、データは以下のように流用できる必要がある。

  • 施肥計画作成
  • 運搬計画作成
  • 散布実績記録
  • 作業記録参照
  • 相手先提出資料への転用

この流れが切れると、従来どおり別DBや別表へ転記が必要になり、システム化の価値が大きく下がる。


3. 今回の判断

3.1 採用するもの

  • SpreadingSessionSpreadingSessionItem
  • WorkRecord の新設
  • 散布保存時の在庫 USE 登録
  • FertilizationEntry.actual_bags の集計反映
  • 施肥計画一覧からの 散布確定 UI 廃止
  • 施肥計画進捗の自動集計表示

3.2 今回は見送るもの

  • SpreadingAllocation
  • DeliveryTripItem.fertilization_entry FK
  • 客先提出PDFの固定様式実装

3.3 理由

  • SpreadingAllocation が必要になる運用は今後も発生しない前提である
  • WorkRecord は必要だが、詳細を二重保持するのではなく索引で十分である
  • 在庫は実際に減るため、散布実績と同時に管理すべきである
  • 提出資料は相手先ごとに違うため、今固定様式を作るより、元データ抽出可能性を優先すべきである
  • 運搬計画は年度ベースで複数施肥計画を横断するため、DeliveryTripItem に単一の fertilization_entry FK を持たせるのは不安がある

4. 機能スコープ

4.1 IN

  • 施肥計画の 散布確定 ボタン廃止
  • 散布実績モデル追加
  • 作業記録索引モデル追加
  • 在庫 USE 連携
  • FertilizationEntry.actual_bags 集計
  • 散布実績一覧・作成・更新・削除 API
  • 散布候補集計 API
  • 施肥計画進捗表示
  • 前年度コピー時の actual_bags → bags 初期化反映
  • 作業記録一覧から運搬・散布の実績を参照可能にすること

4.2 OUT

  • 運搬便ごとの散布充当追跡
  • 相手先ごとのPDF様式実装
  • 残肥返却・再入庫管理
  • カレンダーUIの完成版
  • 栽培管理全体を包含した汎用作業日誌の完成版

5. 新しい業務フロー

5.1 施肥計画

  1. 施肥計画を作成する
  2. 計画値として保持する
  3. この段階では散布確定しない

5.2 運搬計画

  1. 施肥計画を元に運搬計画を作成する
  2. DeliveryTrip.date に運搬日を入れる
  3. 日付が入った運搬回を 運搬済み とみなす
  4. 運搬日が保存されたら WorkRecord を自動生成または更新する

5.3 散布実績

  1. ユーザーは散布日を選ぶ
  2. システムはその年度の 運搬済み データを集計する
  3. 未散布残 がある圃場×肥料を候補として表示する
  4. ユーザーは全部または一部の圃場を選ぶ
  5. 実際に散布した袋数を入力して保存する
  6. 保存時に在庫 USE を作成する
  7. 保存時に WorkRecord を自動生成または更新する

6. データモデル

6.1 FertilizationPlan

既存の is_confirmed / confirmed_at は中心機能ではなくなる。

方針

  • DBカラムは当面残す
  • 新UIでは confirm_spreading / unconfirm を使わない
  • 一覧では散布進捗を計算表示する

追加する表示用項目

  • spread_status: unspread | partial | completed | over_applied
  • planned_total_bags
  • spread_total_bags
  • remaining_total_bags

FertilizationEntry.actual_bags の追加

FertilizationEntry に以下のフィールドを追加する。

  • actual_bags = DecimalField(max_digits=10, decimal_places=4, null=True, blank=True)

役割

  • bags: 計画値
  • actual_bags: 散布実績の集計値

bags はユーザーが立てた計画として保持し、散布後も自動で上書きしない。 実績は actual_bags に集約する。

actual_bags の更新契機

  • SpreadingSessionItem 作成時
  • SpreadingSessionItem 更新時
  • SpreadingSessionItem 削除時

上記のたびに、同一年度・圃場・肥料の散布実績合計を再集計して反映する。

6.2 DeliveryTrip / DeliveryTripItem

MVPではモデル追加は行わない。

前提

  • DeliveryTrip.date != null の明細のみを 運搬済み とみなす
  • DeliveryTripItem は今までどおり field + fertilizer + bags を保持する
  • fertilization_entry FK は追加しない

6.3 SpreadingSession(新規)

散布日の親レコード。

フィールド 制約 説明
id int PK
year int required 年度フィルタ用
date DateField required 散布日
name varchar(100) blank 任意名
notes text blank 備考
created_at / updated_at datetime auto

制約

  • year + date の一意制約は付けない

同日に午前・午後やエリア別で複数散布記録を分けられるようにする。

6.4 SpreadingSessionItem(新規)

散布日の圃場×肥料ごとの実績。

フィールド 制約 説明
id int PK
session FK(SpreadingSession) CASCADE
field FK(fields.Field) PROTECT
fertilizer FK(Fertilizer) PROTECT
actual_bags Decimal(10,4) required 実散布袋数
planned_bags_snapshot Decimal(10,4) required 表示時点の計画値
delivered_bags_snapshot Decimal(10,4) required 表示時点の運搬済み合計
created_at / updated_at datetime auto

制約

  • unique_together = [['session', 'field', 'fertilizer']]

補足

  • fertilization_entry FK は持たない
  • 圃場+肥料単位の事実記録を優先する

6.5 WorkRecord(新規)

作業記録参照用の索引テーブル。

目的

  • 日付順に作業を一覧できるようにする
  • 将来、播種・農薬散布・収穫なども同じ入口で見られるようにする
  • ただし、詳細の本体は各業務テーブル側に持つ
フィールド 制約 説明
id int PK
work_date DateField required 作業日
work_type CharField required fertilizer_delivery / fertilizer_spreading
title varchar(200) required 一覧表示名
year int required 年度フィルタ補助
auto_created bool default=True 自動生成フラグ
delivery_trip OneToOne FK(DeliveryTrip) nullable 運搬由来
spreading_session OneToOne FK(SpreadingSession) nullable 散布由来
created_at / updated_at datetime auto

方針

  • WorkRecord 自体には圃場別明細を持たない
  • 明細参照時は DeliveryTrip / SpreadingSession 側を開く
  • 実体ではなく索引として使う

7. 在庫連携

7.1 変更前

  • 施肥計画保存時に RESERVE
  • 施肥計画の 散布確定USE

7.2 変更後

  • 施肥計画保存時に RESERVE を継続
  • 散布実績保存時に USE
  • USE.occurred_onSpreadingSession.date

7.3 StockTransaction 追加推奨フィールド

  • spreading_item = FK(SpreadingSessionItem, null=True, blank=True, on_delete=SET_NULL)

7.4 USE 作成ルール

  • SpreadingSessionItem ごとに USE を1件作る
  • materialitem.fertilizer.material
  • quantityactual_bags
  • occurred_onsession.date
  • note散布実績「{session.name or session.date}」

7.5 更新・削除

  • 散布実績更新時は、その session に紐づく USE を全置換で作り直す
  • 散布実績削除時は対応 USE を削除する

7.6 RESERVE との整合

  • RESERVE は従来どおり計画値 bags ベースで維持する
  • USE は散布実績 actual_bags ベースで発生する
  • 計画値と実績値は併存する

つまり、

  • 計画: bags
  • 実績集計: actual_bags
  • 引当: RESERVE
  • 使用: USE

を分けて持つ。


8. 集計ルール

SpreadingAllocation を持たないため、残量は集計で求める。

8.1 計画値

planned_total(field, fertilizer, year)

  • FertilizationEntry の合計

8.2 運搬済み量

delivered_total(field, fertilizer, year)

  • DeliveryTrip.date != nullDeliveryTripItem.bags 合計

8.3 散布済み量

spread_total(field, fertilizer, year)

  • SpreadingSessionItem.actual_bags の合計

8.4 FertilizationEntry.actual_bags 集計ルール

actual_bags(field, fertilizer, year)

  • SUM(SpreadingSessionItem.actual_bags)
  • 対象条件は 同一 year, field, fertilizer

実装上は、散布実績保存・更新・削除時に、該当する FertilizationEntry を再計算して更新する。

8.5 表示用の残量

remaining_bags = delivered_total - spread_total

8.6 計画進捗用の残量

remaining_plan_bags = planned_total - spread_total

8.7 差異の扱い

運搬数量と散布数量がずれることは許容する。

  • remaining_bags < 0 の場合: 運搬実績不足
  • remaining_plan_bags < 0 の場合: 計画超過

まずは 圃場+肥料単位で差異が分かること を優先する。


9. API方針

9.1 施肥計画 API

UI上で廃止するもの

  • POST /api/fertilizer/plans/{id}/confirm_spreading/
  • POST /api/fertilizer/plans/{id}/unconfirm/

追加する読み取り項目

  • spread_status
  • planned_total_bags
  • spread_total_bags
  • remaining_total_bags
  • FertilizationEntryactual_bags

9.2 散布実績 API新規

メソッド URL 説明
GET /api/fertilizer/spreading/?year={year} 年度別一覧
POST /api/fertilizer/spreading/ 新規作成
GET /api/fertilizer/spreading/{id}/ 詳細
PUT /api/fertilizer/spreading/{id}/ 更新
DELETE /api/fertilizer/spreading/{id}/ 削除
GET /api/fertilizer/spreading/candidates/?year={year} 散布候補一覧

9.3 作業記録 API新規

メソッド URL 説明
GET /api/workrecords/?year={year} 一覧
GET /api/workrecords/{id}/ 詳細

詳細では元レコードへのリンク情報を返すだけでよい。

9.4 候補一覧レスポンス例

[
  {
    "field": 5,
    "field_name": "田中上",
    "fertilizer": 1,
    "fertilizer_name": "電気炉さい",
    "planned_bags": "4.0000",
    "delivered_bags": "4.0000",
    "spread_bags": "1.5000",
    "remaining_bags": "2.5000",
    "remaining_plan_bags": "2.5000",
    "delivery_gap": "0.0000"
  }
]

9.5 保存時の基本バリデーション

  • actual_bags > 0 を必須
  • 同一 session 内で field + fertilizer の重複禁止
  • remaining_plan_bags を大きく超える入力はエラー
  • remaining_bags を超える入力は警告を出した上で保存は許容してよい

9.6 actual_bags 同期APIルール

  • 散布実績保存後、対象となる FertilizationEntry.actual_bags を即時再集計する
  • SUM(...) = 0 の場合は actual_bags = null としてよい
  • 一覧・編集画面では、再計算後の値を返す

10. フロントエンド変更

10.1 施肥計画一覧 /fertilizer

  • 散布確定 ボタンを削除
  • ConfirmSpreadingModal.tsx は削除対象
  • カードに進捗状態を表示
  • 計画値と実績値を並べて表示できるようにする

表示例:

  • 未散布
  • 一部散布 3.5 / 8.0袋
  • 散布完了
  • 計画超過
  • 計画 4.0 → 実績 3.5

10.2 散布実績画面(新規)

  • /fertilizer/spreading

画面要件

  • 散布日入力
  • 年度選択
  • 運搬済み・未散布候補一覧
  • 圃場単位選択
  • 実績袋数編集
  • 差異のインライン警告

10.4 施肥計画編集画面

  • bags を計画値として表示
  • actual_bags を実績値として参照表示
  • 編集時にも 計画 / 実績 の差が分かるようにする

actual_bags は散布実績からの集計値なので、編集画面で直接入力はしない。

10.3 作業記録画面(新規または将来画面の土台)

最低限、以下を一覧できること。

  • 日付
  • 作業種別
  • タイトル
  • 元データへの遷移

11. 将来の資料生成に向けた要件

今回、固定形式のPDFは実装しない。

ただし、将来どの相手先様式にも対応できるよう、散布実績から少なくとも以下を引き出せる必要がある。

  • 散布日
  • 年度
  • 圃場
  • 肥料
  • 散布袋数
  • 備考

また、資料生成時に計画値と実績値を比較できるよう、少なくとも以下の対比を維持する。

  • bags(計画)
  • actual_bags(実績)

つまり今回の要件は、PDFを作ること ではなく、後で資料化できる元データを崩さず持つこと である。


12. 前年度コピーへの影響

将来 copy_from_previous_year で前年度の FertilizationEntry をコピーする際は、以下のルールを適用する。

  • actual_bags がある場合: actual_bags を新年度の bags 初期値として使う
  • actual_bagsnull の場合: 従来どおり bags をコピーする

この方針により、前年度に実際に散布した量を次年度計画の初期値として再利用できる。


13. WorkRecord 自動生成ルール

12.1 運搬

  • DeliveryTrip.date 保存時に upsert
  • title = 肥料運搬: {delivery_plan.name} {n}回目
  • 日付削除時は対応 WorkRecord を削除

12.2 散布

  • SpreadingSession 保存時に upsert
  • title = 肥料散布: {session.name or session.date}
  • 削除時は対応 WorkRecord を削除

12.3 実装方針

自動生成は view に直書きせず、サービス層で idempotent に実装する。


14. 移行方針

13.1 既存の is_confirmed

  • 既存カラムは残す
  • 新UIでは参照しない
  • 旧方式の確定データは 旧散布確定データ として扱う

13.2 既存 confirm_spreading API

  • まずフロントから呼ばない状態にする
  • しばらく互換維持
  • 新散布実績画面へ完全移行後に削除を検討する

15. 実装ステップ

  1. SpreadingSession / SpreadingSessionItem を追加
  2. apps/workrecords/ を追加
  3. FertilizationEntry.actual_bags を追加
  4. StockTransaction.spreading_item を追加
  5. 散布実績 API を追加
  6. 作業記録 API を追加
  7. 散布候補集計 API を追加
  8. 散布保存時の actual_bags 再集計を追加
  9. 施肥計画一覧に進捗表示を追加
  10. 施肥計画一覧から 散布確定 ボタンを外す
  11. ConfirmSpreadingModal.tsx を撤去
  12. 散布実績画面を追加
  13. WorkRecord 自動生成を追加
  14. 散布保存時の在庫 USE 連携を追加
  15. copy_from_previous_yearactual_bags → bags 反映を追加

16. 影響ファイル(想定)

Backend

  • backend/apps/fertilizer/models.py
  • backend/apps/fertilizer/serializers.py
  • backend/apps/fertilizer/views.py
  • backend/apps/fertilizer/urls.py
  • backend/apps/fertilizer/admin.py
  • backend/apps/fertilizer/migrations/
  • backend/apps/fertilizer/services.py または同等の集計ロジック配置先
  • backend/apps/materials/models.py
  • backend/apps/materials/stock_service.py
  • backend/apps/materials/migrations/
  • backend/apps/workrecords/

Frontend

  • frontend/src/app/fertilizer/page.tsx
  • frontend/src/app/fertilizer/_components/FertilizerEditPage.tsx
  • frontend/src/app/fertilizer/_components/ConfirmSpreadingModal.tsx
  • frontend/src/app/fertilizer/spreading/
  • frontend/src/app/workrecords/
  • frontend/src/types/index.ts

ドキュメント

  • document/13_マスタードキュメント_施肥計画編.md
  • document/14_マスタードキュメント_分配計画編.md
  • CLAUDE.md

17. 受け入れ条件

  • 施肥計画一覧に 散布確定 ボタンが表示されない
  • 日付単位の散布実績を登録できる
  • 散布対象は全部または一部の圃場を選べる
  • 運搬回の順番に依存せず、運搬済みデータを元に散布できる
  • 実際の散布袋数を入力できる
  • 散布保存時に在庫 USE が作成される
  • 散布保存・更新・削除時に FertilizationEntry.actual_bags が再集計される
  • 作業記録一覧から運搬と散布を参照できる
  • 施肥計画一覧で 未散布 / 一部散布 / 完了 / 計画超過 が分かる
  • 施肥計画一覧・編集画面で 計画値 / 実績値 の両方が分かる
  • 前年度コピー時に actual_bags があればそれを次年度 bags 初期値として使える
  • 将来、散布実績データから資料化に必要な情報を取り出せる

18. 将来拡張

今回見送るが、将来必要になれば以下を追加できる。

  • SpreadingAllocation による便単位追跡
  • 相手先別PDFテンプレート群
  • 残肥返却や再入庫
  • 栽培管理全体を包含した作業記録詳細