仕様書に追記しました。更新先は 施肥散布実績連携変更実装仕様.md です。

今回追加した内容は主にこの5点です。

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

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

View File

@@ -1,80 +1,83 @@
# 施肥散布実績連携変更実装仕様
> 作成日: 2026-03-17
> 改訂日: 2026-03-17
> 対象プロジェクト: `keinasystem_t02`
> 目的: 実運用に合わせて、施肥計画の「散布確定」中心設計を見直し、運搬計画起点の散布実績管理と日付ベースの作業記録自動生成へ移行する
> 位置づけ: 実運用反映版・MVP仕様
---
## 1. 結論
今回の変更では、施肥実績の確定主体を `FertilizationPlan` から外し、以下の流れに再設計する。
- 施肥計画は「計画」と「在庫引当」の責務に限定する
- 運搬計画は「どの日に何をどこへ運んだか」の実績起点にする
- 散布実績は、運搬済み肥料を元にした「日付単位の散布記録」で管理する
- 作業記録は、運搬日・散布日から自動生成する
- 在庫の `USE` は施肥計画の散布確定ではなく、散布実績の保存時に発生させる
つまり、業務フローは以下に変更する。
今回の変更では、施肥データの流れを以下の一気通貫に揃える。
1. 施肥計画を作る
2. 運搬計画で肥料を運ぶ
3.んだ肥料の全部または一部を、日付単位で散布する
4. 運搬日・散布日をもとに作業記録を自動作成す
2. 施肥計画を元に運搬計画を作る
3.搬済み肥料を元に散布実績を記録する
4. 散布実績を元に作業記録として参照でき
5. 将来、必要な相手先提出資料へ変換できる
MVPの方針は以下とする。
- 施肥計画の `散布確定` ボタンは廃止する
- 散布実績は `SpreadingSession``SpreadingSessionItem` で管理する
- `SpreadingAllocation` は作らない
- 在庫の `USE` は散布実績保存時に発生させる
- `WorkRecord` は作るが、明細の二重管理はしない
- `WorkRecord``DeliveryTrip``SpreadingSession` への索引として使う
- `FertilizationEntry.bags` は計画値として維持し、`actual_bags` を実績集計値として別保持する
- 客先提出PDFは今回実装しない
- ただし、将来どの様式にも変換できるよう、元データを引き出せる構造にする
---
## 2. 背景と現状課題
## 2. 背景
現状実装では、`FertilizationPlan` に「散布確定」ボタンがあり、ここで実績数量を入力して在庫の `RESERVE → USE` を行っている。
現状実装では、`FertilizationPlan` に「散布確定」ボタンがあり、施肥計画単位で一括確定する流れになっている。
しかし実運用では、以下のギャップがある。
しかし実運用では、実際に必要なのは以下である。
- 実際の散布は施肥計画から直接ではなく、運搬計画で現地へ運んだ肥料を元に行う
- 散布時に参照するのは「1回目」「2回目」という順番ではなく、日付まで付いた運搬済み肥料全体である
- 散布対象は運搬計画全体の中から「全部」または「一部の圃場」になる
- 散布中に、運搬計画上の袋数から実際の散布袋数へ変更が入る
- 作業記録は日付単位で残したい
- 運搬計画にも日付が付いたため、運搬も作業記録として自動生成したい
- どの圃場に
- どの肥料を
- どれだけ散布したか
- それがいつ行われたか
そのため、「施肥計画で一括散布確定する」設計は実態に合わない
また、データは以下のように流用できる必要がある
- 施肥計画作成
- 運搬計画作成
- 散布実績記録
- 作業記録参照
- 相手先提出資料への転用
この流れが切れると、従来どおり別DBや別表へ転記が必要になり、システム化の価値が大きく下がる。
---
## 3. 変更後の設計方針
## 3. 今回の判断
### 3.1 責務分離
### 3.1 採用するもの
- `FertilizationPlan` は計画データ
- `DeliveryPlan / DeliveryTrip / DeliveryTripItem` は運搬計画と運搬実績
- 新設する `SpreadingSession` 系は散布実績
- 新設する `WorkRecord` 系は日付ベースの作業記録
- `SpreadingSession``SpreadingSessionItem`
- `WorkRecord` の新設
- 散布保存時の在庫 `USE` 登録
- `FertilizationEntry.actual_bags` の集計反映
- 施肥計画一覧からの `散布確定` UI 廃止
- 施肥計画進捗の自動集計表示
### 3.2 日付中心設計
### 3.2 今回は見送るもの
作業記録は「年度中心」ではなく「日付中心」で扱う。
年度は `date` から導出できる補助情報とし、画面の年フィルタや施肥計画との照合に使う。
- `SpreadingAllocation`
- `DeliveryTripItem.fertilization_entry` FK
- 客先提出PDFの固定様式実装
### 3.3 散布元の考え方
### 3.3 理由
散布元は「特定の運搬回の順番」ではなく、以下を満たす運搬済み明細の集合とする。
- `DeliveryTrip.date` が入ってい
- まだ未散布残がある
- 年度が一致す
つまり、散布画面では「その年度の運搬済み・未散布残あり」の明細を一覧化し、その中から対象圃場を選んで散布する。
### 3.4 差異の扱い
運搬数量と実散布数量が一致しないことは許容する。
ただし整合性のため、以下の扱いにする。
- 実散布数量は運搬明細から初期値セットする
- ユーザーは実散布数量を編集できる
- 実散布数量が運搬済み残を超える場合は、そのまま黙って保存せず、差異として明示する
- MVP では「運搬実績を先に修正してから再保存」を推奨フローとする
- `SpreadingAllocation` が必要になる運用は今後も発生しない前提である
- `WorkRecord` は必要だが、詳細を二重保持するのではなく索引で十分である
- 在庫は実際に減るため、散布実績と同時に管理すべきであ
- 提出資料は相手先ごとに違うため、今固定様式を作るより、元データ抽出可能性を優先すべきである
- 運搬計画は年度ベースで複数施肥計画を横断するため、`DeliveryTripItem` に単一の `fertilization_entry` FK を持たせるのは不安があ
---
@@ -82,22 +85,24 @@
### 4.1 IN
- 施肥計画一覧から「散布確定」ボタンを外す
- 散布実績を日付単位で新設する
- 散布実績は運搬済み明細を元に、全部または一部の圃場を対象に作成できる
- 散布時に実績袋数を変更できる
- 運搬日から作業記録を自動生成する
- 散布日から作業記録を自動生成する
- 在庫使用実績は散布実績保存時に作成する
- 施肥計画一覧に「未散布 / 一部散布 / 完了」などの進捗状態を表示する
- 施肥計画`散布確定` ボタン廃止
- 散布実績モデル追加
- 作業記録索引モデル追加
- 在庫 `USE` 連携
- `FertilizationEntry.actual_bags` 集計
- 散布実績一覧・作成・更新・削除 API
- 散布候補集計 API
- 施肥計画進捗表示
- 前年度コピー時の `actual_bags → bags` 初期化反映
- 作業記録一覧から運搬・散布の実績を参照可能にすること
### 4.2 OUT
- 肥料の置き場所管理
- 残肥の返却・再入庫
- ルート最適化
- 複数人同時編集のロック制御
- 汎用的な作業日誌機能の完成版 UI
- 運搬便ごとの散布充当追跡
- 相手先ごとのPDF様式実装
- 残肥返却・再入庫管理
- カレンダーUIの完成版
- 栽培管理全体を包含した汎用作業日誌の完成版
---
@@ -105,71 +110,83 @@
### 5.1 施肥計画
1. 施肥計画を作成・更新する
2. 従来どおり `RESERVE` を張
3.では散布確定しない
1. 施肥計画を作成する
2. 計画値として保持す
3.の段階では散布確定しない
### 5.2 運搬計画
1. 年度の施肥計画から運搬計画を作る
2. `DeliveryTrip` に日を入れる
3. 日付が入った `DeliveryTrip` は「運搬実績あり」と見なす
4. `DeliveryTrip` ごとに作業記録を自動生成する
1. 施肥計画を元に運搬計画を作成す
2. `DeliveryTrip.date`運搬日を入れる
3. 日付が入った運搬回を `運搬済み` とみなす
4. 運搬日が保存されたら `WorkRecord` を自動生成または更新する
### 5.3 散布実績
1. ユーザーは散布日を選ぶ
2. その年度の運搬済みかつ未散布残あり」の明細を候補表示する
3. 候補の中から、全部または一部の圃場を選ぶ
4. 初期値として未散布残袋数を入れる
5. 実際散布袋数へ修正して保存する
6. 保存時に `USE` 在庫履歴を散布日で作
7. 散布日の作業記録を自動生成する
2. システムはその年度の `運搬済み` データを集計する
3. `未散布残` がある圃場×肥料を候補として表示する
4. ユーザーは全部または一部の圃場を選ぶ
5. 実際散布した袋数を入力して保存する
6. 保存時に在庫 `USE` を作成す
7. 保存時に `WorkRecord` を自動生成または更新する
---
## 6. 推奨データモデル変更
## 6. データモデル
### 6.1 `FertilizationPlan`
現行`is_confirmed` / `confirmed_at`業務上の意味が薄くなるため、段階的に非推奨化する。
既存`is_confirmed` / `confirmed_at`中心機能ではなくなる。
### 方針
#### 方針
- DB カラムは互換性のため一旦残してよい
- DBカラムは当面残す
- 新UIでは `confirm_spreading` / `unconfirm` を使わない
- 一覧表示は以下の計算状態へ置き換え
- 一覧では散布進捗を計算表示す
### 追加する表示用状態
#### 追加する表示用項目
- `spread_status`: `unspread | partial | completed | over_applied`
- `planned_total_bags`
- `spread_total_bags`
- `remaining_total_bags`
- `spread_started_at`
- `spread_completed_at`
これらは serializer で計算する実装を推奨する。
#### `FertilizationEntry.actual_bags` の追加
### 6.2 `DeliveryTripItem`
`FertilizationEntry` に以下のフィールドを追加する。
現在は `field + fertilizer + bags` のみを持つが、散布実績との照合性を上げるため、内部参照を追加する。
- `actual_bags = DecimalField(max_digits=10, decimal_places=4, null=True, blank=True)`
### 追加推奨フィールド
#### 役割
- `fertilization_entry`: `FK(FertilizationEntry, null=True, on_delete=SET_NULL)`
- `bags`: 計画値
- `actual_bags`: 散布実績の集計値
### 目的
`bags` はユーザーが立てた計画として保持し、散布後も自動で上書きしない。
実績は `actual_bags` に集約する。
- どの施肥計画エントリ由来かを内部的に追跡する
- 施肥計画進捗の自動集計をしやすくする
- 将来の差異分析をしやすくする
#### `actual_bags` の更新契機
※ UI 上は引き続き「年度横断で選ぶ」挙動のままでよい。
- `SpreadingSessionItem` 作成時
- `SpreadingSessionItem` 更新時
- `SpreadingSessionItem` 削除時
上記のたびに、同一年度・圃場・肥料の散布実績合計を再集計して反映する。
### 6.2 `DeliveryTrip` / `DeliveryTripItem`
MVPではモデル追加は行わない。
#### 前提
- `DeliveryTrip.date != null` の明細のみを `運搬済み` とみなす
- `DeliveryTripItem` は今までどおり `field + fertilizer + bags` を保持する
- `fertilization_entry` FK は追加しない
### 6.3 `SpreadingSession`(新規)
散布日単位の親レコード。
散布日の親レコード。
| フィールド | 型 | 制約 | 説明 |
|---|---|---|---|
@@ -180,11 +197,11 @@
| notes | text | blank | 備考 |
| created_at / updated_at | datetime | auto | |
### 制約
#### 制約
- MVP では `unique_together = [['year', 'date']]` を推奨
- `year + date` の一意制約は付けない
同日に複数散布イベントを分けたい場合は、将来 `sequence` を追加して拡張する。
同日に午前・午後やエリア別で複数散布記録を分けられるようにする。
### 6.4 `SpreadingSessionItem`(新規)
@@ -194,117 +211,167 @@
|---|---|---|---|
| id | int | PK | |
| session | FK(SpreadingSession) | CASCADE | |
| fertilization_entry | FK(FertilizationEntry) | SET_NULL, nullable | 元施肥計画エントリ |
| 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 | 初期表示に使った運搬残袋数 |
| 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']]`
### 6.5 `SpreadingAllocation`(新規)
#### 補足
どの運搬明細残からどれだけ散布に充当したかを持つ中間テーブル。
- `fertilization_entry` FK は持たない
- 圃場+肥料単位の事実記録を優先する
| フィールド | 型 | 制約 | 説明 |
|---|---|---|---|
| id | int | PK | |
| spreading_item | FK(SpreadingSessionItem) | CASCADE | |
| delivery_trip_item | FK(DeliveryTripItem) | PROTECT | |
| allocated_bags | Decimal(10,4) | required | 充当袋数 |
### 6.5 `WorkRecord`(新規)
### 目的
作業記録参照用の索引テーブル。
- 運搬順ではなく、運搬済み明細全体から散布したことを追跡する
- どの運搬明細に未散布残がどれだけあるかを算出できる
- 後から「どの便で運んだ肥料をいつ散布したか」を確認できる
#### 目的
### 6.6 `WorkRecord`(新規)
日付単位で自動生成される作業記録。
MVP では汎用化しすぎず、今回必要な 2 種類に絞る。
- 日付順に作業を一覧できるようにする
- 将来、播種・農薬散布・収穫なども同じ入口で見られるようにする
- ただし、詳細の本体は各業務テーブル側に持つ
| フィールド | 型 | 制約 | 説明 |
|---|---|---|---|
| id | int | PK | |
| work_date | DateField | required | 作業日 |
| work_type | CharField | required | `fertilizer_delivery` / `fertilizer_spreading` |
| title | varchar(200) | required | 表示名 |
| title | varchar(200) | required | 一覧表示名 |
| year | int | required | 年度フィルタ補助 |
| auto_created | bool | default=True | 自動生成フラグ |
| delivery_trip | OneToOne FK(DeliveryTrip) | nullable | 運搬由来の場合 |
| spreading_session | OneToOne FK(SpreadingSession) | nullable | 散布由来の場合 |
| delivery_trip | OneToOne FK(DeliveryTrip) | nullable | 運搬由来 |
| spreading_session | OneToOne FK(SpreadingSession) | nullable | 散布由来 |
| created_at / updated_at | datetime | auto | |
### 方針
#### 方針
- 作業記録の明細は二重保持しない
- 画面表示時は元の `DeliveryTrip` / `SpreadingSession` を参照して詳細を出す
- `WorkRecord` 自体には圃場別明細を持たない
- 明細参照時は `DeliveryTrip` / `SpreadingSession` 側を開く
- 実体ではなく索引として使う
---
## 7. 在庫連携の変更
## 7. 在庫連携
### 7.1 変更前
- 施肥計画保存時に `RESERVE`
- 施肥計画の散布確定`USE`
- 施肥計画の `散布確定` `USE`
### 7.2 変更後
- 施肥計画保存時に `RESERVE` を継続
- 散布実績保存時に `USE`
- `USE.occurred_on``SpreadingSession.date`
- `RESERVE` は引き続き `FertilizationPlan` に紐づける
- `USE``SpreadingSessionItem` に紐づける
### 7.3 `StockTransaction` 追加推奨フィールド
- `spreading_item = FK(SpreadingSessionItem, null=True, blank=True, on_delete=SET_NULL)`
### 備考
### 7.4 `USE` 作成ルール
`fertilization_plan` だけでは「どの圃場・どの散布日で使ったか」が追えないため、散布実績由来の FK を追加する。
- `SpreadingSessionItem` ごとに `USE` を1件作る
- `material``item.fertilizer.material`
- `quantity``actual_bags`
- `occurred_on``session.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. API 変更方針
## 8. 集計ルール
### 8.1 施肥計画 API
`SpreadingAllocation` を持たないため、残量は集計で求める。
### 廃止対象
### 8.1 計画値
`planned_total(field, fertilizer, year)`
- `FertilizationEntry` の合計
### 8.2 運搬済み量
`delivered_total(field, fertilizer, year)`
- `DeliveryTrip.date != null``DeliveryTripItem.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/`
即時削除ではなく、まずは UI から呼ばない状態にし、その後バックエンドも段階廃止する。
### 追加する読み取り項目
#### 追加する読み取り項目
- `spread_status`
- `planned_total_bags`
- `spread_total_bags`
- `remaining_total_bags`
- `spread_started_at`
- `spread_completed_at`
- `FertilizationEntry``actual_bags`
### 8.2 運搬計画 API
### 既存の POST / PUT
`DeliveryTrip.date` の保存時に、対応する `WorkRecord` を upsert する。
### 詳細レスポンスへの追加推奨
-`DeliveryTripItem``spread_bags`
-`DeliveryTripItem``remaining_bags`
-`DeliveryTrip``work_record_id`
### 8.3 散布実績 API新規
### 9.2 散布実績 API新規
| メソッド | URL | 説明 |
|---|---|---|
@@ -313,9 +380,18 @@ MVP では汎用化しすぎず、今回必要な 2 種類に絞る。
| GET | `/api/fertilizer/spreading/{id}/` | 詳細 |
| PUT | `/api/fertilizer/spreading/{id}/` | 更新 |
| DELETE | `/api/fertilizer/spreading/{id}/` | 削除 |
| GET | `/api/fertilizer/spreading/candidates/?year={year}&date={date}` | 散布候補一覧 |
| GET | `/api/fertilizer/spreading/candidates/?year={year}` | 散布候補一覧 |
### 候補一覧レスポンスイメージ
### 9.3 作業記録 API新規
| メソッド | URL | 説明 |
|---|---|---|
| GET | `/api/workrecords/?year={year}` | 一覧 |
| GET | `/api/workrecords/{id}/` | 詳細 |
詳細では元レコードへのリンク情報を返すだけでよい。
### 9.4 候補一覧レスポンス例
```json
[
@@ -326,191 +402,168 @@ MVP では汎用化しすぎず、今回必要な 2 種類に絞る。
"fertilizer_name": "電気炉さい",
"planned_bags": "4.0000",
"delivered_bags": "4.0000",
"already_spread_bags": "1.5000",
"spread_bags": "1.5000",
"remaining_bags": "2.5000",
"source_trip_item_ids": [12, 18]
"remaining_plan_bags": "2.5000",
"delivery_gap": "0.0000"
}
]
```
### 散布作成リクエストイメージ
### 9.5 保存時の基本バリデーション
```json
{
"year": 2026,
"date": "2026-03-17",
"items": [
{
"field_id": 5,
"fertilizer_id": 1,
"actual_bags": "2.3000"
},
{
"field_id": 6,
"fertilizer_id": 1,
"actual_bags": "1.0000"
}
]
}
```
- `actual_bags > 0` を必須
- 同一 `session` 内で `field + fertilizer` の重複禁止
- `remaining_plan_bags` を大きく超える入力はエラー
- `remaining_bags` を超える入力は警告を出した上で保存は許容してよい
### サーバー側の割当ルール
### 9.6 `actual_bags` 同期APIルール
- 同一 `field + fertilizer` の候補運搬明細に対して自動割当する
- 割当順は `trip.date asc, trip.id asc, item.id asc` の固定順を推奨
- UI では「何回目」ではなく「残量」を見せる
- 散布実績保存後、対象となる `FertilizationEntry.actual_bags` を即時再集計する
- `SUM(...) = 0` の場合は `actual_bags = null` としてよい
- 一覧・編集画面では、再計算後の値を返す
---
## 9. フロントエンド変更方針
## 10. フロントエンド変更
### 9.1 施肥計画一覧 `/fertilizer`
### 10.1 施肥計画一覧 `/fertilizer`
### 変更点
- 「散布確定」ボタンを廃止
- `散布確定` ボタンを削除
- `ConfirmSpreadingModal.tsx` は削除対象
- 各計画カードに進捗状態を表示
- カードに進捗状態を表示
- 計画値と実績値を並べて表示できるようにする
### 表示例
表示例:
- `未散布`
- `一部散布 3.5 / 8.0袋`
- `散布完了`
- `計画超過`
- `計画 4.0 → 実績 3.5`
### 9.2 運搬計画編集 `/distribution/new` `/distribution/[id]/edit`
### 追加する変更
- `DeliveryTripItem.bags` を画面上で直接編集できるようにする
- 各明細に `残り未散布` を表示する
- `date` 入力済みの回は「運搬実績あり」表示にする
### 理由
現状 UI では回の間の移動はできるが、袋数の直接編集ができない。
今回の業務に合わせるなら、運搬実績の修正も可能である必要がある。
### 9.3 散布実績画面(新規)
新規ページ例:
### 10.2 散布実績画面(新規)
- `/fertilizer/spreading`
- または `/distribution/spreading`
### 画面要件
#### 画面要件
- 日付を先に選ぶ
- その年度の運搬済み未散布残を一覧表示
- 圃場ごとにチェックして「今日散布する対象」を選べる
- 初期袋数は残量を自動セット
- 実績袋数編集可能
- 保存時に差異があればインライン警告を表示
- 散布日入力
- 年度選択
- 運搬済み・未散布候補一覧
- 圃場単位選択
- 実績袋数編集
- 差異のインライン警告
### UI イメージ
### 10.4 施肥計画編集画面
```text
[散布日: 2026-03-17] [年度: 2026]
- `bags` を計画値として表示
- `actual_bags` を実績値として参照表示
- 編集時にも `計画 / 実績` の差が分かるようにする
未散布残一覧
田中上 電気炉さい 計画4.0 運搬残2.5 実績[2.3]
田中下 電気炉さい 計画2.0 運搬残2.0 実績[2.0]
`actual_bags` は散布実績からの集計値なので、編集画面で直接入力はしない。
[保存]
```
### 10.3 作業記録画面(新規または将来画面の土台)
### 9.4 作業記録画面(将来 UI
今回の実装では、まずは自動生成と API を優先する。
一覧 UI は最小限でもよいが、少なくとも以下を表示できる形にする。
最低限、以下を一覧できること。
- 日付
- 作業種別
- タイトル
- 元データへのリンク
- 元データへの遷移
---
## 10. 作業記録自動生成ルール
## 11. 将来の資料生成に向けた要件
### 10.1 運搬
今回、固定形式のPDFは実装しない。
### 作成契機
ただし、将来どの相手先様式にも対応できるよう、散布実績から少なくとも以下を引き出せる必要がある。
- `DeliveryTrip.date``null → 日付あり` になったとき
- 既存レコード更新時に日付が変わったとき
- 散布日
- 年度
- 圃場
- 肥料
- 散布袋数
- 備考
### 作成内容
また、資料生成時に計画値と実績値を比較できるよう、少なくとも以下の対比を維持する。
- `work_type = fertilizer_delivery`
- `work_date = DeliveryTrip.date`
- `bags`(計画)
- `actual_bags`(実績)
つまり今回の要件は、`PDFを作ること` ではなく、`後で資料化できる元データを崩さず持つこと` である。
---
## 12. 前年度コピーへの影響
将来 `copy_from_previous_year` で前年度の `FertilizationEntry` をコピーする際は、以下のルールを適用する。
- `actual_bags` がある場合: `actual_bags` を新年度の `bags` 初期値として使う
- `actual_bags``null` の場合: 従来どおり `bags` をコピーする
この方針により、前年度に実際に散布した量を次年度計画の初期値として再利用できる。
---
## 13. WorkRecord 自動生成ルール
### 12.1 運搬
- `DeliveryTrip.date` 保存時に upsert
- `title = 肥料運搬: {delivery_plan.name} {n}回目`
- 日付削除時は対応 `WorkRecord` を削除
### 更新・削除
### 12.2 散布
- 日付変更時は `WorkRecord.work_date` も更新
- 日付を空に戻したら、自動生成レコードを削除
- `SpreadingSession` 保存時に upsert
- `title = 肥料散布: {session.name or session.date}`
- 削除時は対応 `WorkRecord` を削除
### 10.2 散布
### 12.3 実装方針
### 作成契機
- `SpreadingSession` の作成時
- `date` 更新時
### 作成内容
- `work_type = fertilizer_spreading`
- `work_date = SpreadingSession.date`
- `title = 肥料散布: {date}`
### 更新・削除
- 日付変更時は upsert
- `SpreadingSession` 削除時は対応 `WorkRecord` を削除
### 10.3 実装上の注意
自動生成は view 層に直接書かず、サービス層関数に切り出して idempotent に実装する。
推奨関数:
- `sync_delivery_work_record(trip)`
- `sync_spreading_work_record(session)`
- `delete_auto_work_record_for_delivery(trip)`
- `delete_auto_work_record_for_spreading(session)`
自動生成は view に直書きせず、サービス層で idempotent に実装する。
---
## 11. 移行方針
## 14. 移行方針
### 11.1 既存の確定済み施肥計画
### 13.1 既存の `is_confirmed`
既存の `confirm_spreading` 実装では、圃場別散布実績が永続化されていない。
`StockTransaction` には `material` 単位の `USE` は残るが、圃場別の再構成は正確にできない
- 既存カラムは残す
- 新UIでは参照しない
- 旧方式の確定データは `旧散布確定データ` として扱う
そのため、既存の確定済みデータは以下方針を推奨する。
### 13.2 既存 `confirm_spreading` API
- 既存 `USE` 在庫履歴はそのまま残す
- 既存 `is_confirmed=True` の計画は「旧方式確定済み」と見なす
- `SpreadingSession` への自動変換はしない
- 新方式の散布実績は、リリース後の新データから適用する
### 11.2 実装ステップ
1. `SpreadingSession` / `SpreadingSessionItem` / `SpreadingAllocation` / `WorkRecord` を追加
2. `DeliveryTripItem.fertilization_entry``StockTransaction.spreading_item` を追加
3. 散布実績 API を追加
4. 運搬日と散布日の作業記録同期を追加
5. フロントで「散布確定」ボタンを撤去
6. 散布実績画面を追加
7.`confirm_spreading` を非表示化
8. 十分移行後に旧 API を削除
- まずフロントから呼ばない状態にする
- しばらく互換維持
-散布実績画面へ完全移行後に削除を検討する
---
## 12. 影響ファイル(想定)
## 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_year``actual_bags → bags` 反映を追加
---
## 16. 影響ファイル(想定)
### Backend
@@ -520,20 +573,20 @@ MVP では汎用化しすぎず、今回必要な 2 種類に絞る。
- `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/serializers.py`
- `backend/apps/materials/migrations/`
- `backend/apps/workrecords/` または同等の新規 app
- `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/distribution/_components/DeliveryEditPage.tsx`
- `frontend/src/app/fertilizer/spreading/`
- `frontend/src/app/workrecords/`
- `frontend/src/types/index.ts`
- 散布実績の新規ページ群
- 作業記録の新規ページ群または既存画面への表示追加
### ドキュメント
@@ -541,27 +594,30 @@ MVP では汎用化しすぎず、今回必要な 2 種類に絞る。
- `document/14_マスタードキュメント_分配計画編.md`
- `CLAUDE.md`
※ これらは実装着手時に正式仕様へ反映する。
---
## 17. 受け入れ条件
- 施肥計画一覧に `散布確定` ボタンが表示されない
- 日付単位の散布実績を登録できる
- 散布対象は全部または一部の圃場を選べる
- 運搬回の順番に依存せず、運搬済みデータを元に散布できる
- 実際の散布袋数を入力できる
- 散布保存時に在庫 `USE` が作成される
- 散布保存・更新・削除時に `FertilizationEntry.actual_bags` が再集計される
- 作業記録一覧から運搬と散布を参照できる
- 施肥計画一覧で `未散布 / 一部散布 / 完了 / 計画超過` が分かる
- 施肥計画一覧・編集画面で `計画値 / 実績値` の両方が分かる
- 前年度コピー時に `actual_bags` があればそれを次年度 `bags` 初期値として使える
- 将来、散布実績データから資料化に必要な情報を取り出せる
---
## 13. 受け入れ条件
## 18. 将来拡張
- 施肥計画一覧に「散布確定」ボタンが表示されない
- 運搬済み明細を元に、日付単位の散布実績を新規作成できる
- 散布対象は、運搬計画全体から全部または一部の圃場を選べる
- 運搬回の順番に依存せず散布できる
- 散布時に実際袋数を変更できる
- 散布保存時に `USE` 在庫履歴が散布日で作成される
- `DeliveryTrip.date` 保存時に運搬作業記録が自動生成される
- `SpreadingSession.date` 保存時に散布作業記録が自動生成される
- 施肥計画一覧で未散布・一部散布・完了が分かる
今回見送るが、将来必要になれば以下を追加できる。
---
## 14. 前提と留意点
- 本仕様では「残肥の返却」は扱わない。未散布残は運搬済み残として次回散布へ繰り越す
- 同日複数散布の厳密な分離は MVP では不要とみなし、まずは 1日1レコードを採用する
- 既存確定済みデータの完全移行は行わない
- 作業記録 UI は最小構成でよいが、API と自動生成ロジックは今回入れる
- `SpreadingAllocation` による便単位追跡
- 相手先別PDFテンプレート群
- 残肥返却や再入庫
- 栽培管理全体を包含した作業記録詳細