from rest_framework import serializers from .models import ( Fertilizer, FertilizationPlan, FertilizationEntry, DeliveryPlan, DeliveryGroup, DeliveryGroupField, DeliveryTrip, DeliveryTripItem, ) class FertilizerSerializer(serializers.ModelSerializer): material_id = serializers.SerializerMethodField() class Meta: model = Fertilizer fields = [ 'id', 'name', 'maker', 'capacity_kg', 'nitrogen_pct', 'phosphorus_pct', 'potassium_pct', 'notes', 'material', 'material_id', ] def get_material_id(self, obj): return obj.material_id class FertilizationEntrySerializer(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 ) fertilizer_name = serializers.CharField(source='fertilizer.name', read_only=True) class Meta: model = FertilizationEntry fields = ['id', 'field', 'field_name', 'field_area_tan', 'fertilizer', 'fertilizer_name', 'bags'] class FertilizationPlanSerializer(serializers.ModelSerializer): variety_name = serializers.SerializerMethodField() crop_name = serializers.SerializerMethodField() entries = FertilizationEntrySerializer(many=True, read_only=True) field_count = serializers.SerializerMethodField() fertilizer_count = serializers.SerializerMethodField() is_confirmed = serializers.BooleanField(read_only=True) confirmed_at = serializers.DateTimeField(read_only=True) class Meta: model = FertilizationPlan fields = [ 'id', 'name', 'year', 'variety', 'variety_name', 'crop_name', 'calc_settings', 'entries', 'field_count', 'fertilizer_count', 'is_confirmed', 'confirmed_at', 'created_at', 'updated_at' ] def get_variety_name(self, obj): return obj.variety.name def get_crop_name(self, obj): return obj.variety.crop.name def get_field_count(self, obj): return obj.entries.values('field').distinct().count() def get_fertilizer_count(self, obj): return obj.entries.values('fertilizer').distinct().count() class FertilizationPlanWriteSerializer(serializers.ModelSerializer): """保存用(entries を一括で受け取る)""" entries = serializers.ListField(child=serializers.DictField(), write_only=True, required=False) class Meta: model = FertilizationPlan fields = ['id', 'name', 'year', 'variety', 'calc_settings', 'entries'] def create(self, validated_data): entries_data = validated_data.pop('entries', []) plan = FertilizationPlan.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 _save_entries(self, plan, entries_data): for entry in entries_data: FertilizationEntry.objects.create( plan=plan, field_id=entry['field_id'], fertilizer_id=entry['fertilizer_id'], bags=entry['bags'], ) # ─── 運搬計画 ──────────────────────────────────────────────────────────── class DeliveryGroupFieldSerializer(serializers.ModelSerializer): id = serializers.IntegerField(source='field.id', read_only=True) name = serializers.CharField(source='field.name', read_only=True) area_tan = serializers.DecimalField( source='field.area_tan', max_digits=6, decimal_places=4, read_only=True ) class Meta: model = DeliveryGroupField fields = ['id', 'name', 'area_tan'] class DeliveryGroupReadSerializer(serializers.ModelSerializer): fields = DeliveryGroupFieldSerializer(source='field_assignments', many=True, read_only=True) class Meta: model = DeliveryGroup fields = ['id', 'name', 'order', 'fields'] class DeliveryTripItemSerializer(serializers.ModelSerializer): field_name = serializers.CharField(source='field.name', read_only=True) fertilizer_name = serializers.CharField(source='fertilizer.name', read_only=True) class Meta: model = DeliveryTripItem fields = ['id', 'field', 'field_name', 'fertilizer', 'fertilizer_name', 'bags'] class DeliveryTripReadSerializer(serializers.ModelSerializer): items = DeliveryTripItemSerializer(many=True, read_only=True) class Meta: model = DeliveryTrip fields = ['id', 'order', 'name', 'date', 'items'] class DeliveryPlanListSerializer(serializers.ModelSerializer): group_count = serializers.SerializerMethodField() trip_count = serializers.SerializerMethodField() class Meta: model = DeliveryPlan fields = [ 'id', 'year', 'name', 'group_count', 'trip_count', 'created_at', 'updated_at', ] def get_group_count(self, obj): return obj.groups.count() def get_trip_count(self, obj): return obj.trips.count() class DeliveryPlanReadSerializer(serializers.ModelSerializer): groups = DeliveryGroupReadSerializer(many=True, read_only=True) trips = DeliveryTripReadSerializer(many=True, read_only=True) unassigned_fields = serializers.SerializerMethodField() available_fertilizers = serializers.SerializerMethodField() all_entries = serializers.SerializerMethodField() class Meta: model = DeliveryPlan fields = [ 'id', 'year', 'name', 'groups', 'trips', 'unassigned_fields', 'available_fertilizers', 'all_entries', 'created_at', 'updated_at', ] def get_unassigned_fields(self, obj): assigned_ids = DeliveryGroupField.objects.filter( delivery_plan=obj ).values_list('field_id', flat=True) # 年度の施肥計画に含まれる全圃場 plan_field_ids = FertilizationEntry.objects.filter( plan__year=obj.year ).values_list('field_id', flat=True).distinct() from apps.fields.models import Field unassigned = Field.objects.filter( id__in=plan_field_ids ).exclude(id__in=assigned_ids).order_by('display_order', 'id') return [{'id': f.id, 'name': f.name, 'area_tan': str(f.area_tan)} for f in unassigned] def get_available_fertilizers(self, obj): fert_ids = FertilizationEntry.objects.filter( plan__year=obj.year ).values_list('fertilizer_id', flat=True).distinct() fertilizers = Fertilizer.objects.filter(id__in=fert_ids).order_by('name') return [{'id': f.id, 'name': f.name} for f in fertilizers] def get_all_entries(self, obj): """年度の全施肥計画のエントリ(フロントで袋数計算に使用)""" entries = FertilizationEntry.objects.filter( plan__year=obj.year ).select_related('field', 'fertilizer') return [ { 'field': e.field_id, 'field_name': e.field.name, 'field_area_tan': str(e.field.area_tan), 'fertilizer': e.fertilizer_id, 'fertilizer_name': e.fertilizer.name, 'bags': str(e.bags), } for e in entries ] class DeliveryPlanWriteSerializer(serializers.ModelSerializer): groups = serializers.ListField(child=serializers.DictField(), write_only=True, required=False) trips = serializers.ListField(child=serializers.DictField(), write_only=True, required=False) class Meta: model = DeliveryPlan fields = ['id', 'year', 'name', 'groups', 'trips'] def create(self, validated_data): groups_data = validated_data.pop('groups', []) trips_data = validated_data.pop('trips', []) plan = DeliveryPlan.objects.create(**validated_data) self._save_groups(plan, groups_data) self._save_trips(plan, trips_data) return plan def update(self, instance, validated_data): groups_data = validated_data.pop('groups', None) trips_data = validated_data.pop('trips', None) instance.name = validated_data.get('name', instance.name) instance.year = validated_data.get('year', instance.year) instance.save() if groups_data is not None: instance.groups.all().delete() self._save_groups(instance, groups_data) if trips_data is not None: instance.trips.all().delete() self._save_trips(instance, trips_data) return instance def _save_groups(self, plan, groups_data): for g_data in groups_data: group = DeliveryGroup.objects.create( delivery_plan=plan, name=g_data['name'], order=g_data.get('order', 0), ) for field_id in g_data.get('field_ids', []): DeliveryGroupField.objects.create( delivery_plan=plan, group=group, field_id=field_id, ) def _save_trips(self, plan, trips_data): for t_data in trips_data: trip = DeliveryTrip.objects.create( delivery_plan=plan, order=t_data.get('order', 0), name=t_data.get('name', ''), date=t_data.get('date'), ) for item in t_data.get('items', []): DeliveryTripItem.objects.create( trip=trip, field_id=item['field_id'], fertilizer_id=item['fertilizer_id'], bags=item['bags'], )