分配計画を運搬計画に再設計: 軽トラ1回分を基本単位とする運搬回モデルを導入
実運用のワークフロー(複数施肥計画混在・軽トラ複数回・肥料指定)に合わせ、 旧 DistributionPlan/Group/GroupField を DeliveryPlan/Group/GroupField/Trip/TripItem に置き換え。 施肥計画への直接FK廃止→年度ベースで全施肥計画を横断。 回ごとの日付記録、圃場の回間移動、対象肥料フィルタ、回ごとPDF出力に対応。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
from rest_framework import serializers
|
||||
from .models import Fertilizer, FertilizationPlan, FertilizationEntry, DistributionPlan, DistributionGroup, DistributionGroupField
|
||||
from .models import (
|
||||
Fertilizer, FertilizationPlan, FertilizationEntry,
|
||||
DeliveryPlan, DeliveryGroup, DeliveryGroupField, DeliveryTrip, DeliveryTripItem,
|
||||
)
|
||||
|
||||
|
||||
class FertilizerSerializer(serializers.ModelSerializer):
|
||||
@@ -100,9 +103,9 @@ class FertilizationPlanWriteSerializer(serializers.ModelSerializer):
|
||||
)
|
||||
|
||||
|
||||
# ─── 分配計画 ────────────────────────────────────────────────────────────
|
||||
# ─── 運搬計画 ────────────────────────────────────────────────────────────
|
||||
|
||||
class DistributionGroupFieldSerializer(serializers.ModelSerializer):
|
||||
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(
|
||||
@@ -110,128 +113,163 @@ class DistributionGroupFieldSerializer(serializers.ModelSerializer):
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = DistributionGroupField
|
||||
model = DeliveryGroupField
|
||||
fields = ['id', 'name', 'area_tan']
|
||||
|
||||
|
||||
class DistributionGroupReadSerializer(serializers.ModelSerializer):
|
||||
fields = DistributionGroupFieldSerializer(source='field_assignments', many=True, read_only=True)
|
||||
class DeliveryGroupReadSerializer(serializers.ModelSerializer):
|
||||
fields = DeliveryGroupFieldSerializer(source='field_assignments', many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = DistributionGroup
|
||||
model = DeliveryGroup
|
||||
fields = ['id', 'name', 'order', 'fields']
|
||||
|
||||
|
||||
class FertilizationPlanForDistributionSerializer(serializers.ModelSerializer):
|
||||
"""分配計画詳細に埋め込む施肥計画情報(肥料一覧・entries 含む)"""
|
||||
variety_name = serializers.SerializerMethodField()
|
||||
crop_name = serializers.SerializerMethodField()
|
||||
fertilizers = serializers.SerializerMethodField()
|
||||
entries = serializers.SerializerMethodField()
|
||||
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 = FertilizationPlan
|
||||
fields = ['id', 'name', 'year', 'variety_name', 'crop_name', 'fertilizers', 'entries']
|
||||
|
||||
def get_variety_name(self, obj):
|
||||
return obj.variety.name
|
||||
|
||||
def get_crop_name(self, obj):
|
||||
return obj.variety.crop.name
|
||||
|
||||
def get_fertilizers(self, obj):
|
||||
fert_ids = obj.entries.values_list('fertilizer_id', flat=True).distinct()
|
||||
from .models import Fertilizer as F
|
||||
fertilizers = F.objects.filter(id__in=fert_ids).order_by('name')
|
||||
return [{'id': f.id, 'name': f.name} for f in fertilizers]
|
||||
|
||||
def get_entries(self, obj):
|
||||
return [
|
||||
{'field': e.field_id, 'fertilizer': e.fertilizer_id, 'bags': str(e.bags)}
|
||||
for e in obj.entries.all()
|
||||
]
|
||||
model = DeliveryTripItem
|
||||
fields = ['id', 'field', 'field_name', 'fertilizer', 'fertilizer_name', 'bags']
|
||||
|
||||
|
||||
class DistributionPlanListSerializer(serializers.ModelSerializer):
|
||||
fertilization_plan_id = serializers.IntegerField(source='fertilization_plan.id', read_only=True)
|
||||
fertilization_plan_name = serializers.CharField(source='fertilization_plan.name', read_only=True)
|
||||
year = serializers.IntegerField(source='fertilization_plan.year', read_only=True)
|
||||
variety_name = serializers.SerializerMethodField()
|
||||
crop_name = serializers.SerializerMethodField()
|
||||
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()
|
||||
field_count = serializers.SerializerMethodField()
|
||||
trip_count = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = DistributionPlan
|
||||
model = DeliveryPlan
|
||||
fields = [
|
||||
'id', 'name', 'fertilization_plan_id', 'fertilization_plan_name',
|
||||
'year', 'variety_name', 'crop_name', 'group_count', 'field_count',
|
||||
'id', 'year', 'name', 'group_count', 'trip_count',
|
||||
'created_at', 'updated_at',
|
||||
]
|
||||
|
||||
def get_variety_name(self, obj):
|
||||
return obj.fertilization_plan.variety.name
|
||||
|
||||
def get_crop_name(self, obj):
|
||||
return obj.fertilization_plan.variety.crop.name
|
||||
|
||||
def get_group_count(self, obj):
|
||||
return obj.groups.count()
|
||||
|
||||
def get_field_count(self, obj):
|
||||
return obj.distributiongroupfield_set.count()
|
||||
def get_trip_count(self, obj):
|
||||
return obj.trips.count()
|
||||
|
||||
|
||||
class DistributionPlanReadSerializer(serializers.ModelSerializer):
|
||||
fertilization_plan = FertilizationPlanForDistributionSerializer(read_only=True)
|
||||
groups = DistributionGroupReadSerializer(many=True, read_only=True)
|
||||
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 = DistributionPlan
|
||||
fields = ['id', 'name', 'fertilization_plan', 'groups', 'unassigned_fields', 'created_at', 'updated_at']
|
||||
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 = obj.distributiongroupfield_set.values_list('field_id', flat=True)
|
||||
plan_field_ids = obj.fertilization_plan.entries.values_list('field_id', flat=True).distinct()
|
||||
from apps.fields.models import Field as F
|
||||
unassigned = F.objects.filter(id__in=plan_field_ids).exclude(id__in=assigned_ids).order_by('display_order', 'id')
|
||||
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]
|
||||
|
||||
class DistributionPlanWriteSerializer(serializers.ModelSerializer):
|
||||
fertilization_plan_id = serializers.IntegerField(write_only=True)
|
||||
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 = DistributionPlan
|
||||
fields = ['id', 'name', 'fertilization_plan_id', 'groups']
|
||||
model = DeliveryPlan
|
||||
fields = ['id', 'year', 'name', 'groups', 'trips']
|
||||
|
||||
def create(self, validated_data):
|
||||
groups_data = validated_data.pop('groups', [])
|
||||
plan = DistributionPlan.objects.create(**validated_data)
|
||||
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 = DistributionGroup.objects.create(
|
||||
distribution_plan=plan,
|
||||
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', []):
|
||||
DistributionGroupField.objects.create(
|
||||
distribution_plan=plan,
|
||||
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'],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user