Files
keinasystem/backend/apps/plans/models.py
akira a38472e4a0 品種ごとの種子在庫前提まで実装を進めました。
主な変更は、seed 資材種別の追加と Variety.seed_material の導入です。backend/apps/materials/models.py、backend/apps/plans/models.py、backend/apps/plans/serializers.py で、田植え計画が作物在庫ではなく品種に紐づく種子資材の現在庫を参照するように切り替えました。マイグレーションは backend/apps/materials/migrations/0005_material_seed_type.py と backend/apps/plans/migrations/0008_variety_seed_material.py を追加しています。

画面側は、frontend/src/app/materials/page.tsx と frontend/src/app/materials/masters/page.tsx に「種子」タブを追加し、frontend/src/app/allocation/page.tsx の品種管理モーダルで品種ごとに種子在庫資材を設定できるようにしました。田植え計画画面 frontend/src/app/rice-transplant/_components/RiceTransplantEditPage.tsx も、苗箱数 列中心に整理し、種もみkg 列を削除、反当苗箱枚数 の列反映と ≈ / ↩ の四捨五入トグルを施肥計画寄りの操作感に寄せています。仕様書 document/16_マスタードキュメント_田植え計画編.md も更新済みです。

確認できたのは python3 -m py_compile backend/apps/materials/models.py backend/apps/materials/serializers.py backend/apps/plans/models.py backend/apps/plans/serializers.py backend/apps/plans/views.py までです。frontend/node_modules が無いためフロントのビルド確認はまだできていません。Issue #2 にも反映内容をコメント済みです。必要なら次にコミットします。
2026-04-05 11:22:07 +09:00

134 lines
4.3 KiB
Python

from django.db import models
from apps.fields.models import Field
class Crop(models.Model):
name = models.CharField(max_length=100, unique=True, verbose_name="作物名")
base_temp = models.FloatField(default=0.0, verbose_name="有効積算温度 基準温度(℃)")
seed_inventory_kg = models.DecimalField(
max_digits=10,
decimal_places=3,
default=0,
verbose_name="種もみ在庫(kg)",
)
class Meta:
verbose_name = "作物マスタ"
verbose_name_plural = "作物マスタ"
def __str__(self):
return self.name
class Variety(models.Model):
crop = models.ForeignKey(Crop, on_delete=models.CASCADE, related_name='varieties', verbose_name="作物")
name = models.CharField(max_length=100, verbose_name="品種名")
default_seedling_boxes_per_tan = models.DecimalField(
max_digits=6,
decimal_places=2,
default=0,
verbose_name="反当苗箱枚数デフォルト",
)
seed_material = models.ForeignKey(
'materials.Material',
on_delete=models.SET_NULL,
related_name='varieties',
verbose_name='種子在庫資材',
blank=True,
null=True,
limit_choices_to={'material_type': 'seed'},
)
class Meta:
verbose_name = "品種マスタ"
verbose_name_plural = "品種マスタ"
unique_together = [['crop', 'name']]
def __str__(self):
return f"{self.crop.name} - {self.name}"
class Plan(models.Model):
field = models.ForeignKey(Field, on_delete=models.CASCADE, related_name='plans', verbose_name="圃場")
year = models.IntegerField(verbose_name="作付年度")
crop = models.ForeignKey(Crop, on_delete=models.CASCADE, related_name='plans', verbose_name="作物")
variety = models.ForeignKey(Variety, on_delete=models.SET_NULL, related_name='plans', verbose_name="品種", blank=True, null=True)
notes = models.TextField(blank=True, null=True, verbose_name="備考")
class Meta:
verbose_name = "作付け計画"
verbose_name_plural = "作付け計画"
unique_together = [['field', 'year']]
ordering = ['-year', 'field']
def __str__(self):
return f"{self.field.name} - {self.year} - {self.crop.name}"
class RiceTransplantPlan(models.Model):
name = models.CharField(max_length=200, verbose_name='計画名')
year = models.IntegerField(verbose_name='年度')
variety = models.ForeignKey(
Variety,
on_delete=models.PROTECT,
related_name='rice_transplant_plans',
verbose_name='品種',
)
default_seed_grams_per_box = models.DecimalField(
max_digits=8,
decimal_places=2,
default=0,
verbose_name='苗箱1枚あたり種もみ(g)デフォルト',
)
seedling_boxes_per_tan = models.DecimalField(
max_digits=6,
decimal_places=2,
default=0,
verbose_name='反当苗箱枚数',
)
notes = models.TextField(blank=True, default='', 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 RiceTransplantEntry(models.Model):
plan = models.ForeignKey(
RiceTransplantPlan,
on_delete=models.CASCADE,
related_name='entries',
verbose_name='田植え計画',
)
field = models.ForeignKey(
Field,
on_delete=models.CASCADE,
related_name='rice_transplant_entries',
verbose_name='圃場',
)
installed_seedling_boxes = models.DecimalField(
max_digits=8,
decimal_places=2,
verbose_name='設置苗箱枚数',
)
seed_grams_per_box = models.DecimalField(
max_digits=8,
decimal_places=2,
verbose_name='苗箱1枚あたり種もみ(g)',
)
class Meta:
verbose_name = '田植え計画エントリ'
verbose_name_plural = '田植え計画エントリ'
unique_together = [['plan', 'field']]
ordering = ['field__display_order', 'field__id']
def __str__(self):
return f'{self.plan} / {self.field} / {self.installed_seedling_boxes}'