diff --git a/apps/inm/services.py b/apps/inm/services.py index edcdb208..f7617c04 100644 --- a/apps/inm/services.py +++ b/apps/inm/services.py @@ -169,7 +169,7 @@ def do_in(item: MIOItem): else: raise ParseError(f'{str(xmaterial)}-{xbatch}车间物料不足') - wm_production_dept = wm.mgroup.belong_dept if wm.mgroup else None + wm_production_dept = wm.mgroup.belong_dept if wm.mgroup else wm.belong_dept if production_dept is None: production_dept = wm_production_dept elif wm_production_dept and production_dept != wm_production_dept: @@ -247,10 +247,8 @@ class InmService: BatchLog.clear(mio=instance) else: for item in MIOItem.objects.filter(mio=instance): - if item.mb: # 说明录入到已有批次 - BatchSt.g_create(batch=item.batch) - else: - BatchSt.g_create(batch=item.batch, mio=instance, material_start=item.material) + BatchSt.g_create( + batch=item.batch, mio=instance, material_start=item.material, reuse_node=True) from apps.pum.services import PumService if is_reverse: cls.update_mb(instance, -1) @@ -262,10 +260,8 @@ class InmService: BatchLog.clear(mio=instance) else: for item in MIOItem.objects.filter(mio=instance): - if item.mb: - BatchSt.g_create(batch=item.batch) - else: - BatchSt.g_create(batch=item.batch, mio=instance, material_start=item.material) + BatchSt.g_create( + batch=item.batch, mio=instance, material_start=item.material, reuse_node=True) if is_reverse: cls.update_mb(instance, -1) else: diff --git a/apps/wpm/filters.py b/apps/wpm/filters.py index bd0d04bf..a4a17fc4 100644 --- a/apps/wpm/filters.py +++ b/apps/wpm/filters.py @@ -53,7 +53,7 @@ class WMaterialFilter(filters.FilterSet): qs = queryset.exclude(material__id__in=matoutIds)|queryset.filter(state=WMaterial.WM_REPAIR) return qs elif value == "done": - qs = queryset.filter(material__id__in=matoutIds)|queryset.filter(state=WMaterial.WM_REPAIRED) + qs = queryset.filter(material__id__in=matoutIds).exclude(state=WMaterial.WM_REPAIR)|queryset.filter(state=WMaterial.WM_REPAIRED) return qs else: raise ParseError("请提供工段查询条件") diff --git a/apps/wpm/models.py b/apps/wpm/models.py index c63d7c4f..cf57b70a 100644 --- a/apps/wpm/models.py +++ b/apps/wpm/models.py @@ -616,18 +616,32 @@ class BatchSt(BaseModel): unique_together = [("batch", "version")] @classmethod - def g_create(cls, batch:str, mio=None, handover=None, mlog=None, material_start=None): + def g_create(cls, batch:str, mio=None, handover=None, mlog=None, material_start=None, reuse_node=False): """ 创建新的批次 """ if mio is None and handover is None and mlog is None: - return cls.objects.get_or_create(batch=batch) + try: + node = cls.objects.get(batch=batch) + except cls.DoesNotExist: + return cls.objects.create(batch=batch), True + except cls.MultipleObjectsReturned: + # 兼容性处理 + node = cls.objects.filter(batch=batch).order_by('-version').first() + return node, False else: version = 1 # 带有来源的批次获取,需检查批次号是否可用 - if cls.objects.filter(batch=batch, version=1).exists(): - latest_version = BatchSt.objects.filter(batch=batch).aggregate(Max("version"))["version__max"] - version = latest_version + 1 + if cls.objects.filter(batch=batch).exists(): + if reuse_node: + node = cls.objects.filter(batch=batch).order_by('-version').first() + if node.material_start is not None and node.material_start != material_start: + raise ParseError(f"{batch}-该批次号因物料不同不可引用") + return node, False + else: + raise ParseError(f"{batch}-该批次号不可使用") + # latest_version = BatchSt.objects.filter(batch=batch).aggregate(Max("version"))["version__max"] + # version = latest_version + 1 if mio is None and handover is None and mlog is None: raise ParseError("mio or handover or mlog must be provided") ins = cls.objects.create(batch=batch, mio=mio, handover=handover, mlog=mlog, material_start=material_start, version=version) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index bfa31f78..8edd22c3 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -359,6 +359,9 @@ class MlogSerializer(CustomModelSerializer): with transaction.atomic(): mlogb = validated_data.pop('mlogb', []) instance: Mlog = super().create(validated_data) + ## 返工没有加工前不良 + if instance.is_fix and instance.count_pn_jgqbl >0: + raise ParseError("返工不支持加工前不良") # 自动生成mlogb batch_in = instance.batch wm_in = instance.wm_in @@ -457,6 +460,9 @@ class MlogSerializer(CustomModelSerializer): with transaction.atomic(): mlogb = validated_data.pop('mlogb', []) instance: Mlog = super().update(instance, validated_data) + ## 返工没有加工前不良 + if instance.is_fix and instance.count_pn_jgqbl >0: + raise ParseError("返工不支持加工前不良") wm_in = instance.wm_in batch_in = instance.batch if wm_in: @@ -541,33 +547,37 @@ class MlogSerializer(CustomModelSerializer): attrs['mtype'] = Mlog.MTYPE_SELF # 默认为自生产 fmlog:Fmlog = attrs.get('fmlog', None) mtaskb = attrs.get('mtaskb', None) + wm_in: WMaterial = attrs.get('wm_in', None) if fmlog: attrs['fill_way'] = Mlog.MLOG_12 - wm_in: WMaterial = attrs.get('wm_in', None) - if wm_in: - pass - else: + if not wm_in: raise ParseError('未提供消耗的车间物料') attrs['mgroup'] = fmlog.mgroup + attrs['is_fix'] = fmlog.is_fix attrs['mtask'] = fmlog.mtask attrs['route'] = fmlog.route attrs['mtype'] = fmlog.mgroup.mtype if attrs['mtask'] and attrs['mtask'].route: attrs['route'] = attrs['mtask'].route - # if attrs['mtask'].mtaskb and mtaskb is None: - # raise ParseError('子任务不能为空') if mtaskb and mtaskb.mtask != fmlog.mtask: raise ParseError('子任务不一致') - if wm_in.state in [WMaterial.WM_OK]: - pass - else: + if wm_in: + attrs["material_in"] = wm_in.material + attrs["batch"] = wm_in.batch + if attrs["is_fix"]: + attrs["material_out"] = attrs["material_in"] + if wm_in.state in [WMaterial.WM_REPAIR, WMaterial.WM_NOTOK]: + pass + else: + raise ParseError('返修或复检需使用返修品/不合格品') + elif wm_in.state != WMaterial.WM_OK: raise ParseError('非合格品不可使用') - if wm_in.material != attrs['route'].material_in: + if attrs['route'] and wm_in.material != attrs['route'].material_in: raise ParseError('消耗物料与工艺步骤不一致') - if attrs['mtype'] == Mlog.MTYPE_OUT: - supplier = attrs.get('supplier', None) - if not supplier: - raise ParseError('外协必须选择外协单位') + if attrs['mtype'] == Mlog.MTYPE_OUT: + supplier = attrs.get('supplier', None) + if not supplier: + raise ParseError('外协必须选择外协单位') mtask = attrs.get('mtask', None) count_notok = 0 for i in attrs: @@ -588,8 +598,6 @@ class MlogSerializer(CustomModelSerializer): else: if attrs.get('work_end_time', None): attrs['handle_date'] = localdate(attrs['work_end_time']) - elif attrs.get('work_start_time', None): - attrs['handle_date'] = localdate(attrs['work_start_time']) mtaskb: Mtaskb = attrs.get('mtaskb', None) if mtaskb: mtask = mtaskb.mtask @@ -599,6 +607,8 @@ class MlogSerializer(CustomModelSerializer): attrs['mgroup'] = mtask.mgroup attrs['material_in'] = mtask.material_in material_out = mtask.material_out + if wm_in and wm_in.material != mtask.material_in: + raise ParseError('消耗物料与任务不一致') attrs['material_out'] = material_out if mtask.start_date == mtask.end_date: attrs['handle_date'] = mtask.end_date @@ -628,21 +638,20 @@ class MlogInitSerializer(CustomModelSerializer): } def validate(self, attrs): - route: Route = attrs.get('route', None) mgroup: Mgroup = attrs['mgroup'] is_fix:bool = attrs.get('is_fix', False) attrs['mtype'] = mgroup.mtype if is_fix: attrs["route"] = None - elif route is None: - raise ParseError('缺少工艺路线') - if route and route.process != mgroup.process: - raise ParseError('工序不匹配') - if is_fix: attrs['hour_work'] = None attrs['material_in'] = None attrs['material_out'] = None - if route: + else: + route: Route = attrs.get('route', None) + if not route: + raise ParseError('缺少工艺路线') + if route and route.process != mgroup.process: + raise ParseError('工序不匹配') attrs['hour_work'] = route.hour_work attrs['material_in'] = route.material_in attrs['material_out'] = route.material_out @@ -703,6 +712,8 @@ class MlogbInSerializer(CustomModelSerializer): raise ParseError("请选择相应车间库存!") if is_fix: # 返修或复检 if wm_in.state in [WMaterial.WM_REPAIR, WMaterial.WM_NOTOK]: + pass + else: raise ParseError('返修或复检需使用返修品/不合格品') elif wm_in.state != WMaterial.WM_OK: raise ParseError('非合格品不可使用') @@ -1222,6 +1233,7 @@ class FmlogSerializer(CustomModelSerializer): is_fix = attrs.get("is_fix", False) if is_fix: attrs["mtask"] = None + attrs['route'] = None elif not attrs.get("mtask", None) and not attrs.get("route", None): raise ParseError("请选择任务或工艺步骤") mtask: Mtask = attrs['mtask'] diff --git a/apps/wpm/services.py b/apps/wpm/services.py index c1061103..825fb63d 100644 --- a/apps/wpm/services.py +++ b/apps/wpm/services.py @@ -961,9 +961,9 @@ def mlog_audit_end(ticket: Ticket): mlog_submit(ins, ticket.create_by, now) -def get_batch_dag(batch_number: str): +def get_batch_dag(batch_number: str, version=1): try: - batch_ins = BatchSt.objects.get(batch=batch_number) + batch_ins = BatchSt.objects.get(batch=batch_number, version=version) except Exception: raise ParseError("该批次号未构建关系链") # 收集所有相关批次和边 diff --git a/apps/wpm/views.py b/apps/wpm/views.py index e3525e24..653bab38 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -583,7 +583,7 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust for wpr in Wpr.objects.filter(wm=wm_in).order_by("number"): Mlogbw.objects.get_or_create(wpr=wpr, mlogb=mlogbin, defaults={"number": wpr.number}) - if qct is None and not (is_fix and mtype == Process.PRO_PROD): + if qct is None: mlog.qct = Qct.get(material_out, "process") mlog.save(update_fields = ["qct"]) @@ -816,6 +816,7 @@ class BatchLogViewSet(ListModelMixin, CustomGenericViewSet): 获取该批次的DAG图数据 """ batch = request.data.get("batch", None) + version = request.data.get("version", 1) if not batch: raise ParseError("缺少batch参数") - return Response(get_batch_dag(batch)) \ No newline at end of file + return Response(get_batch_dag(batch, version)) \ No newline at end of file