diff --git a/apps/inm/serializers.py b/apps/inm/serializers.py index 3521d692..6770119c 100644 --- a/apps/inm/serializers.py +++ b/apps/inm/serializers.py @@ -153,7 +153,9 @@ class MIOItemCreateSerializer(CustomModelSerializer): validated_data["batch"] = wm.batch material: Material = validated_data['material'] - batch = validated_data['batch'] + batch = validated_data.get("batch", None) + if not batch: + batch = "无" if material.is_hidden: raise ParseError('隐式物料不可出入库') if mio.type in [MIO.MIO_TYPE_RETURN_IN, MIO.MIO_TYPE_BORROW_OUT]: diff --git a/apps/inm/services.py b/apps/inm/services.py index 145f9568..67d47bc4 100644 --- a/apps/inm/services.py +++ b/apps/inm/services.py @@ -398,7 +398,7 @@ class InmService: if change_count < 0: raise ParseError("存在负数!") state = WMaterial.WM_OK - if defect: + if defect and defect.okcate in [Defect.DEFECT_NOTOK]: state = WMaterial.WM_NOTOK mb, _ = MaterialBatch.objects.get_or_create( material=material, diff --git a/apps/inm/services_daoru.py b/apps/inm/services_daoru.py index f4ea563a..be19207e 100644 --- a/apps/inm/services_daoru.py +++ b/apps/inm/services_daoru.py @@ -138,13 +138,17 @@ def daoru_mioitems(path:str, mio:MIO): mioitems = [] ind = 2 - while sheet[f"b{ind}"].value: + while sheet[f"a{ind}"].value: batch = sheet[f"b{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 batch: + pass + else: + batch = "无" count = sheet[f"c{ind}"].value warehouse_name = sheet[f"d{ind}"].value try: diff --git a/apps/inm/views.py b/apps/inm/views.py index 0239e605..3b094a3f 100644 --- a/apps/inm/views.py +++ b/apps/inm/views.py @@ -206,12 +206,13 @@ class MIOViewSet(CustomModelViewSet): if ins.state != MIO.MIO_CREATE: raise ParseError('记录状态异常') with transaction.atomic(): - ins.submit_time = timezone.now() - ins.state = MIO.MIO_SUBMITED - ins.submit_user = request.user - ins.update_by = request.user - ins.save() - InmService.update_inm(ins) + now = timezone.now() + updated_count = MIO.objects.filter(id=ins.id, submit_time__isnull=True).update( + submit_time=now, update_time=now, state=MIO.MIO_SUBMITED, submit_user=request.user, update_by=request.user) + if updated_count == 1: + InmService.update_inm(ins) + else: + raise ParseError('记录正在处理中,请稍后再试') InmService.update_material_count(ins) return Response(MIOListSerializer(instance=ins).data) @@ -228,11 +229,12 @@ class MIOViewSet(CustomModelViewSet): if ins.submit_user != user: raise ParseError('非提交人不可撤回') with transaction.atomic(): - ins.submit_time = None - ins.state = MIO.MIO_CREATE - ins.update_by = user - ins.save() - InmService.update_inm(ins, is_reverse=True) + updated_count = MIO.objects.filter(id=ins.id, submit_time__isnull=False).update( + submit_time=None, update_time=timezone.now(), state=MIO.MIO_CREATE, submit_user=None, update_by=request.user) + if updated_count == 1: + InmService.update_inm(ins, is_reverse=True) + else: + raise ParseError('记录正在处理中,请稍后再试') InmService.update_material_count(ins) return Response() 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/services.py b/apps/mtm/services.py index 8f4155f1..626b95b4 100644 --- a/apps/mtm/services.py +++ b/apps/mtm/services.py @@ -155,12 +155,12 @@ def bind_routepack(ticket: Ticket, transition, new_ticket_data: dict): raise ParseError('缺少步骤') r_qs = Route.objects.filter(routepack=routepack).order_by('sort', 'process__sort', 'create_time') first_route = r_qs.first() - last_route = r_qs.last() if first_route.batch_bind: first_route.batch_bind = False first_route.save(update_fields=['batch_bind']) - if last_route.material_out != routepack.material: - raise ParseError('最后一步产出与工艺包不一致') + # last_route = r_qs.last() + # if last_route.material_out != routepack.material: + # raise ParseError('最后一步产出与工艺包不一致') ticket_data = ticket.ticket_data ticket_data.update({ 't_model': 'routepack', diff --git a/apps/mtm/views.py b/apps/mtm/views.py index 1ad675d3..d08c79a7 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 Route.objects.filter(from_route__id=ins.id).exists() and (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): diff --git a/apps/qm/migrations/0053_alter_ftest_is_ok.py b/apps/qm/migrations/0053_alter_ftest_is_ok.py new file mode 100644 index 00000000..92b5f83c --- /dev/null +++ b/apps/qm/migrations/0053_alter_ftest_is_ok.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2025-09-05 01:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('qm', '0052_auto_20250718_1558'), + ] + + operations = [ + migrations.AlterField( + model_name='ftest', + name='is_ok', + field=models.BooleanField(default=True, verbose_name='是否合格'), + ), + ] diff --git a/apps/qm/models.py b/apps/qm/models.py index 5ec00882..b724a228 100644 --- a/apps/qm/models.py +++ b/apps/qm/models.py @@ -337,7 +337,7 @@ class Ftest(CommonBDModel): User, verbose_name='操作人', on_delete=models.CASCADE, related_name='ftest_test_user') check_user = models.ForeignKey( User, verbose_name='专检人', on_delete=models.CASCADE, related_name='ftest_check_user', null=True, blank=True) - is_ok = models.BooleanField('是否合格', default=False) + is_ok = models.BooleanField('是否合格', default=True) note = models.TextField('备注', default='', blank=True) ftest_work = models.ForeignKey( FtestWork, verbose_name='关联检验工作', on_delete=models.CASCADE, null=True, blank=True) diff --git a/apps/qm/views.py b/apps/qm/views.py index 410bfd27..ae1e2b57 100644 --- a/apps/qm/views.py +++ b/apps/qm/views.py @@ -322,7 +322,7 @@ class FtestWorkViewSet(CustomModelViewSet): ins:FtestWork = self.get_object() if ins.ticket: raise ParseError('该检验工作存在审批!') - if ins.wm is None or ins.mb is None: + if ins.wm is None and ins.mb is None: raise ParseError('该检验工作未关联库存') if ins.submit_time is None: ftestwork_submit(ins, request.user) diff --git a/apps/wpm/migrations/0123_mlogbdefect_count_has.py b/apps/wpm/migrations/0123_mlogbdefect_count_has.py new file mode 100644 index 00000000..4ef30708 --- /dev/null +++ b/apps/wpm/migrations/0123_mlogbdefect_count_has.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2025-09-04 08:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wpm', '0122_auto_20250827_1522'), + ] + + operations = [ + migrations.AddField( + model_name='mlogbdefect', + name='count_has', + field=models.DecimalField(decimal_places=1, default=0, max_digits=11, verbose_name='含有该缺陷的数量'), + ), + ] diff --git a/apps/wpm/models.py b/apps/wpm/models.py index a1766aec..7baf2930 100644 --- a/apps/wpm/models.py +++ b/apps/wpm/models.py @@ -477,6 +477,7 @@ class MlogbDefect(BaseModel): mlogb = models.ForeignKey(Mlogb, verbose_name='生产记录', on_delete=models.CASCADE) defect = models.ForeignKey("qm.Defect", verbose_name='缺陷', on_delete=models.CASCADE, null=True, blank=True) count = models.DecimalField('数量', default=0, max_digits=11, decimal_places=1) + count_has = models.DecimalField('含有该缺陷的数量', default=0, max_digits=11, decimal_places=1) @classmethod def get_defect_qs(cls, ftype="all"): @@ -512,7 +513,7 @@ class Mlogbw(BaseModel): @classmethod def cal_count_notok(cls, mlogb: Mlogb): - from apps.qm.models import Defect + from apps.qm.models import Defect, FtestDefect # 锁定mlogb以防止并发修改 # mlogb:Mlogb = Mlogb.objects.select_for_update().get(pk=mlogb.pk) count = Mlogbw.objects.filter(mlogb=mlogb).count() @@ -523,9 +524,13 @@ class Mlogbw(BaseModel): mlogb.count_real = count count_notok = 0 count_notok_full = 0 - tqs = Mlogbw.objects.filter(mlogb=mlogb, ftest__is_ok=False) - tqs_a = Mlogbw.objects.filter(mlogb=mlogb, ftest__is_ok=False).values("ftest__defect_main").annotate(xcount=Count('id')) - defects = {defect.id: defect for defect in Defect.objects.filter(id__in=tqs.values_list("ftest__defect_main", flat=True))} + # 个追踪的合格b类不分批 + tqs = Mlogbw.objects.filter(mlogb=mlogb, ftest__defect_main__isnull=False) + tqs_a = Mlogbw.objects.filter(mlogb=mlogb, ftest__defect_main__isnull=False).values("ftest__defect_main").annotate(xcount=Count('id')) + defect_ids = tqs.values_list("ftest__defect_main", flat=True) + tqs_has_a = FtestDefect.objects.filter(ftest__mlogbw_ftest__mlogb=mlogb, has=True).values("defect").annotate(xcount=Count('id')) + defect_ids = defect_ids.union(tqs_has_a.values_list("defect", flat=True)) + defects = {defect.id: defect for defect in Defect.objects.filter(id__in=defect_ids)} md_ids = [] for t in tqs_a: md, _ = MlogbDefect.objects.get_or_create(mlogb=mlogb, defect=defects[t["ftest__defect_main"]]) @@ -535,6 +540,13 @@ class Mlogbw(BaseModel): count_notok += t["xcount"] if defects[t["ftest__defect_main"]].okcate != 10: count_notok_full += t["xcount"] + + for t2 in tqs_has_a: + md, _ = MlogbDefect.objects.get_or_create(mlogb=mlogb, defect=defects[t2["defect"]], defaults={"count": 0}) + md.count_has = t2["xcount"] + md.save() + md_ids.append(md.id) + MlogbDefect.objects.filter(mlogb=mlogb).exclude(id__in=md_ids).delete() mlogb.count_notok = count_notok mlogb.count_ok = count - mlogb.count_notok diff --git a/apps/wpm/scripts/batch_bxerp.py b/apps/wpm/scripts/batch_bxerp.py index 80d14191..d16dc40a 100644 --- a/apps/wpm/scripts/batch_bxerp.py +++ b/apps/wpm/scripts/batch_bxerp.py @@ -1,6 +1,6 @@ from apps.wpm.models import BatchSt import logging -from apps.wpm.models import Mlogb, MlogbDefect +from apps.wpm.models import Mlogb, Mlogbw, MlogbDefect from apps.mtm.models import Mgroup import decimal from django.db.models import Sum @@ -41,10 +41,14 @@ def main(batch: str, mgroup_obj:Mgroup=None): for item in mlogb1_qs: # 找到对应的输入 mlogb_from:Mlogb = item.mlogb_from + mlogbw_from:Mlogbw = item.mlogbw_from if mlogb_from: mlogb_q_ids.append(mlogb_from.id) data[f"{mgroup_name}_count_use"] += mlogb_from.count_use data[f"{mgroup_name}_count_pn_jgqbl"] += mlogb_from.count_pn_jgqbl + if mlogbw_from: + data[f"{mgroup_name}_count_use"] += 1 + data[f"{mgroup_name}_count_pn_jgqbl"] += 0 if item.mlog.handle_user: data[f"{mgroup_name}_操作人"].append(item.mlog.handle_user) if item.mlog.handle_date: diff --git a/apps/wpm/scripts/batch_gxerp.py b/apps/wpm/scripts/batch_gxerp.py index 11f8d633..8758d880 100644 --- a/apps/wpm/scripts/batch_gxerp.py +++ b/apps/wpm/scripts/batch_gxerp.py @@ -29,6 +29,7 @@ def main(batch: str, mgroup_obj): if mlogb1_qs.exists(): data[f"{mgroup_name}_日期"] = [] data[f"{mgroup_name}_操作人"] = [] + data[f"{mgroup_name}_班次"] = [] data[f"{mgroup_name}_count_use"] = 0 data[f"{mgroup_name}_count_real"] = 0 data[f"{mgroup_name}_count_ok"] = 0 @@ -47,6 +48,8 @@ def main(batch: str, mgroup_obj): data[f"{mgroup_name}_操作人"].append(item.mlog.handle_user) if item.mlog.handle_date: data[f"{mgroup_name}_日期"].append(item.mlog.handle_date) + if item.mlog.shift: + data[f"{mgroup_name}_班次"].append(item.mlog.shift.name) data[f"{mgroup_name}_count_real"] += item.count_real data[f"{mgroup_name}_count_ok"] += item.count_ok data[f"{mgroup_name}_count_ok_full"] += item.count_ok_full if item.count_ok_full else 0 @@ -80,6 +83,8 @@ def main(batch: str, mgroup_obj): data[f"{mgroup_name}_日期"] = ";".join([item.strftime("%Y-%m-%d") for item in data[f"{mgroup_name}_日期"]]) data[f"{mgroup_name}_操作人"] = list(set(data[f"{mgroup_name}_操作人"])) data[f"{mgroup_name}_操作人"] = ";".join([item.name for item in data[f"{mgroup_name}_操作人"]]) + data[f"{mgroup_name}_班次"] = list(set(data[f"{mgroup_name}_班次"])) + data[f"{mgroup_name}_班次"] = ";".join([item for item in data[f"{mgroup_name}_班次"]]) mlogb2_qs = Mlogb.objects.filter(mlog__submit_time__isnull=False, diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index bfe1df68..f1c2f477 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -216,12 +216,17 @@ class MlogbDefectSerializer(CustomModelSerializer): defect_okcate = serializers.CharField(source="defect.okcate", read_only=True) class Meta: model = MlogbDefect - fields = ["id", "defect_name", "count", "mlogb", "defect", "defect_okcate"] + fields = ["id", "defect_name", "count", "mlogb", "defect", "defect_okcate", "count_has"] read_only_fields = EXCLUDE_FIELDS_BASE + ["mlogb"] + extra_kwargs = { + 'count_has': {'required': False}, + } def validate(self, attrs): if attrs["count"] < 0: raise serializers.ValidationError("存在负数!") + if "count_has" not in attrs or attrs["count_has"] < attrs["count"]: + attrs["count_has"] = attrs["count"] return attrs class MlogbSerializer(CustomModelSerializer): @@ -635,10 +640,12 @@ class MlogSerializer(CustomModelSerializer): mgroup:Mgroup = attrs['mgroup'] work_start_time:datetime = attrs['work_start_time'] handle_date, attrs["shift"] = mgroup.get_shift(work_start_time) - if mtask.start_date == mtask.end_date: + if mtask and mtask.start_date == mtask.end_date: attrs['handle_date'] = mtask.end_date if attrs['handle_date'] != handle_date: raise ParseError('任务日期与生产日期不一致') + else: + attrs['handle_date'] = handle_date handle_user = attrs.get('handle_user', None) if handle_user is None and hasattr(self, "request"): @@ -684,8 +691,6 @@ class MlogInitSerializer(CustomModelSerializer): supplier = attrs.get('supplier', None) if not supplier: raise ParseError('外协必须选择外协单位') - if attrs.get('work_start_time', None) and 'handle_date' not in attrs: - attrs['handle_date'] = localdate(attrs['work_end_time']) # 如果已经确定产出,则自动获取qct if attrs.get("material_out", None): attrs["qct"] = Qct.get(attrs["material_out"], "process", "out") @@ -696,12 +701,14 @@ class MlogInitSerializer(CustomModelSerializer): class MlogChangeSerializer(CustomModelSerializer): class Meta: model = Mlog - fields = ['id', 'work_end_time', 'handle_user', 'note', 'oinfo_json', 'test_file', 'test_user', 'test_time', 'equipment', "team"] + fields = ['id', 'work_start_time', 'work_end_time', 'handle_user', 'note', 'oinfo_json', 'test_file', 'test_user', 'test_time', 'equipment', "team"] - # def validate(self, attrs): - # if attrs.get('work_end_time', None): - # attrs['handle_date'] = localdate(attrs['work_end_time']) - # return attrs + def update(self, instance, validated_data): + work_start_time = validated_data.get('work_start_time', None) + if work_start_time: + mgroup:Mgroup = instance.mgroup + validated_data["handle_date"], validated_data["shift"] = mgroup.get_shift(work_start_time) + return super().update(instance, validated_data) class CountJsonSerializer(serializers.Serializer): @@ -824,10 +831,9 @@ class MlogbInUpdateSerializer(CustomModelSerializer): MlogbDefect.objects.bulk_create(mlogb_defect_objects) ins.cal_count_pn_jgqbl(cal_mlog=False) # 只有普通工序的才可联动 - material_out:Material = ins.mlog.material_out - route:Route = mlog.route - if material_out.tracking == Material.MA_TRACKING_BATCH: - if route and route.process and route.process.mtype == Process.PRO_NORMAL: + route:Route = ins.route if ins.route else ins.mlog.route + if route and route.process and route.process.mtype == Process.PRO_NORMAL: + if route.material_out.tracking == Material.MA_TRACKING_BATCH: mlogbout_qs = Mlogb.objects.filter(mlog=ins.mlog, mlogb_from=ins) if mlogbout_qs.count() == 1: mlogbout = mlogbout_qs.first() @@ -910,7 +916,7 @@ class MlogbwCreateUpdateSerializer(CustomModelSerializer): class MlogbwStartTestSerializer(serializers.Serializer): mlogbw_ids = serializers.ListField(child=serializers.CharField(), label="mlogbwId列表") - test_equip = serializers.CharField(label="测试设备", allow_null=True, required=False) + test_equip = serializers.CharField(label="测试设备", allow_null=True, required=False, allow_blank=True) test_user = serializers.CharField(label="测试人员") defects = serializers.ListField(child=serializers.CharField(), required=False, allow_null=True, label="缺陷项列表") testitems = serializers.ListField(child=serializers.CharField(), required=False, allow_null=True, label="测试项列表") @@ -950,7 +956,7 @@ class MlogbwStartTestSerializer(serializers.Serializer): if mlogbw.ftest: existing_ftests[mlogbw.ftest_id] = mlogbw.ftest else: - ftest = Ftest(mlogbw=mlogbw, test_date=test_date, qct=qct) + ftest = Ftest(test_date=test_date, qct=qct, test_user=test_user, type="process", id=idWorker.get_id(), is_ok=True) new_ftests.append(ftest) mlogbws_to_update.append(mlogbw) @@ -988,7 +994,7 @@ class MlogbwStartTestSerializer(serializers.Serializer): else: # 新记录,需要创建 defects_to_create.append( - FtestDefect(ftest=ftest, defect=defect, test_user=test_user) + FtestDefect(ftest=ftest, defect=defect, test_user=test_user, id=idWorker.get_id()) ) # 批量创建新记录 @@ -1027,7 +1033,8 @@ class MlogbwStartTestSerializer(serializers.Serializer): ftest=ftest, testitem=testitem, test_user=test_user, - test_equip=test_equip + test_equip=test_equip, + id=idWorker.get_id() ) ) diff --git a/apps/wpm/services.py b/apps/wpm/services.py index 45b0808b..7dae7d1e 100644 --- a/apps/wpm/services.py +++ b/apps/wpm/services.py @@ -152,6 +152,8 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): """ 生产日志提交后需要执行的操作 """ + if mlog.work_start_time > timezone.now(): + raise ParseError('操作开始时间不能晚于当前时间') if mlog.work_start_time and mlog.work_end_time and mlog.work_end_time < mlog.work_start_time: raise ParseError('操作结束时间不能早于操作开始时间') if mlog.count_real == 0: @@ -203,7 +205,7 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): for item in m_ins: mbd_qs = MlogbDefect.get_defect_qs_from_mlogb(item) for itemx in mbd_qs: - if itemx.defect: + if itemx.defect and itemx.count > 0: m_ins_bl_list.append((item.material_in, item.batch, itemx.count, itemx.defect, item)) else: m_ins_list = [(material_in, mlog.batch, mlog.count_use, None, mlog)] @@ -282,7 +284,8 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): # if item.material_out.tracking == Material.MA_TRACKING_SINGLE: # Mlogbw.cal_count_notok(item) for itemx in mbd_qs: - m_outs_list.append((item.material_out, item.batch, itemx.count, 0, itemx.defect, item)) + if itemx.count > 0: + m_outs_list.append((item.material_out, item.batch, itemx.count, 0, itemx.defect, item)) # # 获取所有主要的不合格项/先暂时保留 # bw_qs = Mlogbw.objects.filter(mlogb=item) # defectIds= Ftest.objects.filter(mlogbw_ftest__in=bw_qs).exclude(defect_main=None).values_list("defect_main__id", flat=True).distinct() @@ -435,7 +438,8 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): # if item.material_out.tracking == Material.MA_TRACKING_SINGLE: # Mlogbw.cal_count_notok(item) for itemx in mbd_qs: - m_outs_list.append((item.material_out, item.batch, itemx.count, 0, itemx.defect, item)) + if itemx.count > 0: + m_outs_list.append((item.material_out, item.batch, itemx.count, 0, itemx.defect, item)) # if item.material_out.tracking == Material.MA_TRACKING_SINGLE: # # 获取所有主要的不合格项 # bw_qs = Mlogbw.objects.filter(mlogb=item) @@ -530,7 +534,7 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): for item in m_ins: mbd_qs = MlogbDefect.get_defect_qs_from_mlogb(item) for itemx in mbd_qs: - if itemx.defect: + if itemx.defect and itemx.count > 0: m_ins_bl_list.append((item.material_in, item.batch, itemx.count, itemx.defect, item)) else: m_ins_list = [(material_in, mlog.batch, mlog.count_use, mlog.wm_in, mlog)]