from decimal import Decimal from rest_framework import serializers from apps.fields.models import Field from .models import Crop, Variety, Plan from .models import RiceTransplantEntry, RiceTransplantPlan class VarietySerializer(serializers.ModelSerializer): class Meta: model = Variety fields = '__all__' class CropSerializer(serializers.ModelSerializer): varieties = VarietySerializer(many=True, read_only=True) class Meta: model = Crop fields = '__all__' class PlanSerializer(serializers.ModelSerializer): crop_name = serializers.ReadOnlyField(source='crop.name') variety_name = serializers.ReadOnlyField(source='variety.name') field_name = serializers.ReadOnlyField(source='field.name') class Meta: model = Plan fields = '__all__' read_only_fields = ('id', 'created_at', 'updated_at') def create(self, validated_data): return Plan.objects.create(**validated_data) def update(self, instance, validated_data): for attr, value in validated_data.items(): setattr(instance, attr, value) instance.save() return instance class RiceTransplantEntrySerializer(serializers.ModelSerializer): field_name = serializers.CharField(source='field.name', read_only=True) field_area_tan = serializers.DecimalField( source='field.area_tan', max_digits=6, decimal_places=4, read_only=True, ) planned_boxes = serializers.SerializerMethodField() default_seedling_boxes = serializers.SerializerMethodField() planned_seed_kg = serializers.SerializerMethodField() class Meta: model = RiceTransplantEntry fields = [ 'id', 'field', 'field_name', 'field_area_tan', 'installed_seedling_boxes', 'default_seedling_boxes', 'planned_boxes', 'planned_seed_kg', ] def get_default_seedling_boxes(self, obj): area = Decimal(str(obj.field.area_tan)) default_boxes_per_tan = obj.plan.seedling_boxes_per_tan return str((area * default_boxes_per_tan).quantize(Decimal('0.01'))) def get_planned_boxes(self, obj): return str(obj.installed_seedling_boxes.quantize(Decimal('0.01'))) def get_planned_seed_kg(self, obj): seed_kg = ( obj.installed_seedling_boxes * obj.plan.default_seed_grams_per_box / Decimal('1000') ).quantize(Decimal('0.001')) return str(seed_kg) class RiceTransplantPlanSerializer(serializers.ModelSerializer): variety_name = serializers.CharField(source='variety.name', read_only=True) crop_name = serializers.CharField(source='variety.crop.name', read_only=True) entries = RiceTransplantEntrySerializer(many=True, read_only=True) field_count = serializers.SerializerMethodField() total_seedling_boxes = serializers.SerializerMethodField() total_seed_kg = serializers.SerializerMethodField() crop_seed_inventory_kg = serializers.SerializerMethodField() remaining_seed_kg = serializers.SerializerMethodField() class Meta: model = RiceTransplantPlan fields = [ 'id', 'name', 'year', 'variety', 'variety_name', 'crop_name', 'default_seed_grams_per_box', 'seedling_boxes_per_tan', 'notes', 'entries', 'field_count', 'total_seedling_boxes', 'total_seed_kg', 'crop_seed_inventory_kg', 'remaining_seed_kg', 'created_at', 'updated_at', ] def get_field_count(self, obj): return obj.entries.count() def get_total_seedling_boxes(self, obj): total = sum( entry.installed_seedling_boxes for entry in obj.entries.all() ) return str(total.quantize(Decimal('0.01'))) def get_total_seed_kg(self, obj): total = sum( ( entry.installed_seedling_boxes * obj.default_seed_grams_per_box / Decimal('1000') ) for entry in obj.entries.all() ) return str(total.quantize(Decimal('0.001'))) def get_crop_seed_inventory_kg(self, obj): return str(obj.variety.crop.seed_inventory_kg) def get_remaining_seed_kg(self, obj): total_seed = Decimal(self.get_total_seed_kg(obj)) return str((obj.variety.crop.seed_inventory_kg - total_seed).quantize(Decimal('0.001'))) class RiceTransplantPlanWriteSerializer(serializers.ModelSerializer): entries = serializers.ListField(child=serializers.DictField(), write_only=True, required=False) class Meta: model = RiceTransplantPlan fields = [ 'id', 'name', 'year', 'variety', 'default_seed_grams_per_box', 'seedling_boxes_per_tan', 'notes', 'entries', ] def create(self, validated_data): entries_data = validated_data.pop('entries', []) plan = RiceTransplantPlan.objects.create(**validated_data) self._save_entries(plan, entries_data) return plan def update(self, instance, validated_data): entries_data = validated_data.pop('entries', None) for attr, value in validated_data.items(): setattr(instance, attr, value) instance.save() if entries_data is not None: instance.entries.all().delete() self._save_entries(instance, entries_data) return instance def validate(self, attrs): entries_data = attrs.get('entries') if entries_data is None: return attrs field_ids = [entry.get('field_id') for entry in entries_data if entry.get('field_id') is not None] existing_ids = set(Field.objects.filter(id__in=field_ids).values_list('id', flat=True)) missing_ids = sorted(set(field_ids) - existing_ids) if missing_ids: raise serializers.ValidationError({ 'entries': f'存在しない圃場IDが含まれています: {", ".join(str(field_id) for field_id in missing_ids)}' }) return attrs def _save_entries(self, plan, entries_data): for entry in entries_data: RiceTransplantEntry.objects.create( plan=plan, field_id=entry['field_id'], installed_seedling_boxes=entry['installed_seedling_boxes'], seed_grams_per_box=plan.default_seed_grams_per_box, )