From 164f3f16bb1d89780758ad992110526c3c8963f6 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 13 Nov 2025 10:31:16 +0800 Subject: [PATCH 01/16] =?UTF-8?q?feat:=20=E6=8B=A6=E6=88=AAnew=5Fbatch?= =?UTF-8?q?=E7=9A=84=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/serializers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index 4e2dd35e..91da7ebd 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -1208,6 +1208,8 @@ class HandoverSerializer(CustomModelSerializer): new_defect = new_wm.defect if not attrs.get("new_batch", None): raise ParseError("必须指定合并后的批次") + if 'undefined' in attrs['new_batch'] or 'null' in attrs['new_batch']: + raise ParseError("新批次号错误!") wm:WMaterial = attrs.get('wm', None) From 1d741719880d1766308ce81a14a9698670d09ae8 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 13 Nov 2025 10:54:46 +0800 Subject: [PATCH 02/16] =?UTF-8?q?fix:=20material=E5=A2=9E=E5=8A=A0count=5F?= =?UTF-8?q?=5Fgt=E7=AD=89=E6=9F=A5=E8=AF=A2=E6=9D=A1=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/mtm/filters.py | 3 +++ apps/mtm/views.py | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/mtm/filters.py b/apps/mtm/filters.py index 72be6443..138b48de 100644 --- a/apps/mtm/filters.py +++ b/apps/mtm/filters.py @@ -6,6 +6,9 @@ from rest_framework.exceptions import ParseError class MaterialFilter(filters.FilterSet): tag = filters.CharFilter(method='filter_tag', label="low_inm:库存不足") + count__gt = filters.NumberFilter(field_name='count', lookup_expr='gt') + count_mb__gt = filters.NumberFilter(field_name='count_mb', lookup_expr='gt') + count_wm__gt = filters.NumberFilter(field_name='count_wm', lookup_expr='gt') class Meta: model = Material diff --git a/apps/mtm/views.py b/apps/mtm/views.py index 67b240f5..ec7c93e7 100644 --- a/apps/mtm/views.py +++ b/apps/mtm/views.py @@ -46,12 +46,12 @@ class MaterialViewSet(CustomModelViewSet): qs = super().get_queryset() if self.action in ["list", "retrieve"]: return qs.annotate( - count_wm=Coalesce( + count_mb=Coalesce( Sum('mb_m__count', filter=Q(mb_m__state=10)), Value(0), output_field=DecimalField() ), - count_mb=Coalesce( + count_wm=Coalesce( Sum('wm_m__count', filter=Q(wm_m__state=10)), Value(0), output_field=DecimalField() From 230b71f0aafd6ae4833f4a287a4c4f3c405fc416 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 13 Nov 2025 10:59:32 +0800 Subject: [PATCH 03/16] =?UTF-8?q?feat:=20=E5=8F=8D=E5=90=91=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E6=97=B6=E5=BF=BD=E7=95=A5=E6=98=8E=E7=BB=86=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/inm/services.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/inm/services.py b/apps/inm/services.py index a424c738..49df8dcf 100644 --- a/apps/inm/services.py +++ b/apps/inm/services.py @@ -272,7 +272,7 @@ class InmService: """ 更新库存, 支持反向操作 """ - if not MIOItem.objects.filter(mio=instance).exists(): + if is_reverse is False and not MIOItem.objects.filter(mio=instance).exists(): raise ParseError("出入库记录缺失明细,无法操作") if instance.type == MIO.MIO_TYPE_PUR_IN: # 需要更新订单 From af09d35177ad7fc3b126d8b0598b453f864ed07e Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 13 Nov 2025 11:01:50 +0800 Subject: [PATCH 04/16] =?UTF-8?q?feat:=20=E5=8F=8D=E5=90=91=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E6=97=B6=E5=BF=BD=E7=95=A5=E6=98=8E=E7=BB=86=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/inm/services.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/inm/services.py b/apps/inm/services.py index 49df8dcf..3b968010 100644 --- a/apps/inm/services.py +++ b/apps/inm/services.py @@ -347,8 +347,6 @@ class InmService: out = -1 """ mioitems = MIOItem.objects.filter(mio=instance) - if not mioitems.exists(): - raise ParseError("未填写物料明细") for i in mioitems: cls.update_mb_item(i, in_or_out, 'count') From 509fdb3656d38aa132c106f8bc7e6836b98c6049 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 13 Nov 2025 11:24:12 +0800 Subject: [PATCH 05/16] =?UTF-8?q?fix:=20material=20get=5Fqueryset=E5=BF=98?= =?UTF-8?q?=E8=AE=B0return=20qs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/mtm/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/mtm/views.py b/apps/mtm/views.py index ec7c93e7..e958e01b 100644 --- a/apps/mtm/views.py +++ b/apps/mtm/views.py @@ -62,8 +62,8 @@ class MaterialViewSet(CustomModelViewSet): output_field=DecimalField() ) ) - - + return qs + def perform_destroy(self, instance): from apps.inm.models import MaterialBatch if MaterialBatch.objects.filter(material=instance).exists(): From 6c240179057bda09cadfbc47cae3b81914aea19b Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 13 Nov 2025 13:41:03 +0800 Subject: [PATCH 06/16] =?UTF-8?q?feat:=20base=20=E5=8F=AF=E8=B7=B3?= =?UTF-8?q?=E8=BF=87=E7=9F=AD=E4=BF=A1=E5=8F=91=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/utils/sms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/utils/sms.py b/apps/utils/sms.py index 43aea182..17526cf5 100644 --- a/apps/utils/sms.py +++ b/apps/utils/sms.py @@ -15,7 +15,7 @@ def send_sms(phone: str, template_code: int, template_param: dict): from aliyunsdkcore.request import CommonRequest config = get_sysconfig() if config.get("sms", {}).get('enabled', False) is False: - raise ParseError("短信发送功能未启用") + return False, {} try: client = AcsClient(config['sms']['xn_key'], config['sms']['xn_secret'], 'default') From 53db1cdc35d4a90e9bf81e050910f941f40217a4 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 13 Nov 2025 13:46:04 +0800 Subject: [PATCH 07/16] =?UTF-8?q?feat:=20base=20=E6=B7=BB=E5=8A=A0ticketmi?= =?UTF-8?q?xin=E5=8F=AF=E9=9B=86=E6=88=90=E5=88=B0viewset=E4=B8=8B?= =?UTF-8?q?=E4=BB=A5=E6=94=AF=E6=8C=81=E5=B7=A5=E4=BD=9C=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wf/mixins.py | 60 +++++++++++++++++++++++++++++++++++++++------ apps/wf/services.py | 5 +++- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/apps/wf/mixins.py b/apps/wf/mixins.py index a17a49e6..e9fb1b87 100644 --- a/apps/wf/mixins.py +++ b/apps/wf/mixins.py @@ -1,11 +1,18 @@ from apps.wf.models import Workflow, Ticket, State from rest_framework.exceptions import ParseError from apps.wf.services import WfService +from apps.system.models import User class TicketMixin: + """ + 可挂载到正常model,使其支持工作流 + model添加ticket字段 + serializer添加ticket_ + 该处会修改perform_create和perform_update方法,注意! + """ workflow_key = None ticket_auto_submit_on_update = True - ticket_data_save_keys = [] + ticket_data_save_fields = [] def get_workflow_key(self, instance): return self.workflow_key @@ -13,19 +20,58 @@ class TicketMixin: def should_create_ticket(self, instance): return True + def gen_ticket_data(self, instance): + ticket_data = {"t_model": instance.__class__.__name__, "t_id": instance.id} + if self.ticket_data_save_fields: + for field in self.ticket_data_save_fields: + if '.' in field: + attr_list = field.split('.') + expr = instance + for a in attr_list: + expr = getattr(expr, a) + ticket_data[field] = expr + else: + ticket_data[field] = getattr(instance, field) + return ticket_data + + def perform_update(self, serializer): + ins = serializer.save() + if ins.ticket and self.ticket_auto_submit_on_update: + source_state:State = ins.ticket.state + if source_state.type != State.STATE_TYPE_START: + raise ParseError('该工单已开始流转,不可修改') + transition = WfService.get_state_transitions(source_state).first() + ticket_data = self.gen_ticket_data(ins) + WfService.handle_ticket(ticket=ins.ticket, transition=transition, new_ticket_data=ticket_data, + handler=self.request.user, oinfo=self.request.data) + def perform_create(self, serializer): ins = serializer.save() - if self.workflow_key: + handler:User = self.request.user + if self.should_create_ticket(ins): + workflow_key = self.get_workflow_key(ins) + if not workflow_key: + raise ParseError('工作流异常:必须赋值workflow_key') try: - wf = Workflow.objects.get(key=self.workflow_key) + wf = Workflow.objects.get(key=workflow_key) except Exception as e: - raise ParseError(f'工作流{self.workflow_key}异常:{e}') + raise ParseError(f'工作流{workflow_key}异常:{e}') + + # 开始创建工单 source_state: State = WfService.get_workflow_start_state(wf) transitions = WfService.get_state_transitions(source_state) if transitions.count() == 1: transition = transitions.first() - + ticket_data = self.gen_ticket_data(ins) + WfService.handle_ticket(ticket=None, transition=transition, new_ticket_data=ticket_data, + handler=handler, oinfo=self.request.data) else: - raise ParseError(f'工作流{self.workflow_key}异常:起始状态{source_state}有多个后续状态;不可直接创建') + raise ParseError(f'工作流{workflow_key}异常:有多个后续状态;不可处理') - \ No newline at end of file + def perform_destroy(self, instance): + ticket = instance.ticket + if ticket and ticket.state.type != State.STATE_TYPE_START: + raise ParseError('该工单已开始流转,不可删除') + instance.delete() + ticket.delete() + \ No newline at end of file diff --git a/apps/wf/services.py b/apps/wf/services.py index 60397f88..7a2f1090 100755 --- a/apps/wf/services.py +++ b/apps/wf/services.py @@ -332,7 +332,10 @@ class WfService(object): title_template = ticket.workflow.title_template if title_template: all_ticket_data = {**oinfo, **new_ticket_data} - ticket_title = title_template.format(**all_ticket_data) + try: + ticket_title = title_template.format(**all_ticket_data) + except KeyError as e: + raise ParseError(f"工单标题模板中存在未定义的变量:{e}") sn = WfService.get_ticket_sn(ticket.workflow) # 流水号 ticket.sn = sn ticket.title = ticket_title From c21a39b52d5be75e20de3ef9625de5a25b0aa00c Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 13 Nov 2025 14:12:04 +0800 Subject: [PATCH 08/16] =?UTF-8?q?feat:=20supplieraudit=E9=87=87=E7=94=A8?= =?UTF-8?q?=E6=96=B0=E5=B7=A5=E4=BD=9C=E6=B5=81=E6=8C=82=E8=BD=BD=E6=96=B9?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/pum/serializers.py | 3 ++- apps/pum/views.py | 13 +++---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/apps/pum/serializers.py b/apps/pum/serializers.py index 954e16c6..a9058785 100644 --- a/apps/pum/serializers.py +++ b/apps/pum/serializers.py @@ -150,4 +150,5 @@ class SupplierAuditSerializer(CustomModelSerializer): ticket_ = TicketSimpleSerializer(source="ticket", read_only=True) class Meta: model = SupplierAudit - fields = "__all__" \ No newline at end of file + fields = "__all__" + read_only_fields = EXCLUDE_FIELDS_BASE + ['ticket'] \ No newline at end of file diff --git a/apps/pum/views.py b/apps/pum/views.py index ec668a0b..2a48665b 100644 --- a/apps/pum/views.py +++ b/apps/pum/views.py @@ -11,7 +11,7 @@ from django.db import transaction from rest_framework.response import Response from django.utils import timezone from apps.pum.services import PumService -from apps.wf.models import Ticket +from apps.wf.mixins import TicketMixin # Create your views here. @@ -31,7 +31,7 @@ class SupplierViewSet(CustomModelViewSet): raise ParseError('该供应商存在采购订单不可删除') instance.delete() -class SupplierAuditViewSet(CustomModelViewSet): +class SupplierAuditViewSet(TicketMixin, CustomModelViewSet): """ list: 供应商审核 @@ -40,14 +40,7 @@ class SupplierAuditViewSet(CustomModelViewSet): queryset = SupplierAudit.objects.all() serializer_class = SupplierAuditSerializer search_fields = ['name', 'material_name', 'material_cate'] - - def perform_destroy(self, instance): - ticket:Ticket = instance.ticket - if ticket and ticket.state.type != 1: - raise ParseError('该记录关联的工单已被处理,不可删除') - instance.delete() - if ticket: - ticket.delete() + workflow_key = "wf_supplieraudit" class PuPlanViewSet(CustomModelViewSet): """ From 7c3e63668ec0e06b5a617068689706890ce85440 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 13 Nov 2025 16:29:55 +0800 Subject: [PATCH 09/16] =?UTF-8?q?feat:=20=E6=8F=90=E4=BE=9B=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E7=BC=96=E5=8F=B7=E7=9A=84=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/serializers.py | 7 ++++++- apps/wpm/views.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index 91da7ebd..991d7413 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -1549,4 +1549,9 @@ class MlogQuickSerializer(serializers.Serializer): mgroup = serializers.CharField(label="工段ID") route = serializers.CharField(label="工艺步骤ID", required=False) mtask = serializers.CharField(label="任务ID", required=False) - handle_user = serializers.CharField(label="操作人员ID") \ No newline at end of file + handle_user = serializers.CharField(label="操作人员ID") + + +class BatchChangeSerializer(serializers.Serializer): + old_batch = serializers.CharField(label="原批号") + new_batch = serializers.CharField(label="新批号") \ No newline at end of file diff --git a/apps/wpm/views.py b/apps/wpm/views.py index d6c0ca6a..062fb212 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -49,6 +49,7 @@ from .serializers import ( MlogQuickSerializer, MlogbwStartTestSerializer, HandoverListSerializer, + BatchChangeSerializer ) from .services import mlog_submit, handover_submit, mlog_revert, get_batch_dag, handover_revert from apps.wpm.services import mlog_submit_validate, generate_new_batch @@ -203,6 +204,35 @@ class WMaterialViewSet(CustomListModelMixin, CustomGenericViewSet): defect_ids = queryset.values_list("defect", flat=True).distinct() return Response(DefectSerializer(Defect.objects.filter(id__in=defect_ids), many=True).data) + @action(methods=["post"], detail=False, perms_map={"post": "*"}, serializer_class=BatchChangeSerializer) + @transaction.atomic + def change_batch(self, request, *args, **kwargs): + """修改批次号""" + sr = BatchChangeSerializer(data=request.data) + sr.is_valid(raise_exception=True) + vdata = sr.validated_data + new_batch = vdata["new_batch"] + old_batch = vdata["old_batch"] + if BatchSt.objects.filter(batch=new_batch).exists(): + raise ParseError("新批次号已存在,不可使用") + from apps.cm.models import LableMat + from apps.inm.models import MIOItem, MaterialBatch, MaterialBatchA, MIOItemA + from apps.qm.models import FtestWork + LableMat.objects.filter(batch=old_batch).update(batch=new_batch) + MIOItem.objects.filter(batch=old_batch).update(batch=new_batch) + MIOItemA.objects.filter(batch=old_batch).update(batch=new_batch) + MaterialBatch.objects.filter(batch=old_batch).update(batch=new_batch) + MaterialBatchA.objects.filter(batch=old_batch).update(batch=new_batch) + FtestWork.objects.filter(batch=old_batch).update(batch=new_batch) + + Mlog.objects.filter(batch=old_batch).update(batch=new_batch) + Mlogb.objects.filter(batch=old_batch).update(batch=new_batch) + WMaterial.objects.filter(batch=old_batch).update(batch=new_batch) + Handover.objects.filter(batch=old_batch).update(batch=new_batch) + Handoverb.objects.filter(batch=old_batch).update(batch=new_batch) + Handover.objects.filter(new_batch=old_batch).update(new_batch=new_batch) + BatchSt.objects.filter(batch=old_batch).update(batch=new_batch) + return Response() class MlogViewSet(CustomModelViewSet): """ From 75305bd1fd2bf3bc6f9f1c385020823f2fbb8ec7 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 13 Nov 2025 16:48:08 +0800 Subject: [PATCH 10/16] =?UTF-8?q?feat:=20=E5=A2=9E=E5=BC=BA=E6=96=B0?= =?UTF-8?q?=E6=89=B9=E6=AC=A1=E5=8F=B7=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index 991d7413..23087b5d 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -1208,8 +1208,8 @@ class HandoverSerializer(CustomModelSerializer): new_defect = new_wm.defect if not attrs.get("new_batch", None): raise ParseError("必须指定合并后的批次") - if 'undefined' in attrs['new_batch'] or 'null' in attrs['new_batch']: - raise ParseError("新批次号错误!") + if 'undefined' in attrs['new_batch'] or 'null' in attrs['new_batch'] or '#' in attrs['new_batch']: + raise ParseError("新批次号含有不允许信息!") wm:WMaterial = attrs.get('wm', None) From 0141e539e75d99d61d1de9aa8f08bfe43562da63 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 14 Nov 2025 14:44:39 +0800 Subject: [PATCH 11/16] =?UTF-8?q?feat:=20=E5=AF=BC=E5=85=A5=E7=89=A9?= =?UTF-8?q?=E6=96=99=E6=98=8E=E7=BB=86=E6=94=AF=E6=8C=81=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E4=BB=8E=E5=90=8D=E7=A7=B0=E7=AD=89=E5=8C=B9=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/inm/services_daoru.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/apps/inm/services_daoru.py b/apps/inm/services_daoru.py index 97137496..3d93ece9 100644 --- a/apps/inm/services_daoru.py +++ b/apps/inm/services_daoru.py @@ -137,18 +137,27 @@ def daoru_mioitems(path:str, mio:MIO): mioitems = [] ind = 2 while sheet[f"a{ind}"].value: - batch = sheet[f"b{ind}"].value + batch = sheet[f"e{ind}"].value material_number = sheet[f"a{ind}"].value - try: - material = Material.objects.get(number=material_number) - except Exception as e: - raise ParseError(f"未找到物料:{material_number} {e}") + if material_number: + try: + material = Material.objects.get(number=material_number) + except Exception as e: + raise ParseError(f"未找到物料:{material_number} {e}") + else: + material_name = sheet[f"b{ind}"].value + material_model = sheet[f"c{ind}"].value + material_specification = sheet[f"d{ind}"].value + try: + material = Material.objects.get(name=material_name, model=material_model, specification=material_specification) + except Exception as e: + raise ParseError(f"未找到物料:{material_name} {material_model} {material_specification} {e}") if batch: pass else: batch = "无" - count = sheet[f"c{ind}"].value - warehouse_name = sheet[f"d{ind}"].value + count = sheet[f"f{ind}"].value + warehouse_name = sheet[f"g{ind}"].value try: warehouse = WareHouse.objects.get(name=warehouse_name) except Exception as e: From c739fcfd790ad1774bb820ed884dcc17e537fd89 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 14 Nov 2025 14:50:10 +0800 Subject: [PATCH 12/16] =?UTF-8?q?feat:=20=E5=AF=BC=E5=85=A5=E7=89=A9?= =?UTF-8?q?=E6=96=99=E6=98=8E=E7=BB=86=E6=94=AF=E6=8C=81=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E4=BB=8E=E5=90=8D=E7=A7=B0=E7=AD=89=E5=8C=B9=E9=85=8D2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/inm/services_daoru.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/inm/services_daoru.py b/apps/inm/services_daoru.py index 3d93ece9..3be01a06 100644 --- a/apps/inm/services_daoru.py +++ b/apps/inm/services_daoru.py @@ -146,8 +146,8 @@ def daoru_mioitems(path:str, mio:MIO): raise ParseError(f"未找到物料:{material_number} {e}") else: material_name = sheet[f"b{ind}"].value - material_model = sheet[f"c{ind}"].value - material_specification = sheet[f"d{ind}"].value + material_model = sheet[f"d{ind}"].value + material_specification = sheet[f"c{ind}"].value try: material = Material.objects.get(name=material_name, model=material_model, specification=material_specification) except Exception as e: From 6d030e2c06a8480b043ddea3b87e4d9baf2303cb Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 14 Nov 2025 14:53:51 +0800 Subject: [PATCH 13/16] =?UTF-8?q?feat:=20=E5=AF=BC=E5=85=A5=E7=89=A9?= =?UTF-8?q?=E6=96=99=E6=98=8E=E7=BB=86=E6=94=AF=E6=8C=81=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E4=BB=8E=E5=90=8D=E7=A7=B0=E7=AD=89=E5=8C=B9=E9=85=8D3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/inm/services_daoru.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/inm/services_daoru.py b/apps/inm/services_daoru.py index 3be01a06..a4c8421f 100644 --- a/apps/inm/services_daoru.py +++ b/apps/inm/services_daoru.py @@ -136,7 +136,7 @@ def daoru_mioitems(path:str, mio:MIO): mioitems = [] ind = 2 - while sheet[f"a{ind}"].value: + while sheet[f"a{ind}"].value or sheet[f"b{ind}"].value: batch = sheet[f"e{ind}"].value material_number = sheet[f"a{ind}"].value if material_number: From e7ebdb0e8ec5a8cd9f6305f2171eaaef98b3a283 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 17 Nov 2025 08:51:55 +0800 Subject: [PATCH 14/16] =?UTF-8?q?feat:=20=E6=A0=A1=E9=AA=8C=E6=94=B9?= =?UTF-8?q?=E7=89=88=E6=97=B6=E9=80=89=E6=8B=A9=E7=9A=84=E6=94=B9=E7=89=88?= =?UTF-8?q?=E7=89=A9=E6=96=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/serializers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index 23087b5d..42d3edba 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -1195,7 +1195,9 @@ class HandoverSerializer(CustomModelSerializer): if attrs["type"] == Handover.H_CHANGE: attrs["mtype"] = Handover.H_MERGE - if "material_changed" not in attrs: + if "material_changed" in attrs and attrs["material_changed"]: + pass + else: raise ParseError("必须指定改版后的物料") mtype = attrs["mtype"] From 8b74d0b12169f9f5ae4802111dc6d3ca1f50bcee Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 17 Nov 2025 13:57:10 +0800 Subject: [PATCH 15/16] =?UTF-8?q?fix:=20do=5Fin=20=E5=8F=AF=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E5=8F=96=E7=94=A8wm?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/inm/services.py | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/apps/inm/services.py b/apps/inm/services.py index 3b968010..f4039b7b 100644 --- a/apps/inm/services.py +++ b/apps/inm/services.py @@ -160,9 +160,9 @@ def do_in(item: MIOItem): for i in mias_list: material, batch, rate = i new_count = rate * item.count # 假设 item.count 存在 - action_list.append([material, batch, new_count, None]) + action_list.append([material, batch, new_count, None, None]) else: - action_list = [[item.material, item.batch, item.count, defect]] + action_list = [[item.material, item.batch, item.count, defect, item.wm]] production_dept = None @@ -170,28 +170,31 @@ def do_in(item: MIOItem): if is_zhj: xbatchs = [item.batch] for al in action_list: - xmaterial, xbatch, xcount, defect = al + xmaterial, xbatch, xcount, defect, xwm = al if xcount <= 0: raise ParseError("存在非正数!") xbatchs.append(xbatch) if material.into_wm: - wm_qs = WMaterial.objects.filter( - batch=xbatch, - material=xmaterial, - belong_dept=belong_dept, - mgroup=mgroup, - defect=defect, - state=WMaterial.WM_OK) - count_x = wm_qs.count() - if count_x == 1: - wm = wm_qs.first() - elif count_x == 0: - raise ParseError( - f'{str(xmaterial)}-{xbatch}-批次库存不存在!') + if xwm: + wm = xwm else: - raise ParseError( - f'{str(xmaterial)}-{xbatch}-存在多个相同批次!') + wm_qs = WMaterial.objects.filter( + batch=xbatch, + material=xmaterial, + belong_dept=belong_dept, + mgroup=mgroup, + defect=defect, + state=WMaterial.WM_OK) + count_x = wm_qs.count() + if count_x == 1: + wm = wm_qs.first() + elif count_x == 0: + raise ParseError( + f'{str(xmaterial)}-{xbatch}-批次库存不存在!') + else: + raise ParseError( + f'{str(xmaterial)}-{xbatch}-存在多个相同批次!') # 扣减车间库存 new_count = wm.count - xcount From 168d917232648ea604489d8ce3600754a390d180 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 17 Nov 2025 14:34:13 +0800 Subject: [PATCH 16/16] =?UTF-8?q?feat:=20mioitem=E6=B7=BB=E5=8A=A0count=5F?= =?UTF-8?q?send=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/inm/migrations/0038_mioitem_count_send.py | 18 ++++++++++++++++++ apps/inm/models.py | 1 + 2 files changed, 19 insertions(+) create mode 100644 apps/inm/migrations/0038_mioitem_count_send.py diff --git a/apps/inm/migrations/0038_mioitem_count_send.py b/apps/inm/migrations/0038_mioitem_count_send.py new file mode 100644 index 00000000..84ad27e4 --- /dev/null +++ b/apps/inm/migrations/0038_mioitem_count_send.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2025-11-17 06:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('inm', '0037_alter_materialbatch_material'), + ] + + operations = [ + migrations.AddField( + model_name='mioitem', + name='count_send', + field=models.DecimalField(blank=True, decimal_places=3, max_digits=12, null=True, verbose_name='发出数量'), + ), + ] diff --git a/apps/inm/models.py b/apps/inm/models.py index 83580b49..e0d2cd3d 100644 --- a/apps/inm/models.py +++ b/apps/inm/models.py @@ -161,6 +161,7 @@ class MIOItem(BaseModel): batch = models.TextField('批次号', db_index=True) unit_price = models.DecimalField('单价', max_digits=14, decimal_places=2, null=True, blank=True) count = models.DecimalField('出入数量', max_digits=12, decimal_places=3) + count_send = models.DecimalField('发出数量', max_digits=12, decimal_places=3, null=True, blank=True) count_tested = models.PositiveIntegerField('已检数', null=True, blank=True) test_date = models.DateField('检验日期', null=True, blank=True) test_user = models.ForeignKey(