diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index fc7fa8e9..53d4daad 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -983,10 +983,18 @@ class MlogbwCreateUpdateSerializer(CustomModelSerializer): mlogbw = self.save_ftest(mlogbw, ftest_data) return mlogbw + @transaction.atomic def update(self, instance, validated_data): + old_number = instance.number validated_data.pop("mlogb") ftest_data = validated_data.pop("ftest", None) mlogbw:Mlogbw = super().update(instance, validated_data) + if ( + mlogbw.number != old_number + and mlogbw.mlogb.material_out_id is not None + and mlogbw.wpr is not None + ): + mlogbw.wpr.change_number(mlogbw.number) if ftest_data: mlogbw = self.save_ftest(mlogbw, ftest_data) elif ftest_data is None: diff --git a/apps/wpm/tests/test_mlogbw_number.py b/apps/wpm/tests/test_mlogbw_number.py new file mode 100644 index 00000000..d6720918 --- /dev/null +++ b/apps/wpm/tests/test_mlogbw_number.py @@ -0,0 +1,80 @@ +from types import SimpleNamespace +from unittest.mock import MagicMock, patch + +from django.test import SimpleTestCase + +from apps.wpm.serializers import MlogbwCreateUpdateSerializer +from apps.wpmw.models import Wpr + + +class MlogbwNumberUpdateTests(SimpleTestCase): + @staticmethod + def _apply_update(instance, validated_data): + instance.number = validated_data["number"] + return instance + + @patch("apps.wpm.serializers.CustomModelSerializer.update") + def test_output_number_update_changes_linked_wpr_number(self, base_update): + base_update.side_effect = self._apply_update + wpr = MagicMock() + instance = SimpleNamespace( + number="OLD-001", + mlogb=SimpleNamespace(material_out_id="material-out"), + wpr=wpr, + ftest=None, + ) + + MlogbwCreateUpdateSerializer.update.__wrapped__( + MlogbwCreateUpdateSerializer(), + instance, + {"mlogb": instance.mlogb, "number": "NEW-001"}, + ) + + wpr.change_number.assert_called_once_with("NEW-001") + + @patch("apps.wpm.serializers.CustomModelSerializer.update") + def test_input_number_update_does_not_change_wpr_number(self, base_update): + base_update.side_effect = self._apply_update + wpr = MagicMock() + instance = SimpleNamespace( + number="OLD-001", + mlogb=SimpleNamespace(material_out_id=None), + wpr=wpr, + ftest=None, + ) + + MlogbwCreateUpdateSerializer.update.__wrapped__( + MlogbwCreateUpdateSerializer(), + instance, + {"mlogb": instance.mlogb, "number": "NEW-001"}, + ) + + wpr.change_number.assert_not_called() + + @patch("apps.wpmw.models.MIOItemw.objects.filter") + @patch("apps.wpmw.models.Handoverbw.objects.filter") + @patch("apps.wpmw.models.Mlogbw.objects.filter") + @patch("apps.wpmw.models.Wpr.objects.filter") + def test_wpr_number_change_updates_all_number_copies( + self, + wpr_filter, + mlogbw_filter, + handoverbw_filter, + mioitemw_filter, + ): + conflict_qs = MagicMock() + conflict_qs.exists.return_value = False + current_qs = MagicMock() + wpr_filter.side_effect = [conflict_qs, current_qs] + mlogbw_qs = mlogbw_filter.return_value + handoverbw_qs = handoverbw_filter.return_value + mioitemw_qs = mioitemw_filter.return_value + wpr = Wpr(id="wpr-id", number="OLD-001") + + wpr.change_number("NEW-001") + + current_qs.update.assert_called_once_with(number="NEW-001") + mlogbw_qs.update.assert_called_once_with(number="NEW-001") + handoverbw_qs.update.assert_called_once_with(number="NEW-001") + mioitemw_qs.update.assert_called_once_with(number="NEW-001") + self.assertEqual(wpr.number, "NEW-001") diff --git a/apps/wpmw/models.py b/apps/wpmw/models.py index a655e2e0..bc5832ba 100644 --- a/apps/wpmw/models.py +++ b/apps/wpmw/models.py @@ -33,6 +33,19 @@ class Wpr(BaseModel): data = models.JSONField(verbose_name="数据", default=dict, blank=True) pre_info = models.JSONField(verbose_name="预处理信息", default=dict, blank=True, null=True) + def change_number(self, new_number): + """修改产品编号,并同步所有保存了编号副本的关联明细。""" + if self.number == new_number: + return + if Wpr.objects.filter(number=new_number).exists(): + raise ParseError("新编号已存在,不可使用") + + Wpr.objects.filter(id=self.id).update(number=new_number) + Mlogbw.objects.filter(wpr=self).update(number=new_number) + Handoverbw.objects.filter(wpr=self).update(number=new_number) + MIOItemw.objects.filter(wpr=self).update(number=new_number) + self.number = new_number + @classmethod def change_or_new( cls, wpr=None, number=None, mb=None, wm=None, old_mb=None, diff --git a/apps/wpmw/views.py b/apps/wpmw/views.py index 60c51b69..e72a989c 100644 --- a/apps/wpmw/views.py +++ b/apps/wpmw/views.py @@ -63,15 +63,8 @@ class WprViewSet(BulkUpdateModelMixin, CustomListModelMixin, CustomRetrieveModel vdata = sr.validated_data new_number = vdata["new_number"] old_number = vdata["old_number"] - if Wpr.objects.filter(number=new_number).exists(): - raise ParseError("新编号已存在,不可使用") wpr = Wpr.objects.get(number=old_number) - from apps.wpm.models import Mlogbw, Handoverbw - from apps.inm.models import MIOItemw - Wpr.objects.filter(id=wpr.id).update(number=new_number) - Mlogbw.objects.filter(wpr=wpr).update(number=new_number) - Handoverbw.objects.filter(wpr=wpr).update(number=new_number) - MIOItemw.objects.filter(wpr=wpr).update(number=new_number) + wpr.change_number(new_number) return Response() @action(methods=["post"], detail=False, perms_map={"post": "*"}, serializer_class=WprNewSerializer)