from decimal import Decimal from django.core.validators import MinValueValidator from django.db import models class Material(models.Model): """共通資材マスタ""" class MaterialType(models.TextChoices): FERTILIZER = 'fertilizer', '肥料' PESTICIDE = 'pesticide', '農薬' SEEDLING = 'seedling', '種苗' OTHER = 'other', 'その他' class StockUnit(models.TextChoices): BAG = 'bag', '袋' BOTTLE = 'bottle', '本' KG = 'kg', 'kg' LITER = 'liter', 'L' PIECE = 'piece', '個' name = models.CharField(max_length=100, verbose_name='資材名') material_type = models.CharField( max_length=20, choices=MaterialType.choices, verbose_name='資材種別', ) maker = models.CharField( max_length=100, blank=True, default='', verbose_name='メーカー', ) stock_unit = models.CharField( max_length=20, choices=StockUnit.choices, default=StockUnit.BAG, verbose_name='在庫単位', ) is_active = models.BooleanField(default=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: ordering = ['material_type', 'name'] constraints = [ models.UniqueConstraint( fields=['material_type', 'name'], name='uniq_material_type_name', ), ] verbose_name = '資材' verbose_name_plural = '資材' def __str__(self): return f'{self.get_material_type_display()}: {self.name}' class FertilizerProfile(models.Model): """肥料専用属性""" material = models.OneToOneField( Material, on_delete=models.CASCADE, related_name='fertilizer_profile', ) 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='カリ(%)', ) class Meta: verbose_name = '肥料詳細' verbose_name_plural = '肥料詳細' def __str__(self): return f'肥料詳細: {self.material.name}' class PesticideProfile(models.Model): """農薬専用属性""" material = models.OneToOneField( Material, on_delete=models.CASCADE, related_name='pesticide_profile', ) registration_no = models.CharField( max_length=100, blank=True, default='', verbose_name='農薬登録番号', ) formulation = models.CharField( max_length=100, blank=True, default='', verbose_name='剤型', ) usage_unit = models.CharField( max_length=50, blank=True, default='', verbose_name='使用単位', ) dilution_ratio = models.CharField( max_length=100, blank=True, default='', verbose_name='希釈倍率', ) active_ingredient = models.CharField( max_length=200, blank=True, default='', verbose_name='有効成分', ) category = models.CharField( max_length=100, blank=True, default='', verbose_name='分類', ) class Meta: verbose_name = '農薬詳細' verbose_name_plural = '農薬詳細' def __str__(self): return f'農薬詳細: {self.material.name}' class StockTransaction(models.Model): """入出庫履歴""" class TransactionType(models.TextChoices): PURCHASE = 'purchase', '入庫' USE = 'use', '使用' RESERVE = 'reserve', '引当' ADJUSTMENT_PLUS = 'adjustment_plus', '棚卸増' ADJUSTMENT_MINUS = 'adjustment_minus', '棚卸減' DISCARD = 'discard', '廃棄' INCREASE_TYPES = { TransactionType.PURCHASE, TransactionType.ADJUSTMENT_PLUS, } DECREASE_TYPES = { TransactionType.USE, TransactionType.RESERVE, TransactionType.ADJUSTMENT_MINUS, TransactionType.DISCARD, } material = models.ForeignKey( Material, on_delete=models.PROTECT, related_name='stock_transactions', verbose_name='資材', ) transaction_type = models.CharField( max_length=30, choices=TransactionType.choices, verbose_name='取引種別', ) quantity = models.DecimalField( max_digits=10, decimal_places=3, validators=[MinValueValidator(Decimal('0.001'))], verbose_name='数量', ) occurred_on = models.DateField(verbose_name='発生日') note = models.TextField(blank=True, default='', verbose_name='備考') fertilization_plan = models.ForeignKey( 'fertilizer.FertilizationPlan', on_delete=models.SET_NULL, null=True, blank=True, related_name='stock_reservations', verbose_name='施肥計画', ) spreading_item = models.ForeignKey( 'fertilizer.SpreadingSessionItem', on_delete=models.CASCADE, null=True, blank=True, related_name='stock_transactions', verbose_name='散布実績明細', ) created_at = models.DateTimeField(auto_now_add=True) class Meta: ordering = ['-occurred_on', '-created_at', '-id'] verbose_name = '入出庫履歴' verbose_name_plural = '入出庫履歴' def __str__(self): return ( f'{self.material.name} ' f'{self.get_transaction_type_display()} ' f'{self.quantity}' )