192 lines
6.3 KiB
Python
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'],
|
|
)
|