施肥計画機能を追加(年度×品種単位のマトリクス管理)
- 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:
69
backend/apps/fertilizer/models.py
Normal file
69
backend/apps/fertilizer/models.py
Normal file
@@ -0,0 +1,69 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Fertilizer(models.Model):
|
||||
name = models.CharField(max_length=100, unique=True, verbose_name='肥料名')
|
||||
maker = models.CharField(max_length=100, blank=True, null=True, verbose_name='メーカー')
|
||||
capacity_kg = models.DecimalField(
|
||||
max_digits=8, decimal_places=3, blank=True, null=True, verbose_name='1袋重量(kg)'
|
||||
)
|
||||
nitrogen_pct = models.DecimalField(
|
||||
max_digits=5, decimal_places=2, blank=True, null=True, verbose_name='窒素含有率(%)'
|
||||
)
|
||||
phosphorus_pct = models.DecimalField(
|
||||
max_digits=5, decimal_places=2, blank=True, null=True, verbose_name='リン酸含有率(%)'
|
||||
)
|
||||
potassium_pct = models.DecimalField(
|
||||
max_digits=5, decimal_places=2, blank=True, null=True, verbose_name='カリ含有率(%)'
|
||||
)
|
||||
notes = models.TextField(blank=True, null=True, verbose_name='備考')
|
||||
|
||||
class Meta:
|
||||
verbose_name = '肥料マスタ'
|
||||
verbose_name_plural = '肥料マスタ'
|
||||
ordering = ['name']
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class FertilizationPlan(models.Model):
|
||||
name = models.CharField(max_length=200, verbose_name='計画名')
|
||||
year = models.IntegerField(verbose_name='年度')
|
||||
variety = models.ForeignKey(
|
||||
'plans.Variety', on_delete=models.PROTECT,
|
||||
related_name='fertilization_plans', verbose_name='品種'
|
||||
)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = '施肥計画'
|
||||
verbose_name_plural = '施肥計画'
|
||||
ordering = ['-year', 'variety']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.year} {self.name}"
|
||||
|
||||
|
||||
class FertilizationEntry(models.Model):
|
||||
"""圃場 × 肥料 × 袋数 の中間テーブル"""
|
||||
plan = models.ForeignKey(
|
||||
FertilizationPlan, on_delete=models.CASCADE, related_name='entries'
|
||||
)
|
||||
field = models.ForeignKey(
|
||||
'fields.Field', on_delete=models.CASCADE, verbose_name='圃場'
|
||||
)
|
||||
fertilizer = models.ForeignKey(
|
||||
Fertilizer, on_delete=models.CASCADE, verbose_name='肥料'
|
||||
)
|
||||
bags = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='袋数')
|
||||
|
||||
class Meta:
|
||||
verbose_name = '施肥エントリ'
|
||||
verbose_name_plural = '施肥エントリ'
|
||||
unique_together = [['plan', 'field', 'fertilizer']]
|
||||
ordering = ['field', 'fertilizer']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.plan} / {self.field} / {self.fertilizer}: {self.bags}袋"
|
||||
Reference in New Issue
Block a user