From c9ae99ebc813646eb8254e6409b87f483003988f Mon Sep 17 00:00:00 2001 From: Akira Date: Tue, 17 Mar 2026 16:26:41 +0900 Subject: [PATCH] =?UTF-8?q?CODEX=E7=89=88=20=E6=98=A8=E6=97=A5=E9=81=8B?= =?UTF-8?q?=E3=82=93=E3=81=A0=E8=82=A5=E6=96=99=E3=82=92=E6=95=A3=E5=B8=83?= =?UTF-8?q?=E3=81=97=E3=81=A6=E3=81=8D=E3=81=BE=E3=81=97=E3=81=9F=E3=80=82?= =?UTF-8?q?=20=E3=81=9D=E3=82=8C=E3=81=A7=E3=80=81=E4=BB=8A=E3=81=AF?= =?UTF-8?q?=E6=96=BD=E8=82=A5=E8=A8=88=E7=94=BB=E3=81=AB=E3=80=8C=E6=95=A3?= =?UTF-8?q?=E5=B8=83=E7=A2=BA=E5=AE=9A=E3=80=8D=E3=83=9C=E3=82=BF=E3=83=B3?= =?UTF-8?q?=E3=81=8C=E3=81=82=E3=82=8B=E3=81=AE=E3=81=A7=E3=81=99=E3=81=8C?= =?UTF-8?q?=E3=80=81=E3=81=9D=E3=82=8C=E3=81=A0=E3=81=A8=E5=AE=9F=E6=85=8B?= =?UTF-8?q?=E3=81=AB=E5=90=88=E3=82=8F=E3=81=AA=E3=81=84=E4=BA=8B=E3=81=8C?= =?UTF-8?q?=E3=82=8F=E3=81=8B=E3=82=8A=E3=81=BE=E3=81=97=E3=81=9F=E3=80=82?= =?UTF-8?q?=20=E5=AE=9F=E9=9A=9B=E3=81=AB=E3=81=AF=E9=81=8B=E6=90=AC?= =?UTF-8?q?=E8=A8=88=E7=94=BB=E3=82=92=E5=85=83=E3=81=AB=E3=80=81=E9=81=8B?= =?UTF-8?q?=E3=82=93=E3=81=A0=E8=82=A5=E6=96=99=E3=82=92=E6=95=A3=E5=B8=83?= =?UTF-8?q?=E3=81=97=E3=81=BE=E3=81=99=E3=80=82=20=E9=A0=86=E5=BA=8F?= =?UTF-8?q?=E3=81=AF=E3=80=81=E9=81=8B=E6=90=AC=E8=A8=88=E7=94=BB=E3=81=AE?= =?UTF-8?q?1=E5=9B=9E=E7=9B=AE2=E5=9B=9E=E7=9B=AE=E3=81=AA=E3=81=A9?= =?UTF-8?q?=E3=81=AE=E9=A0=86=E5=BA=8F=E3=81=AB=E3=81=AF=E9=96=A2=E4=BF=82?= =?UTF-8?q?=E3=81=8C=E3=81=AA=E3=81=8F=20=E9=81=8B=E6=90=AC=E8=A8=88?= =?UTF-8?q?=E7=94=BB=E3=81=AE=E3=81=99=E3=81=B9=E3=81=A6=E3=81=AE=E4=B8=AD?= =?UTF-8?q?=E3=81=8B=E3=82=89=E3=80=81=E5=85=A8=E9=83=A8=E3=81=BE=E3=81=9F?= =?UTF-8?q?=E3=81=AF=E4=B8=80=E9=83=A8=E3=81=AE=E5=9C=83=E5=A0=B4=E3=81=AB?= =?UTF-8?q?=E5=AF=BE=E3=81=97=E3=81=A6=E6=95=A3=E5=B8=83=E3=81=97=E3=81=BE?= =?UTF-8?q?=E3=81=99=E3=80=82=20=E6=95=A3=E5=B8=83=E4=B8=AD=E3=81=AB?= =?UTF-8?q?=E3=80=81=E9=81=8B=E6=90=AC=E8=A8=88=E7=94=BB=E3=81=8B=E3=82=89?= =?UTF-8?q?=E5=AE=9F=E9=9A=9B=E3=81=AE=E6=95=A3=E5=B8=83=E8=A2=8B=E6=95=B0?= =?UTF-8?q?=E3=81=8C=E5=A4=89=E6=9B=B4=E3=81=AB=E3=81=AA=E3=82=8B=E5=A0=B4?= =?UTF-8?q?=E5=90=88=E3=81=8C=E3=81=82=E3=82=8B=E3=81=AE=E3=81=A7=E3=80=81?= =?UTF-8?q?=E5=A4=89=E6=9B=B4=E3=81=AB=E5=AF=BE=E5=87=A6=E3=81=A7=E3=81=8D?= =?UTF-8?q?=E3=81=AA=E3=81=91=E3=82=8C=E3=81=B0=E3=81=AA=E3=82=8A=E3=81=BE?= =?UTF-8?q?=E3=81=9B=E3=82=93=E3=80=82=E3=80=82=E6=95=A3=E5=B8=83=E3=81=AF?= =?UTF-8?q?=E6=97=A5=E4=BB=98=E5=8D=98=E4=BD=8D=E3=81=A7=E8=A1=8C=E3=81=84?= =?UTF-8?q?=E3=80=81=E3=81=9D=E3=81=AE=E6=97=A5=E4=BB=98=E3=82=92=E5=85=83?= =?UTF-8?q?=E3=81=AB=E4=BD=9C=E6=A5=AD=E8=A8=98=E9=8C=B2=E3=81=8C=E8=87=AA?= =?UTF-8?q?=E5=8B=95=E7=9A=84=E3=81=AB=E4=BD=9C=E6=88=90=E3=81=95=E3=82=8C?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F=E3=81=84?= =?UTF-8?q?=E3=81=A7=E3=81=99=E3=80=82=20=E9=81=8B=E6=90=AC=E8=A8=88?= =?UTF-8?q?=E7=94=BB=E3=81=AB=E3=82=82=E6=97=A5=E4=BB=98=E3=82=92=E3=81=A4?= =?UTF-8?q?=E3=81=91=E3=81=9F=E3=81=AE=E3=81=A7=E3=80=81=E3=81=9D=E3=82=8C?= =?UTF-8?q?=E3=82=82=E4=BD=9C=E6=A5=AD=E8=A8=98=E9=8C=B2=E3=81=8C=E8=87=AA?= =?UTF-8?q?=E5=8B=95=E7=9A=84=E3=81=AB=E4=BD=9C=E6=88=90=E3=81=95=E3=82=8C?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F=E3=81=84?= =?UTF-8?q?=E3=81=A7=E3=81=99=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 以上のような感じで、変更実装仕様を作成してもらえますか? --- .claude/settings.json | 4 +- .serena/memories/project_overview.md | 1 + .serena/memories/style_and_completion.md | 1 + .serena/memories/suggested_commands.md | 1 + 改善案/施肥散布実績連携変更実装仕様.md | 567 +++++++++++++++++++++++ 5 files changed, 573 insertions(+), 1 deletion(-) create mode 100644 .serena/memories/project_overview.md create mode 100644 .serena/memories/style_and_completion.md create mode 100644 .serena/memories/suggested_commands.md create mode 100644 改善案/施肥散布実績連携変更実装仕様.md diff --git a/.claude/settings.json b/.claude/settings.json index 706647a..d9eb474 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -65,7 +65,9 @@ "Read(//c/Users/akira/Develop/keinasystem_t02/**)", "Bash(TOKEN=\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzczNTY4MTAxLCJpYXQiOjE3NzM0ODE3MDEsImp0aSI6ImM1N2EyNzVjMzkyNTRmMjFiZmUxMzA4ZmU4ZjQ3ZWExIiwidXNlcl9pZCI6Mn0.LOZIpPOHN48YZuf7UBaDrPJfxb12hIm15QRnUPc9sYM\" __NEW_LINE_74a785697e4cd919__ echo \"=== After confirm: stock summary ===\" curl -s http://localhost:8000/api/materials/stock-summary/?material_type=fertilizer -H \"Authorization: Bearer $TOKEN\")", "Bash(git diff:*)", - "mcp__serena__find_symbol" + "mcp__serena__find_symbol", + "mcp__serena__get_symbols_overview", + "Bash(git status:*)" ], "additionalDirectories": [ "C:\\Users\\akira\\AppData\\Local\\Temp", diff --git a/.serena/memories/project_overview.md b/.serena/memories/project_overview.md new file mode 100644 index 0000000..320a469 --- /dev/null +++ b/.serena/memories/project_overview.md @@ -0,0 +1 @@ +keinasystem_t02 は農業生産者向けの作付け計画・圃場管理システム。主要スタックは Django/DRF/PostgreSQL(PostGIS) のバックエンドと Next.js 14 App Router + TypeScript + Tailwind CSS のフロントエンド。backend/apps に fields, plans, weather, reports, fertilizer, materials, mail があり、frontend/src/app に各画面がある。ドキュメント駆動で、CLAUDE.md と document/*.md が重要な仕様ソース。Windows 環境で Docker Compose による開発を前提としている。 \ No newline at end of file diff --git a/.serena/memories/style_and_completion.md b/.serena/memories/style_and_completion.md new file mode 100644 index 0000000..86a71f0 --- /dev/null +++ b/.serena/memories/style_and_completion.md @@ -0,0 +1 @@ +コードと仕様の変更はドキュメントドリブンで進める。仕様変更時は document 配下や CLAUDE.md の更新が重要。バックエンドは Django/DRF の標準的なモデル・serializer・viewset 構成、フロントは Next.js App Router と TypeScript。完了時は影響範囲に応じて少なくとも関連ドキュメント確認、必要な migration 確認、frontend lint (`npm run lint`) や対象 API/画面の動作確認を行う。既存の dirty worktree は勝手に戻さない。 \ No newline at end of file diff --git a/.serena/memories/suggested_commands.md b/.serena/memories/suggested_commands.md new file mode 100644 index 0000000..9e44555 --- /dev/null +++ b/.serena/memories/suggested_commands.md @@ -0,0 +1 @@ +Windows 環境の主要コマンド: `git status`, `rg `, `Get-ChildItem`, `Get-Content `, `docker compose -f docker-compose.develop.yml up -d`, `docker compose exec backend python manage.py migrate`, `docker compose exec backend python manage.py makemigrations`, `docker compose exec backend python manage.py runserver 0.0.0.0:8000`, `cd frontend; npm install; npm run dev`, `cd frontend; npm run lint`。開発用 compose では backend は `python manage.py runserver 0.0.0.0:8000`、frontend は `npm run dev` を利用する。 \ No newline at end of file diff --git a/改善案/施肥散布実績連携変更実装仕様.md b/改善案/施肥散布実績連携変更実装仕様.md new file mode 100644 index 0000000..7580869 --- /dev/null +++ b/改善案/施肥散布実績連携変更実装仕様.md @@ -0,0 +1,567 @@ +# 施肥散布実績連携変更実装仕様 + +> 作成日: 2026-03-17 +> 対象プロジェクト: `keinasystem_t02` +> 目的: 実運用に合わせて、施肥計画の「散布確定」中心設計を見直し、運搬計画起点の散布実績管理と日付ベースの作業記録自動生成へ移行する + +--- + +## 1. 結論 + +今回の変更では、施肥実績の確定主体を `FertilizationPlan` から外し、以下の流れに再設計する。 + +- 施肥計画は「計画」と「在庫引当」の責務に限定する +- 運搬計画は「どの日に何をどこへ運んだか」の実績起点にする +- 散布実績は、運搬済み肥料を元にした「日付単位の散布記録」で管理する +- 作業記録は、運搬日・散布日から自動生成する +- 在庫の `USE` は施肥計画の散布確定ではなく、散布実績の保存時に発生させる + +つまり、業務フローは以下に変更する。 + +1. 施肥計画を作る +2. 運搬計画で肥料を運ぶ +3. 運んだ肥料の全部または一部を、日付単位で散布する +4. 運搬日・散布日をもとに作業記録を自動作成する + +--- + +## 2. 背景と現状課題 + +現状実装では、`FertilizationPlan` に「散布確定」ボタンがあり、ここで実績数量を入力して在庫の `RESERVE → USE` を行っている。 + +しかし実運用では、以下のギャップがある。 + +- 実際の散布は施肥計画から直接ではなく、運搬計画で現地へ運んだ肥料を元に行う +- 散布時に参照するのは「1回目」「2回目」という順番ではなく、日付まで付いた運搬済み肥料全体である +- 散布対象は運搬計画全体の中から「全部」または「一部の圃場」になる +- 散布中に、運搬計画上の袋数から実際の散布袋数へ変更が入る +- 作業記録は日付単位で残したい +- 運搬計画にも日付が付いたため、運搬も作業記録として自動生成したい + +そのため、「施肥計画で一括散布確定する」設計は実態に合わない。 + +--- + +## 3. 変更後の設計方針 + +### 3.1 責務分離 + +- `FertilizationPlan` は計画データ +- `DeliveryPlan / DeliveryTrip / DeliveryTripItem` は運搬計画と運搬実績 +- 新設する `SpreadingSession` 系は散布実績 +- 新設する `WorkRecord` 系は日付ベースの作業記録 + +### 3.2 日付中心設計 + +作業記録は「年度中心」ではなく「日付中心」で扱う。 +年度は `date` から導出できる補助情報とし、画面の年フィルタや施肥計画との照合に使う。 + +### 3.3 散布元の考え方 + +散布元は「特定の運搬回の順番」ではなく、以下を満たす運搬済み明細の集合とする。 + +- `DeliveryTrip.date` が入っている +- まだ未散布残がある +- 年度が一致する + +つまり、散布画面では「その年度の運搬済み・未散布残あり」の明細を一覧化し、その中から対象圃場を選んで散布する。 + +### 3.4 差異の扱い + +運搬数量と実散布数量が一致しないことは許容する。 +ただし整合性のため、以下の扱いにする。 + +- 実散布数量は運搬明細から初期値セットする +- ユーザーは実散布数量を編集できる +- 実散布数量が運搬済み残を超える場合は、そのまま黙って保存せず、差異として明示する +- MVP では「運搬実績を先に修正してから再保存」を推奨フローとする + +--- + +## 4. 機能スコープ + +### 4.1 IN + +- 施肥計画一覧から「散布確定」ボタンを外す +- 散布実績を日付単位で新設する +- 散布実績は運搬済み明細を元に、全部または一部の圃場を対象に作成できる +- 散布時に実績袋数を変更できる +- 運搬日から作業記録を自動生成する +- 散布日から作業記録を自動生成する +- 在庫使用実績は散布実績保存時に作成する +- 施肥計画一覧に「未散布 / 一部散布 / 完了」などの進捗状態を表示する + +### 4.2 OUT + +- 肥料の置き場所管理 +- 残肥の返却・再入庫 +- ルート最適化 +- 複数人同時編集のロック制御 +- 汎用的な作業日誌機能の完成版 UI + +--- + +## 5. 新しい業務フロー + +### 5.1 施肥計画 + +1. 施肥計画を作成・更新する +2. 従来どおり `RESERVE` を張る +3. ここでは散布確定しない + +### 5.2 運搬計画 + +1. 年度の施肥計画から運搬計画を作る +2. 各 `DeliveryTrip` に日付を入れる +3. 日付が入った `DeliveryTrip` は「運搬実績あり」と見なす +4. `DeliveryTrip` ごとに作業記録を自動生成する + +### 5.3 散布実績 + +1. ユーザーは散布日を選ぶ +2. その年度の「運搬済みかつ未散布残あり」の明細を候補表示する +3. 候補の中から、全部または一部の圃場を選ぶ +4. 初期値として未散布残袋数を入れる +5. 実際の散布袋数へ修正して保存する +6. 保存時に `USE` 在庫履歴を散布日で作る +7. 散布日の作業記録を自動生成する + +--- + +## 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` +- `spread_started_at` +- `spread_completed_at` + +これらは serializer で計算する実装を推奨する。 + +### 6.2 `DeliveryTripItem` + +現在は `field + fertilizer + bags` のみを持つが、散布実績との照合性を上げるため、内部参照を追加する。 + +### 追加推奨フィールド + +- `fertilization_entry`: `FK(FertilizationEntry, null=True, on_delete=SET_NULL)` + +### 目的 + +- どの施肥計画エントリ由来かを内部的に追跡する +- 施肥計画進捗の自動集計をしやすくする +- 将来の差異分析をしやすくする + +※ UI 上は引き続き「年度横断で選ぶ」挙動のままでよい。 + +### 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 | | + +### 制約 + +- MVP では `unique_together = [['year', 'date']]` を推奨 + +同日に複数の散布イベントを分けたい場合は、将来 `sequence` を追加して拡張する。 + +### 6.4 `SpreadingSessionItem`(新規) + +散布日の圃場×肥料ごとの実績。 + +| フィールド | 型 | 制約 | 説明 | +|---|---|---|---| +| 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 | 初期表示に使った運搬残袋数 | +| created_at / updated_at | datetime | auto | | + +### 制約 + +- `unique_together = [['session', 'field', 'fertilizer']]` + +### 6.5 `SpreadingAllocation`(新規) + +どの運搬明細残からどれだけ散布に充当したかを持つ中間テーブル。 + +| フィールド | 型 | 制約 | 説明 | +|---|---|---|---| +| id | int | PK | | +| spreading_item | FK(SpreadingSessionItem) | CASCADE | | +| delivery_trip_item | FK(DeliveryTripItem) | PROTECT | | +| allocated_bags | Decimal(10,4) | required | 充当袋数 | + +### 目的 + +- 運搬順ではなく、運搬済み明細全体から散布したことを追跡する +- どの運搬明細に未散布残がどれだけあるかを算出できる +- 後から「どの便で運んだ肥料をいつ散布したか」を確認できる + +### 6.6 `WorkRecord`(新規) + +日付単位で自動生成される作業記録。 + +MVP では汎用化しすぎず、今回必要な 2 種類に絞る。 + +| フィールド | 型 | 制約 | 説明 | +|---|---|---|---| +| id | int | PK | | +| work_date | DateField | required | 作業日 | +| work_type | CharField | required | `fertilizer_delivery` / `fertilizer_spreading` | +| title | varchar(200) | required | 表示名 | +| auto_created | bool | default=True | 自動生成フラグ | +| delivery_trip | OneToOne FK(DeliveryTrip) | nullable | 運搬由来の場合 | +| spreading_session | OneToOne FK(SpreadingSession) | nullable | 散布由来の場合 | +| created_at / updated_at | datetime | auto | | + +### 方針 + +- 作業記録の明細は二重保持しない +- 画面表示時は元の `DeliveryTrip` / `SpreadingSession` を参照して詳細を出す + +--- + +## 7. 在庫連携の変更 + +### 7.1 変更前 + +- 施肥計画保存時に `RESERVE` +- 施肥計画の「散布確定」で `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)` + +### 備考 + +`fertilization_plan` だけでは「どの圃場・どの散布日で使ったか」が追えないため、散布実績由来の FK を追加する。 + +--- + +## 8. API 変更方針 + +### 8.1 施肥計画 API + +### 廃止対象 + +- `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` + +### 8.2 運搬計画 API + +### 既存の POST / PUT + +`DeliveryTrip.date` の保存時に、対応する `WorkRecord` を upsert する。 + +### 詳細レスポンスへの追加推奨 + +- 各 `DeliveryTripItem` に `spread_bags` +- 各 `DeliveryTripItem` に `remaining_bags` +- 各 `DeliveryTrip` に `work_record_id` + +### 8.3 散布実績 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}&date={date}` | 散布候補一覧 | + +### 候補一覧レスポンスイメージ + +```json +[ + { + "field": 5, + "field_name": "田中上", + "fertilizer": 1, + "fertilizer_name": "電気炉さい", + "planned_bags": "4.0000", + "delivered_bags": "4.0000", + "already_spread_bags": "1.5000", + "remaining_bags": "2.5000", + "source_trip_item_ids": [12, 18] + } +] +``` + +### 散布作成リクエストイメージ + +```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" + } + ] +} +``` + +### サーバー側の割当ルール + +- 同一 `field + fertilizer` の候補運搬明細に対して自動割当する +- 割当順は `trip.date asc, trip.id asc, item.id asc` の固定順を推奨 +- UI では「何回目」ではなく「残量」を見せる + +--- + +## 9. フロントエンド変更方針 + +### 9.1 施肥計画一覧 `/fertilizer` + +### 変更点 + +- 「散布確定」ボタンを廃止 +- `ConfirmSpreadingModal.tsx` は削除対象 +- 各計画カードに進捗状態を表示 + +### 表示例 + +- `未散布` +- `一部散布 3.5 / 8.0袋` +- `散布完了` +- `計画超過` + +### 9.2 運搬計画編集 `/distribution/new` `/distribution/[id]/edit` + +### 追加する変更 + +- `DeliveryTripItem.bags` を画面上で直接編集できるようにする +- 各明細に `残り未散布` を表示する +- `date` 入力済みの回は「運搬実績あり」表示にする + +### 理由 + +現状 UI では回の間の移動はできるが、袋数の直接編集ができない。 +今回の業務に合わせるなら、運搬実績の修正も可能である必要がある。 + +### 9.3 散布実績画面(新規) + +新規ページ例: + +- `/fertilizer/spreading` +- または `/distribution/spreading` + +### 画面要件 + +- 日付を先に選ぶ +- その年度の運搬済み未散布残を一覧表示 +- 圃場ごとにチェックして「今日散布する対象」を選べる +- 初期袋数は残量を自動セット +- 実績袋数は編集可能 +- 保存時に差異があればインライン警告を表示 + +### UI イメージ + +```text +[散布日: 2026-03-17] [年度: 2026] + +未散布残一覧 + 田中上 電気炉さい 計画4.0 運搬残2.5 実績[2.3] + 田中下 電気炉さい 計画2.0 運搬残2.0 実績[2.0] + +[保存] +``` + +### 9.4 作業記録画面(将来 UI) + +今回の実装では、まずは自動生成と API を優先する。 +一覧 UI は最小限でもよいが、少なくとも以下を表示できる形にする。 + +- 日付 +- 作業種別 +- タイトル +- 元データへのリンク + +--- + +## 10. 作業記録自動生成ルール + +### 10.1 運搬 + +### 作成契機 + +- `DeliveryTrip.date` が `null → 日付あり` になったとき +- 既存レコード更新時に日付が変わったとき + +### 作成内容 + +- `work_type = fertilizer_delivery` +- `work_date = DeliveryTrip.date` +- `title = 肥料運搬: {delivery_plan.name} {n}回目` + +### 更新・削除 + +- 日付変更時は `WorkRecord.work_date` も更新 +- 日付を空に戻したら、自動生成レコードを削除 + +### 10.2 散布 + +### 作成契機 + +- `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)` + +--- + +## 11. 移行方針 + +### 11.1 既存の確定済み施肥計画 + +既存の `confirm_spreading` 実装では、圃場別散布実績が永続化されていない。 +`StockTransaction` には `material` 単位の `USE` は残るが、圃場別の再構成は正確にできない。 + +そのため、既存の確定済みデータは以下方針を推奨する。 + +- 既存 `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. 影響ファイル(想定) + +### 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/materials/models.py` +- `backend/apps/materials/stock_service.py` +- `backend/apps/materials/serializers.py` +- `backend/apps/materials/migrations/` +- `backend/apps/workrecords/` または同等の新規 app + +### Frontend + +- `frontend/src/app/fertilizer/page.tsx` +- `frontend/src/app/fertilizer/_components/ConfirmSpreadingModal.tsx` +- `frontend/src/app/distribution/_components/DeliveryEditPage.tsx` +- `frontend/src/types/index.ts` +- 散布実績の新規ページ群 +- 作業記録の新規ページ群または既存画面への表示追加 + +### ドキュメント + +- `document/13_マスタードキュメント_施肥計画編.md` +- `document/14_マスタードキュメント_分配計画編.md` +- `CLAUDE.md` + +※ これらは実装着手時に正式仕様へ反映する。 + +--- + +## 13. 受け入れ条件 + +- 施肥計画一覧に「散布確定」ボタンが表示されない +- 運搬済み明細を元に、日付単位の散布実績を新規作成できる +- 散布対象は、運搬計画全体から全部または一部の圃場を選べる +- 運搬回の順番に依存せず散布できる +- 散布時に実際袋数を変更できる +- 散布保存時に `USE` 在庫履歴が散布日で作成される +- `DeliveryTrip.date` 保存時に運搬作業記録が自動生成される +- `SpreadingSession.date` 保存時に散布作業記録が自動生成される +- 施肥計画一覧で未散布・一部散布・完了が分かる + +--- + +## 14. 前提と留意点 + +- 本仕様では「残肥の返却」は扱わない。未散布残は運搬済み残として次回散布へ繰り越す +- 同日複数散布の厳密な分離は MVP では不要とみなし、まずは 1日1レコードを採用する +- 既存確定済みデータの完全移行は行わない +- 作業記録 UI は最小構成でよいが、API と自動生成ロジックは今回入れる