Files
keinasystem/backend/apps/plans/serializers.py
2026-04-04 17:26:55 +09:00

192 lines
6.3 KiB
Python

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()
planned_seed_kg = serializers.SerializerMethodField()
class Meta:
model = RiceTransplantEntry
fields = [
'id',
'field',
'field_name',
'field_area_tan',
'seedling_boxes_per_tan',
'seed_grams_per_box',
'planned_boxes',
'planned_seed_kg',
]
def get_planned_boxes(self, obj):
area = Decimal(str(obj.field.area_tan))
return str((area * obj.seedling_boxes_per_tan).quantize(Decimal('0.01')))
def get_planned_seed_kg(self, obj):
area = Decimal(str(obj.field.area_tan))
boxes = area * obj.seedling_boxes_per_tan
seed_kg = (boxes * obj.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',
'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(
Decimal(str(entry.field.area_tan)) * entry.seedling_boxes_per_tan
for entry in obj.entries.all()
)
return str(total.quantize(Decimal('0.01')))
def get_total_seed_kg(self, obj):
total = sum(
(
Decimal(str(entry.field.area_tan))
* entry.seedling_boxes_per_tan
* entry.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',
'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'],
seedling_boxes_per_tan=entry['seedling_boxes_per_tan'],
seed_grams_per_box=entry['seed_grams_per_box'],
)