From c997cba6a58b42fd9a53fde167bfef936d536583 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 2 Sep 2025 11:23:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20route=E9=87=87=E7=94=A8=E5=BC=95?= =?UTF-8?q?=E7=94=A8=E6=96=B9=E5=BC=8F=E5=85=81=E8=AE=B8=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E5=88=9B=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/mtm/filters.py | 3 +- .../mtm/migrations/0062_auto_20250902_1122.py | 24 +++++++++++++ apps/mtm/models.py | 3 +- apps/mtm/serializers.py | 36 +++++++++++++------ apps/mtm/views.py | 21 ++++++++--- 5 files changed, 69 insertions(+), 18 deletions(-) create mode 100644 apps/mtm/migrations/0062_auto_20250902_1122.py diff --git a/apps/mtm/filters.py b/apps/mtm/filters.py index 334e154f..52d45059 100644 --- a/apps/mtm/filters.py +++ b/apps/mtm/filters.py @@ -58,5 +58,6 @@ class RouteFilter(filters.FilterSet): "mgroup": ["exact", "in", "isnull"], "mgroup__name": ["exact", "contains"], "mgroup__belong_dept": ["exact"], - "mgroup__belong_dept__name": ["exact", "contains"] + "mgroup__belong_dept__name": ["exact", "contains"], + "from_route": ["exact", "isnull"], } diff --git a/apps/mtm/migrations/0062_auto_20250902_1122.py b/apps/mtm/migrations/0062_auto_20250902_1122.py new file mode 100644 index 00000000..1ab1057a --- /dev/null +++ b/apps/mtm/migrations/0062_auto_20250902_1122.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.12 on 2025-09-02 03:22 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('mtm', '0061_material_img'), + ] + + operations = [ + migrations.AddField( + model_name='route', + name='from_route', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='route_f', to='mtm.route', verbose_name='来源路线'), + ), + migrations.AlterField( + model_name='route', + name='parent', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='route_parent', to='mtm.route', verbose_name='上级路线'), + ), + ] diff --git a/apps/mtm/models.py b/apps/mtm/models.py index d382d310..a4bb4b7f 100644 --- a/apps/mtm/models.py +++ b/apps/mtm/models.py @@ -415,8 +415,9 @@ class Route(CommonADModel): batch_bind = models.BooleanField('是否绑定批次', default=True) materials = models.ManyToManyField(Material, verbose_name='关联辅助物料', related_name="route_materials", through="mtm.routemat", blank=True) - parent = models.ForeignKey('self', verbose_name='上级路线', on_delete=models.CASCADE, null=True, blank=True) + parent = models.ForeignKey('self', verbose_name='上级路线', on_delete=models.CASCADE, null=True, blank=True, related_name="route_parent") params_json = models.JSONField('工艺参数', default=dict, blank=True) + from_route = models.ForeignKey('self', verbose_name='来源路线', on_delete=models.SET_NULL, null=True, blank=True, related_name="route_f") def __str__(self): x = "" diff --git a/apps/mtm/serializers.py b/apps/mtm/serializers.py index fed32f54..c3124339 100644 --- a/apps/mtm/serializers.py +++ b/apps/mtm/serializers.py @@ -247,7 +247,7 @@ class RouteSerializer(CustomModelSerializer): # if material and process and Route.objects.filter(material=material, process=process).exists(): # raise ValidationError('已选择该工序!!') with transaction.atomic(): - instance = super().create(validated_data) + instance:Route = super().create(validated_data) material_out = instance.material_out if material_out: if material_out.process is None: @@ -263,12 +263,16 @@ class RouteSerializer(CustomModelSerializer): if instance.material: instance.material_out = RouteSerializer.gen_material_out(instance, material_out_tracking, user=self.request.user) instance.save() - rx = Route.objects.filter(material_in=instance.material_in, material_out=instance.material_out, process=process).exclude(id=instance.id).first() + rx = Route.objects.filter( + material_in=instance.material_in, material_out=instance.material_out, + process=process).exclude(id=instance.id).order_by("create_time").first() if rx: - msg = "" - if rx.routepack: - msg = rx.routepack.name - raise ParseError(f"该工艺步骤已存在-{msg}") + instance.from_route = rx + instance.save() + # msg = "" + # if rx.routepack: + # msg = rx.routepack.name + # raise ParseError(f"该工艺步骤已存在-{msg}") return instance def update(self, instance, validated_data): @@ -294,12 +298,16 @@ class RouteSerializer(CustomModelSerializer): if instance.material: instance.material_out = RouteSerializer.gen_material_out(instance, material_out_tracking, user=self.request.user) instance.save() - rx = Route.objects.filter(material_in=instance.material_in, material_out=instance.material_out, process=process).exclude(id=instance.id).first() + rx = Route.objects.filter( + material_in=instance.material_in, material_out=instance.material_out, + process=process).exclude(id=instance.id).order_by("create_time").first() if rx: - msg = "" - if rx.routepack: - msg = rx.routepack.name - raise ParseError(f"该工艺步骤已存在-{msg}") + instance.from_route = rx + instance.save() + # msg = "" + # if rx.routepack: + # msg = rx.routepack.name + # raise ParseError(f"该工艺步骤已存在-{msg}") return instance def to_representation(self, instance): @@ -330,6 +338,12 @@ class RouteMatSerializer(CustomModelSerializer): model = RouteMat fields = "__all__" read_only_fields = EXCLUDE_FIELDS_BASE + + def validate(self, attrs): + route:Route = attrs["route"] + if route.from_route is not None: + raise ParseError("该工艺步骤引用其他步骤,无法修改") + return attrs class MaterialExportSerializer(CustomModelSerializer): diff --git a/apps/mtm/views.py b/apps/mtm/views.py index 1ad675d3..36508f4a 100644 --- a/apps/mtm/views.py +++ b/apps/mtm/views.py @@ -368,12 +368,23 @@ class RouteViewSet(CustomModelViewSet): select_related_fields = ['material', 'process', 'material_in', 'material_out', 'mgroup', 'routepack'] - def update(self, request, *args, **kwargs): - obj:Route = self.get_object() - routepack = obj.routepack + @transaction.atomic + def perform_update(self, serializer): + ins:Route = self.get_object() + if ins.from_route is not None: + raise ParseError('该工艺步骤引用其他步骤, 无法编辑') + old_m_in, old_m_out, process = ins.material_in, ins.material_out, ins.process + routepack = ins.routepack if routepack and routepack.state != RoutePack.RP_S_CREATE: - raise ParseError('该状态下不可编辑') - return super().update(request, *args, **kwargs) + raise ParseError('该工艺路线非创建中不可编辑') + ins_n:Route = serializer.save() + if ins_n.material_in != old_m_in or ins_n.material_out != old_m_out or ins_n.process != process: + raise ParseError("该工艺步骤被其他步骤引用, 无法修改关键信息") + + def perform_destroy(self, instance:Route): + if Route.objects.filter(from_route=instance).exists(): + raise ParseError('该工艺步骤被其他步骤引用,无法删除') + return super().perform_destroy(instance) class SruleViewSet(CustomModelViewSet):