施肥計画機能を追加(年度×品種単位のマトリクス管理)

- Backend: apps/fertilizer を新規追加
  - Fertilizer(肥料マスタ)、FertilizationPlan、FertilizationEntry モデル
  - 肥料マスタ・施肥計画 CRUD API
  - 3方式の自動計算API(反当袋数・均等配分・反当チッソ成分量)
  - 作付け計画から圃場候補を取得する API
  - WeasyPrint による PDF 出力(圃場×肥料=袋数 マトリクス表)
- Frontend: app/fertilizer を新規追加
  - 施肥計画一覧(年度セレクタ・PDF出力・編集・削除)
  - 肥料マスタ管理(インライン編集)
  - 施肥計画編集(品種選択→圃場自動取得→肥料追加→自動計算→マトリクス手動調整)
- Navbar に「施肥計画」メニューを追加(Sprout アイコン)
- Cursor ルールファイル・連携ガイドを削除(Claude Code 単独運用へ)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Akira
2026-03-01 12:14:29 +09:00
parent 371e40236c
commit f207f5de27
23 changed files with 1695 additions and 174 deletions

View File

@@ -0,0 +1,67 @@
# Generated by Django 5.0 on 2026-03-01 02:50
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('fields', '0006_e1c_chusankan_17_fields'),
('plans', '0004_crop_base_temp'),
]
operations = [
migrations.CreateModel(
name='Fertilizer',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, unique=True, verbose_name='肥料名')),
('maker', models.CharField(blank=True, max_length=100, null=True, verbose_name='メーカー')),
('capacity_kg', models.DecimalField(blank=True, decimal_places=3, max_digits=8, null=True, verbose_name='1袋重量(kg)')),
('nitrogen_pct', models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True, verbose_name='窒素含有率(%)')),
('phosphorus_pct', models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True, verbose_name='リン酸含有率(%)')),
('potassium_pct', models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True, verbose_name='カリ含有率(%)')),
('notes', models.TextField(blank=True, null=True, verbose_name='備考')),
],
options={
'verbose_name': '肥料マスタ',
'verbose_name_plural': '肥料マスタ',
'ordering': ['name'],
},
),
migrations.CreateModel(
name='FertilizationPlan',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='計画名')),
('year', models.IntegerField(verbose_name='年度')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('variety', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='fertilization_plans', to='plans.variety', verbose_name='品種')),
],
options={
'verbose_name': '施肥計画',
'verbose_name_plural': '施肥計画',
'ordering': ['-year', 'variety'],
},
),
migrations.CreateModel(
name='FertilizationEntry',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('bags', models.DecimalField(decimal_places=2, max_digits=8, verbose_name='袋数')),
('field', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fields.field', verbose_name='圃場')),
('plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='entries', to='fertilizer.fertilizationplan')),
('fertilizer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fertilizer.fertilizer', verbose_name='肥料')),
],
options={
'verbose_name': '施肥エントリ',
'verbose_name_plural': '施肥エントリ',
'ordering': ['field', 'fertilizer'],
'unique_together': {('plan', 'field', 'fertilizer')},
},
),
]