Compare commits

..

3 Commits

Author SHA1 Message Date
Akira
edd2f2a274 現状でコミット 2026-03-27 14:59:25 +09:00
Akira
00fd4a8cba 削除: データモデル詳細(145行)→ 「document/03_データ仕様書.md を参照」に集約
移動: 実装状況・既知の課題・次のマイルストーン → TASK_CONTEXT.md へ
削除: 更新履歴(git log で追える)
圧縮: ディレクトリ構造、トラブルシューティング、作業パターンを要約
維持: 絶対制約、コーディング規約、デプロイコマンド、マスタードキュメントへのリンク
2026-03-18 09:25:44 +09:00
Akira
13c21ed7de ローカル更新済み:
13_マスタードキュメント_施肥計画編.md — 散布実績セクション整備、在庫連携・集計ルール・WorkRecord自動生成・前年度コピーのセクション追加、旧「散布確定モーダル」記述削除、型定義・ファイル構成・将来の拡張を更新
14_マスタードキュメント_分配計画編.md — 散布実績との連携・WorkRecord自動生成のセクション追加
CLAUDE.md — データモデル(SpreadingSession/Item, WorkRecord, actual_bags)追加、プロジェクト構造にfertilizer/workrecordsアプリ追加、実装状況に散布実績・作業記録索引を追記、更新履歴に2026-03-17エントリ追加
2026-03-17 20:31:22 +09:00
9 changed files with 487 additions and 537 deletions

View File

@@ -67,7 +67,10 @@
"Bash(git diff:*)", "Bash(git diff:*)",
"mcp__serena__find_symbol", "mcp__serena__find_symbol",
"mcp__serena__get_symbols_overview", "mcp__serena__get_symbols_overview",
"Bash(git status:*)" "Bash(git status:*)",
"Bash(npx next:*)",
"mcp__butler__butler__list_skills",
"mcp__butler__butler__get_skill_usage"
], ],
"additionalDirectories": [ "additionalDirectories": [
"C:\\Users\\akira\\AppData\\Local\\Temp", "C:\\Users\\akira\\AppData\\Local\\Temp",

View File

@@ -133,3 +133,17 @@ symbol_info_budget:
# list of regex patterns which, when matched, mark a memory entry as readonly. # list of regex patterns which, when matched, mark a memory entry as readonly.
# Extends the list from the global configuration, merging the two lists. # Extends the list from the global configuration, merging the two lists.
read_only_memory_patterns: [] read_only_memory_patterns: []
# list of regex patterns for memories to completely ignore.
# Matching memories will not appear in list_memories or activate_project output
# and cannot be accessed via read_memory or write_memory.
# To access ignored memory files, use the read_file tool on the raw file path.
# Extends the list from the global configuration, merging the two lists.
# Example: ["_archive/.*", "_episodes/.*"]
ignored_memory_patterns: []
# advanced configuration option allowing to configure language server-specific options.
# Maps the language key to the options.
# Have a look at the docstring of the constructors of the LS implementations within solidlsp (e.g., for C# or PHP) to see which options are available.
# No documentation on options means no options are available.
ls_specific_settings: {}

569
CLAUDE.md
View File

@@ -1,540 +1,125 @@
# Keina System - Claude 向けガイド # Keina System - Claude 向けガイド
> **最終更新**: 2026-03-16 ## プロジェクト概要
> **現在のフェーズ**: Phase 1 (MVP) - 気象データ基盤を追加
## 📌 このファイルの目的
このファイルは、Claude が新しいセッションを開始する際に最初に読むべきドキュメントです。
プロジェクト全体の構造、重要な設計判断、現在の状態を把握するための情報を集約しています。
## ⚠️ Claude への重要な指示
**このファイルは、セッションごとに必ず最初に読んでください。**
さらに、以下のルールを厳守してください:
### 📝 更新義務
**ドキュメントドリブンの徹底**
- ✅ 仕様に変更がある時は、まず関連するドキュメントから更新する事。
**機能追加・変更時は、必ずこのファイルを更新すること。**
- ✅ 新機能実装時 → 「実装状況」セクションを更新
- ✅ データモデル変更時 → 「データモデル概要」を更新
- ✅ 重要な設計判断時 → 「重要な制約・ルール」に追記
- ✅ 新作業パターン確立時 → 「よくある作業パターン」に追加
- ✅ 問題解決時 → 「トラブルシューティング」に追加
- ✅ 更新時は必ず「更新履歴」セクションに記録
**更新を忘れると、次のセッションで情報が失われます。これは最優先事項です。**
---
## 🎯 プロジェクト概要30秒で理解
**何を作っているか:**
農業生産者向けの作付け計画管理システム。圃場管理、作付け計画、申請書自動生成を行う。 農業生産者向けの作付け計画管理システム。圃場管理、作付け計画、申請書自動生成を行う。
ユーザーは65歳の農家元プログラマー、シングルユーザー、39筆の圃場を管理。
**ユーザー:** **技術スタック:** Django 5.2 + DRF + PostGIS / Next.js 14 (App Router) + TypeScript + Tailwind / PostgreSQL 16 + PostGIS 3.4
65歳の農家元プログラマー、シングルユーザー、39筆の圃場を管理
**技術スタック:** **開発方針:** シンプルさ最優先、段階的な機能追加、過度な複雑化を避ける
- Backend: Django 5.2 + DRF + PostGIS
- Frontend: Next.js 14 (App Router) + TypeScript + Tailwind CSS
- Database: PostgreSQL 16 + PostGIS 3.4
**開発方針:**
シンプルさ最優先、段階的な機能追加、過度な複雑化を避ける
--- ---
## 📂 プロジェクト構造 ## 絶対に守るべき制約
1. **Field ↔ OfficialKyosaiField / OfficialChusankanField は M:N** — 決してFK (1:N) に戻さない
2. **年度+圃場の組み合わせは1つの Plan のみ** (`unique_together`)
3. **面積**: 表示=反(tan)、計算・保存=m2、変換: 1反=1000m2
4. **FertilizationEntry.fertilizer は PROTECT** — 使用中の肥料は削除不可
5. **3回同じコードを書くまでは抽象化しない**
6. **ドキュメントドリブン**: 仕様変更時はまず関連ドキュメントから更新する
## コーディング規約
- **Backend**: Django ベストプラクティス、日本語フィールドは `verbose_name` で対応
- **Frontend**: TypeScript strict mode、ESLint に従う
- **API**: REST原則、エンドポイントは複数形 (`/api/fields/`, `/api/plans/`)
---
## プロジェクト構造
``` ```
keinasystem_t02/ keinasystem_t02/
├── CLAUDE.md # このファイルClaude向けガイド ├── CLAUDE.md # このファイル
├── .cursor/ ├── TASK_CONTEXT.md # 実装状況・課題・次のマイルストーン
│ └── rules/ ├── document/ # 設計書・マスタードキュメント
│ └── 30_Cursorガイド.md # Cursor専用ガイド
├── document/ # 詳細設計書(人間向け)
│ ├── 00_Gemini向け統合指示書.md # 全体像の詳細
│ ├── 01_プロダクトビジョン.md
│ ├── 02_ユーザーストーリー.md
│ ├── 03_データ仕様書.md
│ ├── 04_画面設計書.md
│ └── 05_実装優先順位.md
├── backend/ ├── backend/
│ ├── keinasystem/ # Django設定 │ ├── keinasystem/ # Django設定 (settings.py, urls.py)
│ │ ├── settings.py # 重要: CORS, JWT, DB設定
│ │ └── urls.py # ルートURL設定
│ └── apps/ │ └── apps/
│ ├── fields/ # 圃場管理アプリ │ ├── fields/ # 圃場管理Field, OfficialKyosaiField, OfficialChusankanField
│ ├── models.py # Field, OfficialKyosaiField, OfficialChusankanField ├── plans/ # 作付け計画Plan, Crop, Variety
│ ├── views.py # インポート機能、CRUD API ├── weather/ # 気象データWeatherRecord
│ └── urls.py ├── reports/ # 申請書PDF生成
│ ├── plans/ # 作付け計画アプリ │ ├── fertilizer/ # 施肥計画・散布実績・運搬計画
│ ├── models.py # Plan, Crop(+base_temp), Variety ├── workrecords/ # 作業記録索引
│ └── views.py # 作付け計画API、集計API └── mail/ # メールフィルタリングWindmill連携
│ ├── weather/ # 気象データアプリ └── frontend/src/app/
├── models.py # WeatherRecord (1日1行) ├── allocation/ # 作付け計画編集(メイン画面)
├── views.py # sync(APIキー), records, summary, gdd, similarity ├── fields/ # 圃場一覧・詳細
│ ├── urls.py ├── fertilizer/ # 施肥計画・散布実績
│ └── management/commands/fetch_weather.py # 初回一括取得・差分取得 ├── distribution/ # 運搬計画
── reports/ # 申請書生成アプリ ── weather/ # 気象データ
├── views.py # PDF生成API ├── reports/ # 申請書DL
└── templates/ # PDF用HTMLテンプレート ├── import/ # データ取込
└── frontend/ ├── mail/ # メール管理
└── src/app/ └── settings/ # パスワード変更
├── allocation/ # 作付け計画編集画面(メイン)
├── fields/ # 圃場一覧・詳細
├── reports/ # 申請書ダウンロード
├── import/ # データ取込画面
├── mail/
│ ├── feedback/[token]/ # フィードバックページ(認証不要)
│ ├── history/ # メール処理履歴
│ └── rules/ # 送信者ルール管理
├── weather/ # 気象データ画面(年別集計・期間指定・グラフ)
└── settings/
└── password/ # パスワード変更
``` ```
--- ---
## 🗄️ データモデル概要 ## よくある作業パターン
### コアエンティティ
```
Field (実圃場)
├── 39筆の実際の農地
├── area_tan (反), area_m2 (m2) の2つの面積フィールド
├── group_name, display_order (グループ分け・表示順)
└── ManyToMany関係
├── kyosai_fields (共済マスタ、M:N)
└── chusankan_fields (中山間マスタ、M:N)
OfficialKyosaiField (共済マスタ)
└── 31区画水稲共済細目書用
OfficialChusankanField (中山間マスタ)
├── 71区画中山間地域等直接支払交付金用
└── 17フィールド: c_id, chusankan_flag, oaza, aza, chiban,
branch_num, land_type, area, planting_area,
original_crop, manager, owner, slope,
base_amount, steep_slope_addition, smart_agri_addition,
payment_amount
Plan (作付け計画)
├── field (FK to Field)
├── year (年度)
├── crop (FK to Crop)
├── variety (FK to Variety, nullable)
└── unique_together = ['field', 'year']
Crop (作物マスタ)
├── name米、トウモロコシ、エンドウ、野菜、その他
└── base_temp (有効積算温度 基準温度℃、default=0.0) ← 2026-02-28 追加
Variety (品種マスタ)
├── crop (FK to Crop)
├── name (品種名)
└── unique_together = ['crop', 'name']
MailSender (送信者ルール)
├── email (EmailField, nullable)
├── domain (CharField, nullable)
├── rule ('never_notify' | 'always_notify')
└── ConstraintCheck: email/domain どちらか一方のみ
MailEmail (受信メール記録)
├── account (gmail / gmail_service / hotmail / xserver1〜xserver6、旧データxserver)
├── message_id (unique)
├── sender_email, sender_domain
├── subject, body_preview
├── received_at, llm_verdict (important/not_important)
├── notified_at (LINE通知日時、nullable)
└── feedback (important/not_important/never_notify/always_notify, nullable)
MailNotificationToken (フィードバックURL用トークン)
├── email (OneToOne FK to MailEmail)
└── token (UUID, unique)
WeatherRecord (日次気象記録)
├── date (DateField, unique)
├── temp_mean, temp_max, temp_min (気温℃)
├── sunshine_h (日照時間h)
├── precip_mm (降水量mm)
├── wind_max (最大風速m/s)
└── pressure_min (最低気圧hPa)
※ 観測地点: 窪川 (lat=33.213, lon=133.133)、データソース: Open-Meteo archive API
※ 2016-01-01 から蓄積(初回は fetch_weather --full で一括投入)
Fertilizer (肥料マスタ)
├── name肥料名、必須・unique
├── makerメーカー、任意
├── capacity_kg1袋重量kg、任意
├── nitrogen_pct / phosphorus_pct / potassium_pct成分%、任意)
└── notes備考、任意
FertilizationPlan (施肥計画)
├── name計画名
├── year年度
└── variety (FK to plans.Variety)
FertilizationEntry (施肥エントリ・中間テーブル)
├── plan (FK to FertilizationPlan, CASCADE)
├── field (FK to fields.Field, CASCADE)
├── fertilizer (FK to Fertilizer, PROTECT) ← 使用中の肥料は削除不可
├── bags袋数、Decimal
└── unique_together = ['plan', 'field', 'fertilizer']
DeliveryPlan (運搬計画) ← 旧 DistributionPlan を置き換え2026-03-16 再設計)
├── year年度← 施肥計画へのFK廃止、年度ベースで全施肥計画を横断
├── name計画名
├── groups → DeliveryGroup
└── trips → DeliveryTrip
DeliveryGroup (配送先グループ)
├── delivery_plan (FK to DeliveryPlan, CASCADE)
├── nameグループ名
├── order表示順
└── unique_together = ['delivery_plan', 'name']
DeliveryGroupField (グループ圃場割り当て)
├── delivery_plan (FK to DeliveryPlan, CASCADE) ← 一意制約用
├── group (FK to DeliveryGroup, CASCADE)
├── field (FK to fields.Field, PROTECT)
└── unique_together = ['delivery_plan', 'field'] ← 1圃場=1グループ/1計画
DeliveryTrip (運搬回)
├── delivery_plan (FK to DeliveryPlan, CASCADE)
├── order何回目
├── name任意の名前
├── date運搬日、nullable、デフォルト=1回目の日付
└── items → DeliveryTripItem
DeliveryTripItem (運搬明細)
├── trip (FK to DeliveryTrip, CASCADE)
├── field (FK to fields.Field, PROTECT)
├── fertilizer (FK to Fertilizer, PROTECT)
├── bags袋数、Decimal
└── unique_together = ['trip', 'field', 'fertilizer']
```
### 重要な設計判断
1. **M:N関係に変更**: 当初はM:1だったが、実運用で「1つの実圃場が複数の申請区画に紐づく」ケースが判明し、ManyToManyに変更マイグレーション0003で実施
2. **面積単位の二重管理**:
- DB内部は `area_m2` (整数) で保存
- 表示用に `area_tan` (反, Decimal) も保持
- 理由: 申請書ではm2、農家の感覚では反
3. **品種は全作物で統一**:
- 「作付けしない」も「その他」作物の品種として扱う
- UI操作を統一するため
4. **グループ機能**:
- `group_name` (エリアや用途によるグループ分け)
- `display_order` (リスト表示時の順序)
- マイグレーション0004で追加
5. **年度管理の設計方針**(⚠️ Phase 2 で必ず参照):
- **作付け計画**: 年度セレクタで独立して来年度も選べる。選んだ年度はlocalStorageに保存して維持
- **過去年度**: 「参照モード」として視覚的に区別(背景色・バナー)
- **Phase 2 の栽培管理・販売管理**: グローバル作業年度を導入し、基本は今年度に従う
- **栽培記録・作業日誌**: 日付中心設計、年度は日付から自動算出
- 参考: ソリマチ農業簿記の年度管理方式(明示的に年度を選択、変更するまで固定)
---
## 🔑 重要な制約・ルール
### 絶対に守るべきこと
1. **データの整合性**
- 年度 + 圃場の組み合わせは1つの Plan のみ (`unique_together`)
- 作物 + 品種名の組み合わせは一意 (`unique_together`)
2. **面積の扱い**
- 表示: 反 (tan)
- 計算・保存: m2
- 変換: 1反 = 1000m2 (正確には991.736m2だが、実運用では1000で統一)
3. **M:N関係の重要性**
- Field と OfficialKyosaiField は M:N
- Field と OfficialChusankanField は M:N
- 決して FK (1:N) に戻さない
4. **シンプルさ優先**
- 過度な抽象化を避ける
- 3回同じコードを書くまでは抽象化しない
- ユーザーは1人、パフォーマンス最適化は後回し
### コーディング規約
- **Backend**: Django のベストプラクティスに従う
- **Frontend**: TypeScript strict mode、ESLint に従う
- **API**: REST原則、エンドポイントは複数形 (`/api/fields/`, `/api/plans/`)
- **命名**: 日本語のフィールドは `verbose_name` で対応
---
## 📍 現在の実装状況
### ✅ 実装済みPhase 1 - MVP
1. **認証**: JWT認証アクセストークン24h、リフレッシュトークン7日
2. **圃場管理**:
- CRUD API (`/api/fields/`)
- ODS/Excelインポート (`/api/fields/import/`)
- グループ機能マイグレーション0004
3. **作付け計画**:
- 年度別の作付け計画 CRUD (`/api/plans/?year=2025`)
- 前年度コピー機能 (`/api/plans/copy_from_previous_year/`)
- 一括更新 (`/api/plans/bulk_update/`)
- 集計API (`/api/plans/summary/?year=2025`)
4. **申請書生成**:
- 水稲共済細目書 PDF (`/api/reports/kyosai/?year=2025`)
- 中山間交付金 PDF (`/api/reports/chusankan/?year=2025`)
5. **フロントエンド**:
- 作付け計画編集画面(集計サイドバー付き)
- 圃場一覧・詳細・新規作成
- データ取込画面
- 申請書ダウンロード画面
- ダッシュボード画面(概要サマリー、作物別集計、クイックアクセス)
6. **対応付け可視化・紐づけ管理** (E-2):
- 圃場一覧「対応表」モード(共済漢字地名・中山間所在地の一覧表示、直接紐づけ追加・解除)
- 圃場詳細画面の共済/中山間リンク管理(+追加、×解除、面積参考表示)
- 共通 LinkModal コンポーネント
7. **メールフィルタリング機能**Windmill連携:
- Django `apps/mail` アプリMailSender, MailEmail, MailNotificationToken
- Windmill向けAPIAPIキー認証: `GET /api/mail/sender-rule/`, `GET /api/mail/sender-context/`, `POST /api/mail/emails/`, `GET /api/mail/stats/`
- フィードバックAPI認証不要・UUIDトークン: `GET/POST /api/mail/feedback/<token>/`
- ルール管理APIJWT認証: `GET/POST/DELETE /api/mail/senders/`, `PATCH /api/mail/emails/<pk>/feedback/`
- フィードバックページ: `/mail/feedback/[token]`LINEからタップ一発、認証不要
- ルール管理ページ: `/mail/rules/`
- 処理履歴ページ: `/mail/history/`
- 対応アカウント: Gmail × 2、Xserver × 6本番稼働中、`account``xserver1``xserver6` で識別)
- Windmill フロー: `f/mail/mail_filter`(本番: windmill.keinafarm.net にデプロイ済み、10分間隔スケジュール
- To: ヘッダー宛先補正を実装Gmail先行取り込み時も @keinafarm.com 宛は xserver1〜xserver6 として記録/通知)
- マスタードキュメント: `document/11_マスタードキュメント_メール通知関連編.md`
8. **パスワード変更機能**:
- Backend: `POST /api/auth/change-password/`JWT認証、`ChangePasswordView` in `keinasystem/urls.py`
- Frontend: `/settings/password` ページ
- Navbar: KeyRound アイコンボタン(ログアウトボタンの左隣)
9. **気象データ基盤**Windmill連携:
- Django `apps/weather` アプリWeatherRecord: 1日1行、2016-01-01〜
- データソース: Open-Meteo archive API窪川 lat=33.213, lon=133.133
- Windmill向けAPIAPIキー認証: `POST /api/weather/sync/`upsert、単一/リスト両対応)
- フロントエンド向けAPIJWT認証:
- `GET /api/weather/records/?year=&start=&end=` 日次レコード一覧
- `GET /api/weather/summary/?year=` 月別・年間サマリー(猛暑日・冬日数含む)
- `GET /api/weather/gdd/?start_date=&base_temp=&end_date=` 有効積算温度GDD
- `GET /api/weather/similarity/?year=` 類似年分析(月別パターン比較)
- 管理コマンド: `python manage.py fetch_weather [--full] [--start-date] [--end-date]`
- Windmill フロー: `f/weather/weather_sync`本番稼働中、毎朝6時 Asia/Tokyo
- `Crop.base_temp`GDD計算の基準温度、default=0.0℃をCropモデルに追加
- **初回データ投入**: `docker compose exec backend python manage.py fetch_weather --full`
- フロントエンド `/weather` 画面(年別集計・期間指定 モード、グラフは Recharts
- **将来計画**: 開花・収穫予測品種ごとの目標GDD設定 → 到達日予測)
- マスタードキュメント: `document/12_マスタードキュメント_気象データ編.md`
10. **施肥計画機能**(本番稼働中):
- Django `apps/fertilizer` アプリFertilizer, FertilizationPlan, FertilizationEntry
- API: `/api/fertilizer/fertilizers/`, `/api/fertilizer/plans/`, `/api/fertilizer/calculate/`, `/api/fertilizer/candidate_fields/`
- PDF出力: `/api/fertilizer/plans/{id}/pdf/`WeasyPrint、A4横向き
- FertilizationEntry.fertilizer は PROTECT使用中の肥料は削除不可・migration 0002
- 自動計算3方式: per_tan反当袋数/ even均等配分/ nitrogen反当チッソ
- 四捨五入トグル: `≈`(丸め)/ `↩`(元の計算値に戻す)
- フロントエンド: `/fertilizer`(一覧)、`/fertilizer/masters`(肥料マスタ)、`/fertilizer/new``/fertilizer/[id]/edit`(編集)
- 施肥機能全体で alert/confirm を廃止し、React インラインバナーでエラー表示
- マスタードキュメント: `document/13_マスタードキュメント_施肥計画編.md`
10. **施肥計画機能**:
- Django `apps/fertilizer` アプリFertilizer, FertilizationPlan, FertilizationEntry
- APIJWT認証: `GET/POST /api/fertilizer/fertilizers/`, `GET/POST /api/fertilizer/plans/?year=`, `GET /api/fertilizer/plans/{id}/pdf/`, `GET /api/fertilizer/candidate_fields/?year=&variety_id=`, `POST /api/fertilizer/calculate/`
- 自動計算3方式: 反当袋数(per_tan)、均等配分(even)、反当チッソ(nitrogen)
- フロントエンド: `/fertilizer/`(一覧), `/fertilizer/new``/fertilizer/[id]/edit`(編集・マトリクス表), `/fertilizer/masters/`(肥料マスタ)
- スコープ外(将来): 購入管理
11. **運搬計画機能**(旧・分配計画、本番稼働中):
- 旧 DistributionPlan/Group/GroupField → 新 DeliveryPlan/Group/GroupField/Trip/TripItem に移行
- 施肥計画への直接FK廃止 → 年度ベースで全施肥計画を横断
- 「軽トラ1回分」を基本単位とする運搬回DeliveryTripを追加
- 運搬明細DeliveryTripItemで圃場×肥料単位の袋数を管理
- 運搬回ごとの日付記録(作業記録としても機能)
- グループ一括割り当て・グループ単位の回間移動・未割り当て戻し
- APIJWT認証: `/api/fertilizer/delivery/` 配下
- PDF出力A4横向き・回ごとに1ページ
- フロントエンド: `/distribution/`(一覧・編集)
- マスタードキュメント: `document/14_マスタードキュメント_分配計画編.md`
### 🚧 既知の課題・技術的負債
1. **認証周り**: ログアウト処理が未実装(トークン破棄のみ)
2. **エラーハンドリング**: フロントエンドでの統一的なエラー表示が未実装
3. **テスト**: 自動テストが未実装Phase 2で追加予定
4. **パフォーマンス**: N+1問題が一部存在現状は問題ないが、データ増加時に対応必要
### 🔜 次の実装タスク(優先順)
差異レポートの全タスクA-1〜A-8, B-1〜B-5, C-1〜C-8, D-1〜D-4, E-1〜E-2は全件完了。
Phase 2 のタスクに進む段階。
詳細は `document/06_ドキュメントvs実装_差異レポート.md` を参照
### 📅 次のマイルストーンPhase 2
- 栽培履歴管理(播種日、農薬・肥料の散布記録)
- 作業予定のカレンダー表示
- モバイル対応の改善(スマホでの記録入力)
---
## 🛠️ よくある作業パターン
### 新しいモデルを追加する場合 ### 新しいモデルを追加する場合
1. `apps/<app_name>/models.py` にモデルクラスを追加 1. `apps/<app>/models.py` → 2. `makemigrations` → 3. `migrate` → 4. `admin.py` 登録
2. `python manage.py makemigrations` 5. Serializer → 6. ViewSet → 7. URL登録
3. `python manage.py migrate`
4. `apps/<app_name>/admin.py` に登録(管理画面で確認するため)
5. Serializer 作成 (`apps/<app_name>/serializers.py`)
6. ViewSet 作成 (`apps/<app_name>/views.py`)
7. URL登録 (`apps/<app_name>/urls.py`)
### 新しいAPI エンドポイントを追加する場合 ### 新しいAPI / 画面を追加する場合
1. `apps/<app_name>/views.py` にビューを追加 - API: `views.py``urls.py` → フロントの型定義 (`lib/types.ts`) → API呼び出し
2. `apps/<app_name>/urls.py` にパスを追加 - 画面: `frontend/src/app/<page>/page.tsx` → ローディング/エラー状態を処理
3. フロントエンドで型定義 (`frontend/src/lib/types.ts`)
4. API呼び出し関数作成 (`frontend/src/lib/api.ts` または直接fetch)
### 新しい画面を追加する場合
1. `frontend/src/app/<page_name>/page.tsx` を作成
2. 必要に応じてレイアウト調整 (`layout.tsx`)
3. API呼び出しは `useEffect` + `fetch` で実装
4. ローディング状態、エラー状態を適切に処理
--- ---
## 🔍 トラブルシューティング ## デプロイ・トラブルシューティング
### 本番デプロイコマンド(必須)
```bash ```bash
# deploy.sh で git pull → down → build → up -d を一括実行 # 本番デプロイ(git pull → build → up -d を一括実行
ssh keinafarm-claude 'sudo -u keinasystem bash /home/keinasystem/keinasystem_t02/deploy.sh' ssh keinafarm-claude 'sudo -u keinasystem bash /home/keinasystem/keinasystem_t02/deploy.sh'
```
**Docker Compose 構成:** # 本番ヘルスチェック9項目、curlベース
- `docker-compose.yml` = 本番用Traefik連携、gunicorn、prod Dockerfile
- `docker-compose.develop.yml` = 開発用ホットリロード、DEBUG=True
- 本番サーバー: `.env``.env.production` シンボリックリンク
- `deploy.sh` = 本番デプロイ、`develop.bat` = ローカル開発起動
### 本番確認手順(デプロイ後の必須チェック)
**⚠️ Playwrightビジュアルテストを使う前に、必ずcurlで先に確認すること。**
curlはキャッシュの影響を受けず、偽装不可能な確認手段。
```bash
# ステップ1: curlヘルスチェック全9項目、所要約10秒
bash scripts/check_prod.sh claude keina1234 bash scripts/check_prod.sh claude keina1234
# → 全 9 項目 PASS が出れば本番が正常稼働中
# ステップ2任意: Playwrightでビジュアル確認する場合のプロンプト原則 # 本番マイグレーション(バックエンド変更時のみ)
# - 「認証できなければ即中止して報告せよ」を必ず明記
# - 「スクリーンショットには今日の日付が画面内に見えること」を要求
# - 「成功の証跡HTTP レスポンスの実テキスト)を必ず添付すること」を要求
```
**本番バックエンドのマイグレーション適用(バックエンド変更時のみ):**
```bash
ssh keinafarm-claude 'cd /home/keinasystem/keinasystem_t02 && \ ssh keinafarm-claude 'cd /home/keinasystem/keinasystem_t02 && \
sudo -u keinasystem docker compose build backend && \ sudo -u keinasystem docker compose build backend && \
sudo -u keinasystem docker compose up -d && \ sudo -u keinasystem docker compose up -d && sleep 5 && \
sleep 5 && \
sudo -u keinasystem docker compose exec backend python manage.py migrate' sudo -u keinasystem docker compose exec backend python manage.py migrate'
``` ```
### マイグレーションエラー - **Docker Compose**: `docker-compose.yml`=本番、`docker-compose.develop.yml`=開発
- **CORS**: `settings.py``CORS_ALLOWED_ORIGINS`localhost:3000 許可済み)
```bash - **JWT**: アクセストークン24h、リフレッシュ: `/api/auth/jwt/refresh/`
# マイグレーションをリセット(開発環境のみ!)
docker-compose exec backend python manage.py migrate <app_name> zero
docker-compose exec backend python manage.py makemigrations
docker-compose exec backend python manage.py migrate
```
### CORS エラー
- `backend/keinasystem/settings.py``CORS_ALLOWED_ORIGINS` を確認
- 現在は `http://localhost:3000``http://127.0.0.1:3000` を許可
### JWT トークンエラー
- トークンの有効期限を確認(アクセストークン: 24時間
- リフレッシュトークンを使って更新(エンドポイント: `/api/auth/jwt/refresh/`
### PDF 生成エラー
- WeasyPrint のインストールを確認
- 日本語フォントの設定を確認HTMLテンプレートのCSS
--- ---
## 📚 詳細情報へのリンク ## マスタードキュメント(機能別リファレンス)
### マスタードキュメント(機能別の網羅的リファレンス) 特定機能の詳細を知りたい場合、**まずマスタードキュメントを参照**すること。
データモデル・API仕様・画面仕様がソースコード参照不要なレベルで記載されている。
**特定機能の実装詳細を知りたい場合、まずマスタードキュメントを参照すること。** | 機能 | ドキュメント |
マスタードキュメントにはデータモデル・API仕様・画面仕様・インポート/エクスポート仕様が |------|------------|
ソースコード参照不要なレベルで記載されている。ソース確認が必要な場合もファイル名と行番号の索引がある。 | 圃場管理 | `document/10_マスタードキュメント_圃場管理編.md` |
| メール通知 | `document/11_マスタードキュメント_メール通知関連編.md` |
- **圃場管理機能**: `document/10_マスタードキュメント_圃場管理編.md` | 気象データ | `document/12_マスタードキュメント_気象データ編.md` |
- **メール通知機能**: `document/11_マスタードキュメント_メール通知関連編.md` | 施肥計画 | `document/13_マスタードキュメント_施肥計画編.md` |
- **気象データ機能**: `document/12_マスタードキュメント_気象データ編.md` | 運搬計画 | `document/14_マスタードキュメント_分配計画編.md` |
- **施肥計画機能**: `document/13_マスタードキュメント_施肥計画編.md` | データモデル全体 | `document/03_データ仕様書.md` |
- **運搬計画機能(旧・分配計画)**: `document/14_マスタードキュメント_分配計画編.md`
### 設計ドキュメント(プロジェクト横断)
- **プロジェクトの背景・目的**: `document/01_プロダクトビジョン.md`
- **機能要求・ユーザーストーリー**: `document/02_ユーザーストーリー.md`
- **データモデル詳細**: `document/03_データ仕様書.md`
- **画面設計**: `document/04_画面設計書.md`
- **実装手順**: `document/00_Gemini向け統合指示書.md`
- **差異レポート・タスク一覧**: `document/06_ドキュメントvs実装_差異レポート.md`
--- ---
## 💡 新しいセッションでの推奨フロー ## セッション開始・終了フロー
### 開始時
1. この `CLAUDE.md` を読む 1. この `CLAUDE.md` を読む
2. タスク対象の機能に対応する**マスタードキュメント**を読む(例: 圃場関連 → `document/10_マスタードキュメント_圃場管理編.md` 2. `HANDOVER.md` で前回の引き継ぎを確認する
3. マスタードキュメントで不足する場合のみ、ソースコードや他のドキュメントを参照 3. `TASK_CONTEXT.md` で現在の状況を把握する
4. 実装・修正を行う 4. タスク対象の**マスタードキュメント**を読む
5. 重要な設計判断があれば、この `CLAUDE.md` と該当マスタードキュメントを更新
---
## 📝 更新履歴
- 2026-03-16: 分配計画を「運搬計画」に再設計・本番稼働。実運用のワークフロー軽トラ複数回・複数施肥計画混在・肥料指定に合わせ、DeliveryPlan/Trip/TripItem モデルへ移行。施肥計画へのFK廃止→年度ベース。グループ一括割り当て・グループ単位の回間移動機能を追加。マスタードキュメント14を全面改訂
- 2026-03-05: メール通知機能を更新。MailEmail.account を xserver1〜xserver6 で識別可能に変更。Windmill mail_filter に To ヘッダー宛先補正を追加し、Gmail先行取り込みでも Xserver 宛先ラベルが崩れないよう修正。マスタードキュメント/仕様書を同期。
- 2026-02-28: Cursor連携を廃止。Claude Code 単独運用に変更。`document/20_Cursor_Claude連携ガイド.md` を削除
- 2026-03-02: 分配計画機能を実装。`apps/fertilizer` に DistributionPlan/DistributionGroup/DistributionGroupField 追加、API `/api/fertilizer/distribution/`、PDF出力A4横・グループ★行圃場サブ行、フロントエンド `/distribution/`。マスタードキュメント `document/14_マスタードキュメント_分配計画編.md` 追加
- 2026-03-01: 施肥計画機能を実装・本番稼働。`apps/fertilizer`Fertilizer, FertilizationPlan, FertilizationEntry, 自動計算3方式, PDF出力, PROTECT migration 0002、フロントエンド `/fertilizer/`(一覧・編集・肥料マスタ)。施肥機能全体で alert/confirm 廃止・インラインバナーに統一。マスタードキュメント `document/13_マスタードキュメント_施肥計画編.md` 追加
- 2026-02-28: 気象データ機能を実装・本番稼働。`apps/weather`WeatherRecord, 5 API、Windmill `f/weather/weather_sync`毎朝6時、フロントエンド `/weather`年別集計・期間指定・Rechartsグラフ`Crop.base_temp` 追加。デプロイコマンドの本番パス修正(/home/keinasystem/)。マスタードキュメント `document/12_マスタードキュメント_気象データ編.md` 追加
- 2026-02-25: CLAUDE.md更新。パスワード変更機能追記。メールフィルタリング機能を本番稼働済みに更新。マスタードキュメント `document/11_マスタードキュメント_メール通知関連編.md` リンク追加。デプロイコマンド(`--env-file .env.production` 必須)をトラブルシューティングに追加
- 2026-02-22: メールフィルタリング機能を実装。`apps/mail` Django app、Windmill向けAPIAPIキー認証、フィードバックページ、ルール管理ページを追加。仕様書: `document/メールフィルタ/mail_filter_spec.md`
- 2026-02-21: マスタードキュメント体系を導入。`document/10_マスタードキュメント_圃場管理編.md` を追加。セッション推奨フローにマスタードキュメント参照を追加
- 2026-02-18: E-2対応付け可視化・紐づけ管理仕様追加。画面設計書・差異レポート・次タスク一覧を更新。完了済みタスク(A-8, D-1〜D-4, E-1)を既知の課題から除外
- 2026-02-17: ドキュメント一斉更新差異レポートA〜E反映、CSV→PDF統一、M:N関係、中山間モデル17列化、インライン編集方式、Navbar追加、既知の課題・次タスク一覧追加
- 2026-02-16: 初版作成(ハイブリッドアプローチの方針決定)
### 終了時(または作業の区切りで必ず実行)
1. `HANDOVER.md` を定型フォーマットで更新する
2. 重要な設計判断があれば `CLAUDE.md` と該当マスタードキュメントを更新
3. 実装状況に変化があれば `TASK_CONTEXT.md` を更新

34
HANDOVER.md Normal file
View File

@@ -0,0 +1,34 @@
# Goal
Phase 1 全タスク完了後の安定運用。Phase 2 移行準備。
# Done
- 施肥散布実績連携を実装・本番稼働2026-03-17
- 運搬計画を再設計・本番稼働2026-03-16
- CLAUDE.md を120行にスリム化、TASK_CONTEXT.md を分離
# In Progress
- なし
# Pending
- Phase 2 設計(栽培履歴管理、カレンダー表示、モバイル対応)
- 自動テスト導入
- フロントエンドの統一的エラーハンドリング
# Next Step
Phase 2 の最初のタスクを決定する(栽培履歴管理 or カレンダー表示 or モバイル対応)
# Decisions
- CLAUDE.md からデータモデル詳細・実装状況・更新履歴を分離2026-03-18
- 実装状況は TASK_CONTEXT.md で管理する方針に変更
# Commands Run
なし(ドキュメント整理のみ)
# Errors / Risks
- 認証: ログアウト処理が未実装(トークン破棄のみ)
- N+1問題が一部存在現状はデータ量が少なく問題なし
# Do Not Touch
- Field ↔ OfficialKyosaiField / OfficialChusankanField の M:N 関係
- FertilizationEntry.fertilizer の PROTECT 制約
- 旧 is_confirmed / confirmed_at カラムDB残留、UI未使用 — 将来のマイグレーションで削除予定)

52
TASK_CONTEXT.md Normal file
View File

@@ -0,0 +1,52 @@
# 現在の作業状況
> **最終更新**: 2026-03-16
> **現在のフェーズ**: Phase 1 (MVP) - 全タスク完了、Phase 2 移行準備中
## 実装済み機能Phase 1 - MVP
1. **認証**: JWT認証アクセストークン24h、リフレッシュトークン7日
2. **圃場管理**: CRUD、ODS/Excelインポート、グループ機能
3. **作付け計画**: 年度別CRUD、前年度コピー、一括更新、集計API
4. **申請書生成**: 水稲共済細目書PDF、中山間交付金PDF
5. **フロントエンド**: 作付け計画編集、圃場一覧/詳細、データ取込、申請書DL、ダッシュボード
6. **対応付け可視化・紐づけ管理** (E-2): 圃場一覧「対応表」モード、共済/中山間リンク管理
7. **メールフィルタリング**Windmill連携:
- Django `apps/mail`、Windmill向けAPIAPIキー認証
- フィードバックページ認証不要・UUIDトークン、ルール管理、処理履歴
- 対応アカウント: Gmail × 2、Xserver × 6本番稼働中、10分間隔
- To ヘッダー宛先補正実装済み
- マスタードキュメント: `document/11_マスタードキュメント_メール通知関連編.md`
8. **パスワード変更**: `POST /api/auth/change-password/``/settings/password`
9. **気象データ基盤**Windmill連携:
- Django `apps/weather`WeatherRecord: 1日1行、2016-01-01〜
- Open-Meteo archive API窪川、Windmill毎朝6時同期
- API: records, summary, gdd, similarity
- フロントエンド `/weather`年別集計・期間指定、Recharts
- マスタードキュメント: `document/12_マスタードキュメント_気象データ編.md`
10. **施肥計画**(本番稼働中):
- 自動計算3方式: per_tan / even / nitrogen
- 四捨五入トグル、PDF出力A4横、PROTECT制約
- **散布実績**: 散布日単位記録、在庫USE連携、actual_bags再集計、WorkRecord自動生成
- マスタードキュメント: `document/13_マスタードキュメント_施肥計画編.md`
11. **運搬計画**(本番稼働中):
- 旧 Distribution → Delivery に再設計年度ベース、施肥計画FK廃止
- 軽トラ1回分単位、グループ一括割り当て、回間移動
- マスタードキュメント: `document/14_マスタードキュメント_分配計画編.md`
12. **作業記録索引**: `apps/workrecords`、運搬/散布の自動upsert
## 既知の課題・技術的負債
1. **認証周り**: ログアウト処理が未実装(トークン破棄のみ)
2. **エラーハンドリング**: フロントエンドでの統一的なエラー表示が未実装
3. **テスト**: 自動テストが未実装Phase 2で追加予定
4. **パフォーマンス**: N+1問題が一部存在
## 次のマイルストーンPhase 2
- 栽培履歴管理(播種日、農薬・肥料の散布記録)
- 作業予定のカレンダー表示
- モバイル対応の改善(スマホでの記録入力)
差異レポートの全タスクA-1〜A-8, B-1〜B-5, C-1〜C-8, D-1〜D-4, E-1〜E-2は全件完了。
詳細は `document/06_ドキュメントvs実装_差異レポート.md` を参照。

1
butler.pid Normal file
View File

@@ -0,0 +1 @@
3396

View File

@@ -1,16 +1,16 @@
# マスタードキュメント:施肥計画機能 # マスタードキュメント:施肥計画機能
> **作成**: 2026-03-01 > **作成**: 2026-03-01
> **最終更新**: 2026-03-15 > **最終更新**: 2026-03-17
> **対象機能**: 施肥計画(年度×品種単位のマトリクス管理・在庫引当・散布確定 > **対象機能**: 施肥計画(年度×品種単位のマトリクス管理・在庫引当・散布実績記録
> **実装状況**: 実装完了・本番稼働中 > **実装状況**: 実装完了・本番稼働中(散布実績連携追加)
--- ---
## 概要 ## 概要
農業生産者が「年度 × 品種」単位で施肥計画を立てる機能。 農業生産者が「年度 × 品種」単位で施肥計画を立てる機能。
複数圃場 × 複数肥料 × 袋数をマトリクス形式で管理し、PDF出力に加えて在庫引当散布確定まで一連で扱う。 複数圃場 × 複数肥料 × 袋数をマトリクス形式で管理し、PDF出力在庫引当散布実績記録・作業記録索引生成まで一連で扱う。
### 機能スコープIN / OUT ### 機能スコープIN / OUT
@@ -18,11 +18,14 @@
|---|---| |---|---|
| 肥料マスタ管理 | 肥料購入管理 | | 肥料マスタ管理 | 肥料購入管理 |
| 施肥計画の作成・編集・削除 | 運搬計画(→ `14_マスタードキュメント_分配計画編.md` 参照) | | 施肥計画の作成・編集・削除 | 運搬計画(→ `14_マスタードキュメント_分配計画編.md` 参照) |
| 3方式の自動計算 | 個別作業日報の詳細管理 | | 3方式の自動計算 | 運搬便ごとの散布充当追跡 |
| 作付け計画からの圃場自動取得 | | | 作付け計画からの圃場自動取得 | 相手先ごとのPDF様式実装 |
| PDF出力圃場×肥料マトリクス表 | | | PDF出力圃場×肥料マトリクス表 | 残肥返却・再入庫管理 |
| 在庫引当・引当解除 | | | 在庫引当・引当解除 | |
| 散布確定(計画値確認 + 実績入力 | | | 散布実績記録(日付単位・運搬済み肥料ベース | |
| 作業記録索引WorkRecord自動生成 | |
| 在庫USE連携散布実績保存時 | |
| 施肥計画進捗表示(未散布/一部散布/完了/計画超過) | |
--- ---
@@ -49,11 +52,20 @@
| name | varchar(200) | required | 計画名(ユーザーが自由入力) | | name | varchar(200) | required | 計画名(ユーザーが自由入力) |
| year | int | required | 年度 | | year | int | required | 年度 |
| variety | FK(plans.Variety) | PROTECT | 品種≠NULL | | variety | FK(plans.Variety) | PROTECT | 品種≠NULL |
| is_confirmed | bool | default=False | 散布確定済みフラグ | | is_confirmed | bool | default=False | ~~散布確定済みフラグ~~deprecated: 新UIでは使用しない |
| confirmed_at | datetime | nullable | 散布確定日時 | | confirmed_at | datetime | nullable | ~~散布確定日時~~deprecated: 新UIでは使用しない |
| created_at | datetime | auto | | | created_at | datetime | auto | |
| updated_at | datetime | auto | | | updated_at | datetime | auto | |
#### 表示用計算項目APIレスポンスに含まれる
| 項目 | 型 | 説明 |
|---|---|---|
| spread_status | string | `unspread` / `partial` / `completed` / `over_applied` |
| planned_total_bags | decimal | 計画袋数合計全entries.bagsの合計 |
| spread_total_bags | decimal | 散布済み袋数合計全entries.actual_bagsの合計 |
| remaining_total_bags | decimal | 残袋数planned_total_bags - spread_total_bags |
### FertilizationEntry施肥エントリ圃場×肥料×袋数 ### FertilizationEntry施肥エントリ圃場×肥料×袋数
| フィールド | 型 | 制約 | 説明 | | フィールド | 型 | 制約 | 説明 |
@@ -62,11 +74,60 @@
| plan | FK(FertilizationPlan) | CASCADE | | | plan | FK(FertilizationPlan) | CASCADE | |
| field | FK(fields.Field) | CASCADE | | | field | FK(fields.Field) | CASCADE | |
| fertilizer | FK(Fertilizer) | **PROTECT** | 施肥計画で使用中の肥料は削除不可 | | fertilizer | FK(Fertilizer) | **PROTECT** | 施肥計画で使用中の肥料は削除不可 |
| bags | decimal(8,2) | required | 袋数 | | bags | decimal(8,2) | required | 袋数(計画値) |
| actual_bags | decimal(10,4) | nullable | 散布実績集計値SpreadingSessionItemから自動集計 |
- `unique_together = ['plan', 'field', 'fertilizer']` - `unique_together = ['plan', 'field', 'fertilizer']`
- 順序: `field__display_order, field__id, fertilizer__name` - 順序: `field__display_order, field__id, fertilizer__name`
### SpreadingSession散布実績セッション
| フィールド | 型 | 制約 | 説明 |
|---|---|---|---|
| id | int | PK | |
| year | int | required | 年度フィルタ用 |
| date | DateField | required | 散布日 |
| name | varchar(100) | required | セッション名(必須) |
| notes | text | blank | 備考 |
| created_at | datetime | auto | |
| updated_at | datetime | auto | |
- `year + date` の一意制約は付けない(同日に午前・午後やエリア別で複数記録可能)
### 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 | datetime | auto | |
| updated_at | datetime | auto | |
- `unique_together = ['session', 'field', 'fertilizer']`
### WorkRecord作業記録索引
別アプリ `apps/workrecords/` で管理。施肥・運搬の作業を日付順に一覧するための索引テーブル。
詳細の本体は各業務テーブル側DeliveryTrip / SpreadingSessionに持つ。
| フィールド | 型 | 制約 | 説明 |
|---|---|---|---|
| id | int | PK | |
| work_date | DateField | required | 作業日 |
| work_type | varchar | 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 | datetime | auto | |
| updated_at | datetime | auto | |
--- ---
## API エンドポイント ## API エンドポイント
@@ -106,8 +167,8 @@
| GET | `/api/fertilizer/plans/{id}/` | 詳細取得entries 含む) | | GET | `/api/fertilizer/plans/{id}/` | 詳細取得entries 含む) |
| PUT | `/api/fertilizer/plans/{id}/` | 更新entries 全置換) | | PUT | `/api/fertilizer/plans/{id}/` | 更新entries 全置換) |
| DELETE | `/api/fertilizer/plans/{id}/` | 削除 | | DELETE | `/api/fertilizer/plans/{id}/` | 削除 |
| POST | `/api/fertilizer/plans/{id}/confirm_spreading/` | 散布確定(引当 → 使用へ変換 | | POST | `/api/fertilizer/plans/{id}/confirm_spreading/` | ~~散布確定~~deprecated: UI上で廃止、バックエンドは互換維持 |
| POST | `/api/fertilizer/plans/{id}/unconfirm/` | 散布確定取消(使用 → 引当に戻す | | POST | `/api/fertilizer/plans/{id}/unconfirm/` | ~~散布確定取消~~deprecated: UI上で廃止、バックエンドは互換維持 |
| GET | `/api/fertilizer/plans/{id}/pdf/` | PDF出力application/pdf | | GET | `/api/fertilizer/plans/{id}/pdf/` | PDF出力application/pdf |
一覧レスポンス例FertilizationPlan: 一覧レスポンス例FertilizationPlan:
@@ -154,18 +215,60 @@ POST/PUT リクエスト例:
PUT 時は entries が全置換削除→再作成。entries を省略した場合は既存を維持。 PUT 時は entries が全置換削除→再作成。entries を省略した場合は既存を維持。
散布確定 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}` | 散布候補一覧 |
散布候補一覧レスポンス例:
```json
[
{
"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"
}
]
```
散布実績 POST リクエスト例:
```json ```json
{ {
"entries": [ "year": 2026,
{"field_id": 5, "fertilizer_id": 1, "actual_bags": 2.4}, "date": "2026-04-15",
{"field_id": 6, "fertilizer_id": 1, "actual_bags": 0} "name": "午前・田中エリア",
"notes": "",
"items": [
{
"field": 5,
"fertilizer": 1,
"actual_bags": "2.5000",
"planned_bags_snapshot": "4.0000",
"delivered_bags_snapshot": "4.0000"
}
] ]
} }
``` ```
- `actual_bags > 0`: 対応する引当を使用実績へ変換 ### 作業記録(新規・別アプリ)
- `actual_bags = 0`: 未散布として引当解除
| メソッド | URL | 説明 |
|---|---|---|
| GET | `/api/workrecords/?year={year}` | 一覧 |
| GET | `/api/workrecords/{id}/` | 詳細(元レコードへのリンク情報を返す) |
### 圃場候補取得 ### 圃場候補取得
@@ -290,9 +393,11 @@ GET /api/plans/crops/
### 施肥計画一覧(`/fertilizer` ### 施肥計画一覧(`/fertilizer`
- 年度セレクタlocalStorage `fertilizerYear` で保持) - 年度セレクタlocalStorage `fertilizerYear` で保持)
- 計画カード一覧: 計画名・作物/品種・圃場数・肥料数・散布確定状態 - 計画カード一覧: 計画名・作物/品種・圃場数・肥料数・散布進捗
- 操作ボタン: PDF出力・編集・削除・散布確定 - 操作ボタン: PDF出力・編集・削除
- ヘッダー: 「肥料マスタ」「新規作成」ボタン - ヘッダー: 「肥料マスタ」「新規作成」ボタン
- 進捗表示: `未散布` / `一部散布 3.5 / 8.0袋` / `散布完了` / `計画超過`
- 計画値と実績値を並べて表示
### 肥料マスタ(`/fertilizer/masters` ### 肥料マスタ(`/fertilizer/masters`
@@ -316,11 +421,11 @@ GET /api/plans/crops/
6. **手動調整**: マトリクス表のセルを直接編集 6. **手動調整**: マトリクス表のセルを直接編集
7. **保存**: 「保存」ボタンで entries を一括送信 7. **保存**: 「保存」ボタンで entries を一括送信
#### 在庫連携・確定状態 #### 在庫連携・実績表示
- 肥料列ヘッダーに在庫 / 利用可能在庫 / 計画計 / 不足数を表示 - 肥料列ヘッダーに在庫 / 利用可能在庫 / 計画計 / 不足数を表示
- 散布確定済みの計画は情報バナーを表示し、編集操作をロック - マトリクス表で `bags`(計画値)を編集可能、`actual_bags`(実績値)は読み取り専用で参照表示
- 「確定取消」で使用実績を引当に戻し、再編集できる - 散布実績画面(`/fertilizer/spreading`)へのリンクを表示
#### マトリクスの表示仕様 #### マトリクスの表示仕様
@@ -329,16 +434,24 @@ GET /api/plans/crops/
- `↩` ボタン押下: 整数値を破棄し、元の計算値に戻る(参照グレー表示も消える) - `↩` ボタン押下: 整数値を破棄し、元の計算値に戻る(参照グレー表示も消える)
- 編集中に計算を再実行すると、その肥料列の `adjusted``roundedColumns` がリセットされる - 編集中に計算を再実行すると、その肥料列の `adjusted``roundedColumns` がリセットされる
### 散布確定モーダル`/fertilizer` 一覧から起動 ### 散布実績画面`/fertilizer/spreading`
- 全画面遷移ではなくモーダル表示 - 年度セレクタlocalStorage `fertilizerYear` と連動)
- 施肥計画編集と同じ視線移動になるよう、`圃場 = 行``肥料 = 列` のマトリクス表を採用 - 散布日入力DateField
- 画面上部に計画名・年度・作物/品種・対象圃場数・肥料数を表示 - セッション名入力(必須)
- 各セルは「薄いグレーの計画値」+「実績入力欄」の2段表示 - 運搬済み・未散布候補一覧を表示(`candidates` APIから取得
- 行末に圃場ごとの実績合計、表フッターに肥料別合計と総合計を表示 - 圃場単位で選択可能(全部または一部)
- `0` を入力したセルは未散布として扱い、対応する引当を解除する - 実績袋数の編集
- 差異がある場合はインライン警告表示
- 保存時に在庫USE連携・WorkRecord自動生成・FertilizationEntry.actual_bags再集計を実行
#### State 構成 ### 作業記録画面(`/workrecords`
- 年度セレクタ
- 日付・作業種別・タイトルの一覧表示
- 元データ(運搬回 / 散布セッション)への遷移リンク
#### State 構成(施肥計画編集画面)
```typescript ```typescript
// 基本情報 // 基本情報
@@ -374,13 +487,15 @@ backend/apps/fertilizer/
├── __init__.py ├── __init__.py
├── admin.py # Django admin 登録 ├── admin.py # Django admin 登録
├── apps.py # FertilizerConfig ├── apps.py # FertilizerConfig
├── models.py # Fertilizer, FertilizationPlan, FertilizationEntry ├── models.py # Fertilizer, FertilizationPlan, FertilizationEntry, SpreadingSession, SpreadingSessionItem
├── serializers.py # FertilizerSerializer, FertilizationPlanSerializer/WriteSerializer ├── serializers.py # FertilizerSerializer, FertilizationPlanSerializer/WriteSerializer, SpreadingSessionSerializer
├── views.py # FertilizerViewSet, FertilizationPlanViewSet, CandidateFieldsView, CalculateView ├── services.py # actual_bags再集計、WorkRecord自動生成、在庫USE連携
├── urls.py # DefaultRouter + candidate_fields/ + calculate/ ├── views.py # FertilizerViewSet, FertilizationPlanViewSet, SpreadingSessionViewSet, CandidateFieldsView, CalculateView
├── urls.py # DefaultRouter + candidate_fields/ + calculate/ + spreading/
├── migrations/ ├── migrations/
│ ├── 0001_initial.py │ ├── 0001_initial.py
── 0002_alter_fertilizationentry_fertilizer.py # CASCADE → PROTECT ── 0002_alter_fertilizationentry_fertilizer.py # CASCADE → PROTECT
│ └── ... # SpreadingSession, SpreadingSessionItem, actual_bags 追加
└── templates/ └── templates/
└── fertilizer/ └── fertilizer/
└── pdf.html # WeasyPrint テンプレートA4横向き └── pdf.html # WeasyPrint テンプレートA4横向き
@@ -398,25 +513,131 @@ frontend/src/app/fertilizer/
│ └── page.tsx # 編集FertilizerEditPage をラップ) │ └── page.tsx # 編集FertilizerEditPage をラップ)
├── masters/ ├── masters/
│ └── page.tsx # 肥料マスタ管理 │ └── page.tsx # 肥料マスタ管理
├── spreading/
│ └── ... # 散布実績画面(一覧・作成・編集)
└── _components/ └── _components/
└── FertilizerEditPage.tsx # 新規/編集共通コンポーネント(複雑) └── FertilizerEditPage.tsx # 新規/編集共通コンポーネント(複雑)
frontend/src/app/workrecords/
└── ... # 作業記録画面(一覧・詳細)
``` ```
### 変更されたファイル ### 変更されたファイル
| ファイル | 変更内容 | | ファイル | 変更内容 |
|---|---| |---|---|
| `backend/keinasystem/settings.py` | `INSTALLED_APPS``'apps.fertilizer'` を追加 | | `backend/keinasystem/settings.py` | `INSTALLED_APPS``'apps.fertilizer'`, `'apps.workrecords'` を追加 |
| `backend/keinasystem/urls.py` | `path('api/fertilizer/', include('apps.fertilizer.urls'))` を追加 | | `backend/keinasystem/urls.py` | `api/fertilizer/`, `api/workrecords/` を追加 |
| `frontend/src/types/index.ts` | `Fertilizer`, `FertilizationEntry`, `FertilizationPlan` 型を追加 | | `backend/apps/materials/models.py` | `StockTransaction.spreading_item` FK 追加(`on_delete=SET_NULL` |
| `backend/apps/workrecords/` | 作業記録索引アプリWorkRecord モデル・API・services |
| `frontend/src/types/index.ts` | 施肥・散布・作業記録の型を追加 |
| `frontend/src/components/Navbar.tsx` | Sprout アイコン + 施肥計画メニューを追加 | | `frontend/src/components/Navbar.tsx` | Sprout アイコン + 施肥計画メニューを追加 |
--- ---
## 在庫連携
### RESERVE施肥計画保存時
- 従来どおり計画値 `bags` ベースで維持
- 施肥計画の entries 保存時に RESERVE トランザクションを作成
### USE散布実績保存時
- `SpreadingSessionItem` ごとに USE を1件作成
- `material`: `item.fertilizer.material`
- `quantity`: `actual_bags`
- `occurred_on`: `session.date`
- `note`: `散布実績「{session.name or session.date}」`
### StockTransaction 追加フィールド
- `spreading_item = FK(SpreadingSessionItem, null=True, blank=True, on_delete=SET_NULL)`
### 更新・削除
- 散布実績更新時: その session に紐づく USE を全置換で作り直す
- 散布実績削除時: 対応 USE を削除する
### RESERVE と USE の整合
- RESERVE は計画値 `bags` ベース
- USE は散布実績 `actual_bags` ベース
- 計画値と実績値は併存する
---
## 集計ルール
### planned_total圃場×肥料×年度
`FertilizationEntry.bags` の合計
### delivered_total圃場×肥料×年度
`DeliveryTrip.date != null``DeliveryTripItem.bags` 合計
### spread_total圃場×肥料×年度
`SpreadingSessionItem.actual_bags` の合計
### actual_bags 再集計ルール
- `SUM(SpreadingSessionItem.actual_bags)` を同一 year, field, fertilizer で集計
- 散布実績の保存・更新・削除時に該当する `FertilizationEntry.actual_bags` を即時再計算
- `SUM(...) = 0` の場合は `actual_bags = null`
### remaining_bags表示用の残量
`delivered_total - spread_total`
### remaining_plan_bags計画進捗用の残量
`planned_total - spread_total`
### 差異の扱い
- `remaining_bags < 0`: 運搬実績不足
- `remaining_plan_bags < 0`: 計画超過
- 圃場+肥料単位で差異が分かることを優先する
---
## WorkRecord 自動生成ルール
### 運搬fertilizer_delivery
- `DeliveryTrip.date` 保存時に upsert
- `title = 肥料運搬: {delivery_plan.name} {n}回目`
- 日付削除時は対応 WorkRecord を削除
### 散布fertilizer_spreading
- `SpreadingSession` 保存時に upsert
- `title = 肥料散布: {session.name or session.date}`
- 削除時は対応 WorkRecord を削除
### 実装方針
自動生成は view に直書きせず、サービス層(`services.py`)で idempotent に実装する。
---
## 前年度コピー
`copy_from_previous_year` で前年度の `FertilizationEntry` をコピーする際のルール:
- `actual_bags` がある場合: `actual_bags` を新年度の `bags` 初期値として使用
- `actual_bags``null` の場合: 従来どおり `bags` をコピー
前年度に実際に散布した量を次年度計画の初期値として再利用できる。
---
## 型定義TypeScript ## 型定義TypeScript
```typescript ```typescript
// frontend/src/types/index.ts // frontend/src/types/index.ts(主要な型のみ抜粋)
export interface Fertilizer { export interface Fertilizer {
id: number; id: number;
@@ -437,6 +658,7 @@ export interface FertilizationEntry {
fertilizer: number; fertilizer: number;
fertilizer_name: string; fertilizer_name: string;
bags: string; bags: string;
actual_bags: string | null; // 散布実績集計値
} }
export interface FertilizationPlan { export interface FertilizationPlan {
@@ -449,6 +671,10 @@ export interface FertilizationPlan {
field_count: number; field_count: number;
fertilizer_count: number; fertilizer_count: number;
entries: FertilizationEntry[]; entries: FertilizationEntry[];
spread_status: 'unspread' | 'partial' | 'completed' | 'over_applied';
planned_total_bags: string;
spread_total_bags: string;
remaining_total_bags: string;
} }
``` ```
@@ -487,6 +713,25 @@ plans アプリの `DefaultRouter(r'', PlanViewSet)` が `plans/get-crops-with-v
PUT 時は entries を全削除→再作成する「全置換」方式。 PUT 時は entries を全削除→再作成する「全置換」方式。
部分更新は非対応PATCH でも entries がある場合は全置換)。 部分更新は非対応PATCH でも entries がある場合は全置換)。
### 散布実績の在庫連携
- 施肥計画保存時: `RESERVE`(計画値 `bags` ベース)
- 散布実績保存時: `USE`(実績値 `actual_bags` ベース)
- `RESERVE``USE` は併存する(計画値と実績値は別管理)
- 散布実績更新時は `session` に紐づく `USE` を全置換で作り直す
- 散布実績削除時は対応 `USE` を削除する(`StockTransaction.spreading_item``SET_NULL`
- `perform_destroy` で明示的に `StockTransaction` を削除してから `session.delete()` を呼ぶ
### 散布セッション名は必須
`SpreadingSession.name` は必須フィールド。WorkRecord のタイトル生成や一覧表示に使用するため、
空文字での保存は許可しない。
### useSearchParams と SuspenseNext.js 14
散布実績画面(`/fertilizer/spreading`)では `useSearchParams()` を使用するため、
`Suspense` boundary でラップする必要がある(本番ビルドで必須)。
### Next.js ホットリロードが効かない問題Windows + Docker ### Next.js ホットリロードが効かない問題Windows + Docker
Windows 環境では Docker ボリュームマウント経由のファイル変更が inotify で検知されず、 Windows 環境では Docker ボリュームマウント経由のファイル変更が inotify で検知されず、
@@ -499,6 +744,8 @@ Windows 環境では Docker ボリュームマウント経由のファイル変
## 将来の拡張(スコープ外) ## 将来の拡張(スコープ外)
- **配置計画**: 複数圃場分を一か所にまとめる時の置き場所割り当て(別機能として検討 - **相手先別PDF様式**: 客先ごとの提出資料フォーマット(元データは散布実績から取得可能
- **残肥返却・再入庫管理**: 散布後の残りを在庫に戻す処理
- **SpreadingAllocation**: 運搬便単位の散布充当追跡(現状は集計ベースで十分)
- **購入管理**: 肥料の購入・在庫管理(施肥計画の集計から購入数量を自動算出) - **購入管理**: 肥料の購入・在庫管理(施肥計画の集計から購入数量を自動算出)
- **作業記録との連携**: 施肥計画の実施記録(実施日・実際の袋数 - **配置計画**: 複数圃場分を一か所にまとめる時の置き場所割り当て(別機能として検討

View File

@@ -396,3 +396,17 @@ PDF生成時のみサーバーサイドで同じ計算を実施。
### エラー表示方針 ### エラー表示方針
施肥計画機能と同じく alert/confirm 廃止・インラインバナーに統一。 施肥計画機能と同じく alert/confirm 廃止・インラインバナーに統一。
### 散布実績との連携
- 運搬計画の `DeliveryTripItem` が散布実績画面(`/fertilizer/spreading`)の候補データソースとなる
- `DeliveryTrip.date != null` の明細のみを「運搬済み」とみなし、散布候補に含める
- 散布実績画面から運搬計画を指定して遷移する場合(`?delivery_plan_id=N`)、日付フィルタは適用されない(その計画の全明細が候補になる)
- 散布実績の保存時に在庫 `USE` が作成される(運搬時点では在庫変動なし)
### WorkRecord 自動生成
- `DeliveryTrip` に日付が保存されると、`WorkRecord``work_type=fertilizer_delivery`)が自動生成される
- 実装: `apps/workrecords/services.py``sync_delivery_work_record()`
- `DeliveryTrip` の日付が削除されると、対応する `WorkRecord` も削除される
- `WorkRecord` は索引として機能し、明細データは `DeliveryTrip` / `DeliveryTripItem` 側が保持する

File diff suppressed because one or more lines are too long