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='備考') material = models.OneToOneField( 'materials.Material', on_delete=models.SET_NULL, null=True, blank=True, related_name='legacy_fertilizer', 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='品種' ) calc_settings = models.JSONField(default=list, blank=True, verbose_name='計算設定') is_confirmed = models.BooleanField(default=False, verbose_name='散布確定済み') confirmed_at = models.DateTimeField(null=True, blank=True, 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.PROTECT, verbose_name='肥料' ) bags = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='袋数') actual_bags = models.DecimalField( max_digits=10, decimal_places=4, null=True, blank=True, 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}袋" class DeliveryPlan(models.Model): """運搬計画:施肥計画の肥料を軽トラで運ぶ単位で計画・記録する""" year = models.IntegerField(verbose_name='年度') name = models.CharField(max_length=200, 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', 'name'] def __str__(self): return f"{self.year} {self.name}" class DeliveryGroup(models.Model): """配送先グループ:まとめて運ぶ圃場のグループ""" delivery_plan = models.ForeignKey( DeliveryPlan, on_delete=models.CASCADE, related_name='groups', verbose_name='運搬計画' ) name = models.CharField(max_length=100, verbose_name='グループ名') order = models.PositiveIntegerField(default=0, verbose_name='表示順') class Meta: verbose_name = '配送先グループ' verbose_name_plural = '配送先グループ' unique_together = [['delivery_plan', 'name']] ordering = ['order', 'id'] def __str__(self): return f"{self.delivery_plan} / {self.name}" class DeliveryGroupField(models.Model): """圃場のグループへの割り当て(1圃場=1グループ/1運搬計画)""" delivery_plan = models.ForeignKey( DeliveryPlan, on_delete=models.CASCADE, verbose_name='運搬計画' ) group = models.ForeignKey( DeliveryGroup, on_delete=models.CASCADE, related_name='field_assignments', verbose_name='グループ' ) field = models.ForeignKey( 'fields.Field', on_delete=models.PROTECT, verbose_name='圃場' ) class Meta: verbose_name = 'グループ圃場割り当て' verbose_name_plural = 'グループ圃場割り当て' unique_together = [['delivery_plan', 'field']] ordering = ['field__display_order', 'field__id'] def __str__(self): return f"{self.group.name} / {self.field.name}" class DeliveryTrip(models.Model): """運搬回:軽トラ1回分の積載""" delivery_plan = models.ForeignKey( DeliveryPlan, on_delete=models.CASCADE, related_name='trips', verbose_name='運搬計画' ) order = models.PositiveIntegerField(default=0, verbose_name='何回目') name = models.CharField(max_length=100, blank=True, verbose_name='名前') date = models.DateField(null=True, blank=True, verbose_name='運搬日') class Meta: verbose_name = '運搬回' verbose_name_plural = '運搬回' ordering = ['order', 'id'] def __str__(self): return f"{self.delivery_plan} / {self.order + 1}回目" class DeliveryTripItem(models.Model): """運搬明細:圃場×肥料単位の袋数""" trip = models.ForeignKey( DeliveryTrip, on_delete=models.CASCADE, related_name='items', verbose_name='運搬回' ) field = models.ForeignKey( 'fields.Field', on_delete=models.PROTECT, verbose_name='圃場' ) fertilizer = models.ForeignKey( Fertilizer, on_delete=models.PROTECT, verbose_name='肥料' ) bags = models.DecimalField(max_digits=10, decimal_places=4, verbose_name='袋数') class Meta: verbose_name = '運搬明細' verbose_name_plural = '運搬明細' unique_together = [['trip', 'field', 'fertilizer']] ordering = ['field__display_order', 'field__id', 'fertilizer__name'] def __str__(self): return f"{self.trip} / {self.field.name} / {self.fertilizer.name}: {self.bags}袋" class SpreadingSession(models.Model): """散布日単位の実績""" year = models.IntegerField(verbose_name='年度') date = models.DateField(verbose_name='散布日') name = models.CharField(max_length=100, blank=True, 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 = ['-date', '-id'] def __str__(self): label = self.name.strip() or f'{self.date}' return f'{self.year} {label}' class SpreadingSessionItem(models.Model): """散布実績明細:圃場×肥料ごとの実績""" session = models.ForeignKey( SpreadingSession, on_delete=models.CASCADE, related_name='items', verbose_name='散布実績', ) field = models.ForeignKey( 'fields.Field', on_delete=models.PROTECT, verbose_name='圃場' ) fertilizer = models.ForeignKey( Fertilizer, on_delete=models.PROTECT, verbose_name='肥料' ) actual_bags = models.DecimalField(max_digits=10, decimal_places=4, verbose_name='実散布袋数') planned_bags_snapshot = models.DecimalField( max_digits=10, decimal_places=4, verbose_name='計画袋数スナップショット', ) delivered_bags_snapshot = models.DecimalField( max_digits=10, decimal_places=4, verbose_name='運搬済み袋数スナップショット', ) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: verbose_name = '散布実績明細' verbose_name_plural = '散布実績明細' unique_together = [['session', 'field', 'fertilizer']] ordering = ['field__display_order', 'field__id', 'fertilizer__name'] def __str__(self): return ( f'{self.session} / {self.field.name} / ' f'{self.fertilizer.name}: {self.actual_bags}袋' )