Move unspread fertilization entries on variety change
This commit is contained in:
@@ -3,9 +3,10 @@ from decimal import Decimal
|
||||
from django.db import transaction
|
||||
from django.db.models import Sum
|
||||
|
||||
from apps.materials.stock_service import create_reserves_for_plan
|
||||
from apps.materials.models import StockTransaction
|
||||
from apps.workrecords.services import sync_spreading_work_record
|
||||
from .models import FertilizationEntry, SpreadingSessionItem
|
||||
from .models import FertilizationEntry, FertilizationPlan, SpreadingSessionItem
|
||||
|
||||
|
||||
def sync_actual_bags_for_pairs(year, field_fertilizer_pairs):
|
||||
@@ -56,3 +57,51 @@ def sync_stock_uses_for_spreading_session(session):
|
||||
fertilization_plan=None,
|
||||
spreading_item=item,
|
||||
)
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
def move_unspread_entries_for_variety_change(change):
|
||||
moved_count = 0
|
||||
old_variety_id = change.old_variety_id
|
||||
new_variety = change.new_variety
|
||||
if old_variety_id is None or new_variety is None:
|
||||
return 0
|
||||
|
||||
old_plans = (
|
||||
FertilizationPlan.objects
|
||||
.filter(
|
||||
year=change.year,
|
||||
variety_id=old_variety_id,
|
||||
entries__field_id=change.field_id,
|
||||
entries__actual_bags__isnull=True,
|
||||
)
|
||||
.distinct()
|
||||
.prefetch_related('entries')
|
||||
)
|
||||
|
||||
for old_plan in old_plans:
|
||||
entries_to_move = list(
|
||||
old_plan.entries.filter(
|
||||
field_id=change.field_id,
|
||||
actual_bags__isnull=True,
|
||||
).order_by('id')
|
||||
)
|
||||
if not entries_to_move:
|
||||
continue
|
||||
|
||||
new_plan = FertilizationPlan.objects.create(
|
||||
name=f'{change.year}年度 {new_variety.name} 施肥計画(品種変更移動)',
|
||||
year=change.year,
|
||||
variety=new_variety,
|
||||
calc_settings=old_plan.calc_settings,
|
||||
)
|
||||
|
||||
FertilizationEntry.objects.filter(
|
||||
id__in=[entry.id for entry in entries_to_move]
|
||||
).update(plan=new_plan)
|
||||
|
||||
create_reserves_for_plan(old_plan)
|
||||
create_reserves_for_plan(new_plan)
|
||||
moved_count += len(entries_to_move)
|
||||
|
||||
return moved_count
|
||||
|
||||
@@ -59,7 +59,12 @@ def handle_plan_variety_change(plan: Plan, *, old_variety, new_variety, reason:
|
||||
|
||||
|
||||
def process_plan_variety_change(change: PlanVarietyChange):
|
||||
"""後続 issue で施肥計画・田植え計画への移動処理を追加する入口。"""
|
||||
from apps.fertilizer.services import move_unspread_entries_for_variety_change
|
||||
|
||||
moved_count = move_unspread_entries_for_variety_change(change)
|
||||
if moved_count != change.moved_entry_count:
|
||||
change.moved_entry_count = moved_count
|
||||
change.save(update_fields=['moved_entry_count'])
|
||||
return change
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,10 @@ from django.contrib.auth import get_user_model
|
||||
from django.test import TestCase
|
||||
from rest_framework.test import APIRequestFactory, force_authenticate
|
||||
|
||||
from apps.fertilizer.models import FertilizationEntry, FertilizationPlan, Fertilizer
|
||||
from apps.fields.models import Field
|
||||
from apps.materials.models import Material, StockTransaction
|
||||
from apps.materials.stock_service import create_reserves_for_plan
|
||||
from .models import Crop, Plan, PlanVarietyChange, Variety
|
||||
from .serializers import PlanSerializer
|
||||
from .views import PlanViewSet
|
||||
@@ -34,6 +37,15 @@ class PlanVarietyChangeTests(TestCase):
|
||||
variety=self.old_variety,
|
||||
notes='',
|
||||
)
|
||||
self.other_field = Field.objects.create(
|
||||
name='足川南',
|
||||
address='高知県高岡郡',
|
||||
area_tan='0.8000',
|
||||
area_m2=800,
|
||||
owner_name='吉田',
|
||||
group_name='南',
|
||||
display_order=2,
|
||||
)
|
||||
|
||||
def test_serializer_update_creates_history_when_variety_changes(self):
|
||||
serializer = PlanSerializer(
|
||||
@@ -90,3 +102,102 @@ class PlanVarietyChangeTests(TestCase):
|
||||
change = PlanVarietyChange.objects.get(plan=self.plan)
|
||||
self.assertEqual(change.old_variety_id, self.old_variety.id)
|
||||
self.assertEqual(change.new_variety_id, self.new_variety.id)
|
||||
|
||||
def test_serializer_update_moves_only_unspread_fertilizer_entries(self):
|
||||
material_unspread = Material.objects.create(
|
||||
name='高度化成14号',
|
||||
material_type=Material.MaterialType.FERTILIZER,
|
||||
)
|
||||
material_partial = Material.objects.create(
|
||||
name='分げつ一発',
|
||||
material_type=Material.MaterialType.FERTILIZER,
|
||||
)
|
||||
fertilizer_unspread = Fertilizer.objects.create(
|
||||
name='高度化成14号',
|
||||
material=material_unspread,
|
||||
)
|
||||
fertilizer_partial = Fertilizer.objects.create(
|
||||
name='分げつ一発',
|
||||
material=material_partial,
|
||||
)
|
||||
old_fertilization_plan = FertilizationPlan.objects.create(
|
||||
name='2026年度 にこまる 元肥',
|
||||
year=2026,
|
||||
variety=self.old_variety,
|
||||
calc_settings=[{'fertilizer_id': fertilizer_unspread.id, 'method': 'per_tan', 'param': '1.0'}],
|
||||
)
|
||||
unspread_entry = FertilizationEntry.objects.create(
|
||||
plan=old_fertilization_plan,
|
||||
field=self.field,
|
||||
fertilizer=fertilizer_unspread,
|
||||
bags='4.00',
|
||||
actual_bags=None,
|
||||
)
|
||||
partial_entry = FertilizationEntry.objects.create(
|
||||
plan=old_fertilization_plan,
|
||||
field=self.field,
|
||||
fertilizer=fertilizer_partial,
|
||||
bags='3.00',
|
||||
actual_bags='1.0000',
|
||||
)
|
||||
untouched_entry = FertilizationEntry.objects.create(
|
||||
plan=old_fertilization_plan,
|
||||
field=self.other_field,
|
||||
fertilizer=fertilizer_unspread,
|
||||
bags='2.00',
|
||||
actual_bags=None,
|
||||
)
|
||||
create_reserves_for_plan(old_fertilization_plan)
|
||||
|
||||
serializer = PlanSerializer(
|
||||
instance=self.plan,
|
||||
data={'variety': self.new_variety.id},
|
||||
partial=True,
|
||||
)
|
||||
self.assertTrue(serializer.is_valid(), serializer.errors)
|
||||
serializer.save()
|
||||
|
||||
change = PlanVarietyChange.objects.get(plan=self.plan)
|
||||
self.assertEqual(change.moved_entry_count, 1)
|
||||
|
||||
old_fertilization_plan.refresh_from_db()
|
||||
new_plan = FertilizationPlan.objects.exclude(id=old_fertilization_plan.id).get(
|
||||
year=2026,
|
||||
variety=self.new_variety,
|
||||
)
|
||||
self.assertEqual(
|
||||
new_plan.name,
|
||||
f'2026年度 {self.new_variety.name} 施肥計画(品種変更移動)',
|
||||
)
|
||||
self.assertEqual(new_plan.calc_settings, old_fertilization_plan.calc_settings)
|
||||
|
||||
unspread_entry.refresh_from_db()
|
||||
partial_entry.refresh_from_db()
|
||||
untouched_entry.refresh_from_db()
|
||||
|
||||
self.assertEqual(unspread_entry.plan_id, new_plan.id)
|
||||
self.assertEqual(partial_entry.plan_id, old_fertilization_plan.id)
|
||||
self.assertEqual(untouched_entry.plan_id, old_fertilization_plan.id)
|
||||
|
||||
old_reserves = list(
|
||||
StockTransaction.objects.filter(
|
||||
fertilization_plan=old_fertilization_plan,
|
||||
transaction_type=StockTransaction.TransactionType.RESERVE,
|
||||
).order_by('material__name')
|
||||
)
|
||||
new_reserves = list(
|
||||
StockTransaction.objects.filter(
|
||||
fertilization_plan=new_plan,
|
||||
transaction_type=StockTransaction.TransactionType.RESERVE,
|
||||
).order_by('material__name')
|
||||
)
|
||||
self.assertEqual(len(old_reserves), 2)
|
||||
self.assertEqual(len(new_reserves), 1)
|
||||
self.assertEqual(
|
||||
{(reserve.material_id, reserve.quantity) for reserve in old_reserves},
|
||||
{
|
||||
(material_partial.id, partial_entry.bags),
|
||||
(material_unspread.id, untouched_entry.bags),
|
||||
},
|
||||
)
|
||||
self.assertEqual(new_reserves[0].quantity, unspread_entry.bags)
|
||||
|
||||
Reference in New Issue
Block a user