From 8c4b11d9c9c17504945e272ae68d4493a09215c7 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 27 Feb 2025 16:59:20 +0800 Subject: [PATCH 01/32] =?UTF-8?q?feat:=20mb=E6=B7=BB=E5=8A=A0=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E6=9D=A1=E4=BB=B6state?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/inm/filters.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/inm/filters.py b/apps/inm/filters.py index 9a83211b..31c2f88f 100644 --- a/apps/inm/filters.py +++ b/apps/inm/filters.py @@ -11,7 +11,9 @@ class MaterialBatchFilter(filters.FilterSet): "material": ["exact"], "material__type": ["exact", "in"], "material__process": ["exact", "in"], - "count": ["exact", "gte", "lte"] + "count": ["exact", "gte", "lte"], + "state": ["exact", "in"], + "defect": ["exact"] } From b2f801deda86b8c19bf804508fc0ec78d5c13b4a Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 28 Feb 2025 09:14:55 +0800 Subject: [PATCH 02/32] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0mlogbdefect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/migrations/0089_mlogbdefect.py | 31 +++++++ apps/wpm/models.py | 10 +++ apps/wpm/serializers.py | 114 ++++++++++++++++++------ apps/wpm/services.py | 2 + 4 files changed, 129 insertions(+), 28 deletions(-) create mode 100644 apps/wpm/migrations/0089_mlogbdefect.py diff --git a/apps/wpm/migrations/0089_mlogbdefect.py b/apps/wpm/migrations/0089_mlogbdefect.py new file mode 100644 index 00000000..feaaa309 --- /dev/null +++ b/apps/wpm/migrations/0089_mlogbdefect.py @@ -0,0 +1,31 @@ +# Generated by Django 3.2.12 on 2025-02-28 01:11 + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('qm', '0044_testitem_cd_expr'), + ('wpm', '0088_auto_20250224_0938'), + ] + + operations = [ + migrations.CreateModel( + name='MlogbDefect', + fields=[ + ('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')), + ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')), + ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')), + ('count', models.PositiveIntegerField(default=0, verbose_name='数量')), + ('defect', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='qm.defect', verbose_name='缺陷')), + ('mlogb', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.mlogb', verbose_name='生产记录')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/wpm/models.py b/apps/wpm/models.py index 3b1a9745..8cb6e55a 100644 --- a/apps/wpm/models.py +++ b/apps/wpm/models.py @@ -357,6 +357,16 @@ class Mlogb(BaseModel): return "in", self.material_in.tracking elif self.material_out: return "out", self.material_out.tracking + + @property + def mlogbdefect(self): + return MlogbDefect.objects.filter(mlogb=self) + + +class MlogbDefect(BaseModel): + mlogb = models.ForeignKey(Mlogb, verbose_name='生产记录', on_delete=models.CASCADE) + defect = models.ForeignKey("qm.Defect", verbose_name='缺陷', on_delete=models.CASCADE) + count = models.PositiveIntegerField('数量', default=0) class Mlogbw(BaseModel): """TN: 单个产品生产/检验日志 diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index b2c725db..b20f1285 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -6,7 +6,7 @@ from datetime import datetime from .models import (SfLog, StLog, SfLogExp, WMaterial, Mlog, Handover, Handoverb, Mlogb, AttLog, - OtherLog, Fmlog, BatchSt, Mlogbw, Handoverbw) + OtherLog, Fmlog, BatchSt, Mlogbw, Handoverbw, MlogbDefect) from apps.system.models import Dept, User from apps.system.serializers import UserSimpleSerializer from apps.pm.models import Mtask, Mtaskb @@ -23,6 +23,7 @@ from apps.wf.serializers import TicketSimpleSerializer from apps.wpmw.models import Wpr from apps.qm.serializers import FtestProcessSerializer import logging +from apps.qm.models import Defect mylogger = logging.getLogger("log") class OtherLogSerializer(CustomModelSerializer): @@ -687,42 +688,99 @@ class MlogbwCreateUpdateSerializer(CustomModelSerializer): mlogbw = self.save_ftest(mlogbw, ftest_data) return mlogbw +class MlogbDefectSerializer(CustomModelSerializer): + defect_name = serializers.CharField(source="defect.name", read_only=True) + class Meta: + model = Mlogb + fields = ["id", "defect_name", "count", "mlogb", "defect"] + class MlogbOutUpdateSerializer(CustomModelSerializer): + mlogbdefect = MlogbDefectSerializer(many=True, required=False) + class Meta: model = Mlogb fields = "__all__" read_only_fields = EXCLUDE_FIELDS_BASE + ['mlog', 'mtask', 'wm_in', 'material_in', 'material_out', - 'count_use', 'count_break', 'count_pn_jgqbl'] + 'count_use', 'count_break', 'count_pn_jgqbl', 'mlogbdefect'] + + def create(self, validated_data): + material_out:Material = validated_data["material_out"] + mlogbdefect = validated_data.pop("mlogbdefect", []) + with transaction.atomic(): + ins = super().create(validated_data) + if mlogbdefect and material_out.tracking == Material.MA_TRACKING_BATCH: + count_notok = 0 + mlogbdefect_new = [item for item in mlogbdefect if item["count"] > 0] + for item in mlogbdefect_new: + defect:Defect = item["defect"] + Mlogb.objects.create(mlogb=ins, defect=defect, count=item["count"]) + if defect.cate == Defect.DEFECT_NOTOK: + count_notok +=1 + ins.count_notok = count_notok + ins.count_ok = ins.count_real - ins.count_notok + ins.save() + else: + raise ParseError("mlogbdefect仅支持批次件") + return ins + + def update(self, instance, validated_data): + material_out:Material = validated_data["material_out"] + mlogbdefect = validated_data.pop("mlogbdefect", []) + with transaction.atomic(): + ins:Mlogb = super().update(instance, validated_data) + if mlogbdefect and material_out.tracking == Material.MA_TRACKING_BATCH: + defectIds = [item["defect"].id for item in mlogbdefect if item["count"] > 0] + Mlogb.objects.filter(mlogb=ins).exclude(defect_id__in=defectIds).delete() + count_notok = 0 + for item in mlogbdefect: + defect = item["defect"] + insb = MlogbDefect.objects.get(mlogb=ins, defect=defect) + insb.count = item["count"] + insb.save(update_fields=["count"]) + if defect.cate == Defect.DEFECT_NOTOK: + count_notok +=1 + ins.count_notok = count_notok + ins.count_ok = ins.count_real + ins.save() + return ins def validate(self, attrs): - count_notok_json = attrs.get('count_notok_json', []) - # count_notok_json字段处理 - if count_notok_json: - # 先置0字段 + mlogbdefect = attrs.get("mlogbdefect", []) + if mlogbdefect: + pass + # attrs.pop("count_notok_json") + # for i in attrs: + # if 'count_n_' in i: + # attrs.pop(i, None) + else: + count_notok_json = attrs.get('count_notok_json', []) + # count_notok_json字段处理 + if count_notok_json: + # 先置0字段 + for i in attrs: + if 'count_n_' in i: + i = 0 + count_notok_dict = {} + for item in count_notok_json: + notok = item['notok'] + count = item['count'] + full_notok = f'count_n_{notok}' + if not hasattr(Mlogb, full_notok): + raise ParseError(f'{notok}-该不合格项不存在') + if full_notok in count_notok_dict: + count_notok_dict[full_notok] = count_notok_dict[full_notok] + count + else: + count_notok_dict[full_notok] = count + for k, v in count_notok_dict.items(): + attrs[k] = v + + count_notok = 0 for i in attrs: if 'count_n_' in i: - i = 0 - count_notok_dict = {} - for item in count_notok_json: - notok = item['notok'] - count = item['count'] - full_notok = f'count_n_{notok}' - if not hasattr(Mlogb, full_notok): - raise ParseError(f'{notok}-该不合格项不存在') - if full_notok in count_notok_dict: - count_notok_dict[full_notok] = count_notok_dict[full_notok] + count - else: - count_notok_dict[full_notok] = count - for k, v in count_notok_dict.items(): - attrs[k] = v - - count_notok = 0 - for i in attrs: - if 'count_n_' in i: - if not hasattr(Mlogb, i): - raise ParseError(f'{i}不存在') - count_notok = count_notok + attrs[i] - attrs['count_notok'] = count_notok + if not hasattr(Mlogb, i): + raise ParseError(f'{i}不存在') + count_notok = count_notok + attrs[i] + attrs['count_notok'] = count_notok if attrs['count_real'] >= attrs['count_ok'] + attrs['count_notok']: pass else: diff --git a/apps/wpm/services.py b/apps/wpm/services.py index e5332027..80b3254b 100644 --- a/apps/wpm/services.py +++ b/apps/wpm/services.py @@ -170,6 +170,7 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): for mi in m_ins.all(): m_ins_list.append((mi.material_in, mi.batch, mi.count_use, mi)) if mi.count_pn_jgqbl > 0: + raise ParseError("暂不支持加工前不良") m_ins_bl_list.append((mi.material_in, mi.batch, mi.count_pn_jgqbl, mi)) else: m_ins_list = [(material_in, mlog.batch, mlog.count_use, mlog)] @@ -446,6 +447,7 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): for mi in m_ins.all(): m_ins_list.append((mi.material_in, mi.batch, mi.count_use, mi.wm_in, mi)) if mi.count_pn_jgqbl > 0: + raise ParseError("暂不支持加工前不良") m_ins_bl_list.append((mi.material_in, mi.batch, mi.count_pn_jgqbl, mi)) else: m_ins_list = [(material_in, mlog.batch, mlog.count_use, mlog.wm_in, mlog)] From 1bddfaa92098f83606e7d2110a2e76166c187d62 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 28 Feb 2025 09:18:10 +0800 Subject: [PATCH 03/32] fix: get_alldata_with_batch bug --- apps/wpm/services_2.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/wpm/services_2.py b/apps/wpm/services_2.py index 355976cf..7fea0f5c 100644 --- a/apps/wpm/services_2.py +++ b/apps/wpm/services_2.py @@ -126,7 +126,8 @@ def get_alldata_with_batch(batch: str): data["七车间入库_车间执行人"] = list(set(data["七车间入库_车间执行人"])) data["七车间入库_车间执行人"] = ";".join([item.name for item in data["七车间入库_车间执行人"]]) data["七车间入库_仓库执行人"] = list(set(data["七车间入库_仓库执行人"])) - data["七车间入库_仓库执行人"].remove(None) + if None in data["七车间入库_仓库执行人"]: + data["七车间入库_仓库执行人"].remove(None) data["七车间入库_仓库执行人"] = ";".join([item.name for item in data["七车间入库_仓库执行人"]]) # 十车间入库检验 From fb7fbb7388002d60f1fad43827d9b8b45400987f Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 28 Feb 2025 09:41:13 +0800 Subject: [PATCH 04/32] fix: drf swagger bug --- apps/wpm/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index b20f1285..c5576b61 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -691,7 +691,7 @@ class MlogbwCreateUpdateSerializer(CustomModelSerializer): class MlogbDefectSerializer(CustomModelSerializer): defect_name = serializers.CharField(source="defect.name", read_only=True) class Meta: - model = Mlogb + model = MlogbDefect fields = ["id", "defect_name", "count", "mlogb", "defect"] class MlogbOutUpdateSerializer(CustomModelSerializer): From fac85d17d2f57d2d73dc78b729039705991c6a84 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 28 Feb 2025 09:50:30 +0800 Subject: [PATCH 05/32] =?UTF-8?q?feat:=20defect=20code=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E4=B8=BA=E9=9D=9E=E5=BF=85=E5=A1=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/qm/migrations/0045_alter_defect_code.py | 18 ++++++++++++++++++ apps/qm/models.py | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 apps/qm/migrations/0045_alter_defect_code.py diff --git a/apps/qm/migrations/0045_alter_defect_code.py b/apps/qm/migrations/0045_alter_defect_code.py new file mode 100644 index 00000000..971cc386 --- /dev/null +++ b/apps/qm/migrations/0045_alter_defect_code.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2025-02-28 01:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('qm', '0044_testitem_cd_expr'), + ] + + operations = [ + migrations.AlterField( + model_name='defect', + name='code', + field=models.CharField(blank=True, max_length=50, null=True, verbose_name='标识'), + ), + ] diff --git a/apps/qm/models.py b/apps/qm/models.py index f15e433d..cdaf2701 100644 --- a/apps/qm/models.py +++ b/apps/qm/models.py @@ -15,7 +15,7 @@ class Defect(CommonAModel): DEFECT_OK_B = 20 DEFECT_NOTOK = 30 name = models.CharField(max_length=50, verbose_name="名称") - code = models.CharField(max_length=50, verbose_name="标识") + code = models.CharField(max_length=50, verbose_name="标识", null=True, blank=True) cate = models.CharField(max_length=50, verbose_name="分类", choices=(("尺寸", "尺寸"), ("外观", "外观"), ("内质", "内质"))) okcate= models.PositiveSmallIntegerField(verbose_name="不合格分类", choices=((DEFECT_OK, "合格"), (DEFECT_OK_B, "合格B类"), (DEFECT_NOTOK, "不合格")), From 77f7a454a19ab63140750e21f4507e67335dce40 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 28 Feb 2025 11:28:40 +0800 Subject: [PATCH 06/32] =?UTF-8?q?feat:=20mlogbw=E5=8F=98=E6=9B=B4=E5=87=BA?= =?UTF-8?q?=E5=8F=91mlogbdefect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/serializers.py | 7 ++++--- apps/wpm/views.py | 15 +++++++++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index c5576b61..e4660110 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -729,16 +729,17 @@ class MlogbOutUpdateSerializer(CustomModelSerializer): with transaction.atomic(): ins:Mlogb = super().update(instance, validated_data) if mlogbdefect and material_out.tracking == Material.MA_TRACKING_BATCH: - defectIds = [item["defect"].id for item in mlogbdefect if item["count"] > 0] - Mlogb.objects.filter(mlogb=ins).exclude(defect_id__in=defectIds).delete() count_notok = 0 + md_ids = [] for item in mlogbdefect: defect = item["defect"] - insb = MlogbDefect.objects.get(mlogb=ins, defect=defect) + insb, _ = MlogbDefect.objects.get_or_create(mlogb=ins, defect=defect) insb.count = item["count"] insb.save(update_fields=["count"]) if defect.cate == Defect.DEFECT_NOTOK: count_notok +=1 + md_ids.append(insb.id) + Mlogbw.objects.filter(mlogb=ins).exclude(id__in=md_ids).delete() ins.count_notok = count_notok ins.count_ok = ins.count_real ins.save() diff --git a/apps/wpm/views.py b/apps/wpm/views.py index 70ebafd8..f8da0bab 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -16,7 +16,7 @@ from apps.utils.mixins import CustomListModelMixin, BulkCreateModelMixin, BulkDe from .filters import StLogFilter, SfLogFilter, WMaterialFilter, MlogFilter, HandoverFilter, MlogbFilter, BatchStFilter from .models import (SfLog, SfLogExp, StLog, WMaterial, Mlog, Handover, Mlogb, - Mlogbw, AttLog, OtherLog, Fmlog, BatchSt) + Mlogbw, AttLog, OtherLog, Fmlog, BatchSt, MlogbDefect) from .serializers import (SflogExpSerializer, SfLogSerializer, StLogSerializer, WMaterialSerializer, MlogRevertSerializer, MlogSerializer, MlogRelatedSerializer, DeptBatchSerializer, HandoverSerializer, @@ -34,6 +34,7 @@ from apps.qm.models import QctMat, Ftest from apps.enm.models import EnStat from django.db.models import Q from apps.utils.tools import convert_ordereddict +from django.db.models import Count # Create your views here. @@ -678,7 +679,17 @@ class MlogbwViewSet(CustomModelViewSet): mlogb.count_use = count elif mlogb.material_out: mlogb.count_real = count - mlogb.count_notok = Mlogbw.objects.filter(mlogb=mlogb, ftest__is_ok=False).count() + count_notok = 0 + tqs = Mlogbw.objects.filter(mlogb=mlogb, ftest__is_ok=False).values("defect_main").annotate(xcount=Count('id')) + md_ids = [] + for t in tqs: + md, _ = MlogbDefect.objects.get_or_create(mlogb=mlogb, defect=t["defect_main"]) + md.count = t["xcount"] + md.save() + md_ids.append(md.id) + count_notok += t["xcount"] + MlogbDefect.objects.filter(mlogb=mlogb).exclude(id__in=md_ids).delete() + mlogb.count_notok = count_notok mlogb.count_ok = count - mlogb.count_notok mlogb.save() From 286a5306e37f83eeaece1a1037708a4ebfcecf1a Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 28 Feb 2025 15:31:38 +0800 Subject: [PATCH 07/32] =?UTF-8?q?feat:=20=E6=89=B9=E6=AC=A1=E5=A4=84?= =?UTF-8?q?=E7=90=86=E5=81=9A=E5=85=BC=E5=AE=B9=E6=80=A7=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/qm/models.py | 2 +- apps/wpm/models.py | 20 +++++++++ apps/wpm/serializers.py | 2 +- apps/wpm/services.py | 95 +++++++++++++++++++++++------------------ apps/wpm/views.py | 43 ++++++------------- 5 files changed, 89 insertions(+), 73 deletions(-) diff --git a/apps/qm/models.py b/apps/qm/models.py index cdaf2701..b5557dfb 100644 --- a/apps/qm/models.py +++ b/apps/qm/models.py @@ -151,7 +151,7 @@ QC_TRACE_CHOICES = ( class Qct(CommonAModel): name = models.CharField(max_length=50, verbose_name="名称") number = models.CharField(max_length=20, verbose_name="编号") - tags = models.JSONField('检测类型', default=list, blank=True) + tags = models.JSONField('检测类型', default=list, blank=True) # process 和 inm testitems = models.ManyToManyField(TestItem, verbose_name="检测项", blank=True, through='qm.qcttestitem') defects = models.ManyToManyField(Defect, verbose_name="缺陷项", blank=True, through='qm.qctdefect') materials = models.ManyToManyField(Material, verbose_name="物料", blank=True, through='qm.qctmat') diff --git a/apps/wpm/models.py b/apps/wpm/models.py index 8cb6e55a..ee86b37a 100644 --- a/apps/wpm/models.py +++ b/apps/wpm/models.py @@ -382,6 +382,26 @@ class Mlogbw(BaseModel): on_delete=models.PROTECT, null=True, blank=True, related_name="mlogbw_ftest") note = models.TextField('备注', null=True, blank=True) + @classmethod + def cal_count_notok(mlogb: Mlog): + count = Mlogbw.objects.filter(mlogb=mlogb).count() + if mlogb.material_in: + mlogb.count_use = count + elif mlogb.material_out: + mlogb.count_real = count + count_notok = 0 + tqs = Mlogbw.objects.filter(mlogb=mlogb, ftest__is_ok=False).values("defect_main").annotate(xcount=Count('id')) + md_ids = [] + for t in tqs: + md, _ = MlogbDefect.objects.get_or_create(mlogb=mlogb, defect=t["defect_main"]) + md.count = t["xcount"] + md.save() + md_ids.append(md.id) + count_notok += t["xcount"] + MlogbDefect.objects.filter(mlogb=mlogb).exclude(id__in=md_ids).delete() + mlogb.count_notok = count_notok + mlogb.count_ok = count - mlogb.count_notok + mlogb.save() class Handover(CommonADModel): """ diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index e4660110..3a29fb50 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -724,7 +724,7 @@ class MlogbOutUpdateSerializer(CustomModelSerializer): return ins def update(self, instance, validated_data): - material_out:Material = validated_data["material_out"] + material_out:Material = instance.material_out mlogbdefect = validated_data.pop("mlogbdefect", []) with transaction.atomic(): ins:Mlogb = super().update(instance, validated_data) diff --git a/apps/wpm/services.py b/apps/wpm/services.py index 80b3254b..59edc78f 100644 --- a/apps/wpm/services.py +++ b/apps/wpm/services.py @@ -11,7 +11,7 @@ from apps.system.models import User from apps.pm.models import Mtask from apps.mtm.models import Mgroup, Shift, Material, Route, RoutePack, Team, Srule -from .models import SfLog, WMaterial, Mlog, Mlogb, Mlogbw, Handover, Handoverb, Handoverbw +from .models import SfLog, WMaterial, Mlog, Mlogb, Mlogbw, Handover, Handoverb, Handoverbw, MlogbDefect from apps.mtm.services_2 import cal_material_count from apps.wf.models import Ticket from apps.utils.thread import MyThread @@ -233,29 +233,35 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): process = mgroup.process into_wm_mgroup = process.into_wm_mgroup need_store_notok = process.store_notok - m_outs = Mlogb.objects.filter(mlog=mlog, material_out__isnull=False) + mlogb_out_qs = Mlogb.objects.filter(mlog=mlog, material_out__isnull=False) stored_notok = need_store_notok stored_mgroup = need_store_notok - if m_outs.exists(): - m_outs_list = [(mo.material_out, mo.batch if mo.batch else mlog.batch, mo.count_ok, mlog.count_real_eweight, None, mo) for mo in m_outs.all()] + if mlogb_out_qs.exists(): + m_outs_list = [(mo.material_out, mo.batch if mo.batch else mlog.batch, mo.count_ok, mlog.count_real_eweight, None, mo) for mo in mlogb_out_qs.all()] if need_store_notok: - for item in m_outs: - if item.material_out.tracking == Material.MA_TRACKING_SINGLE: - # 获取所有主要的不合格项 - 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() - defects_map = {d.id: d for d in Defect.objects.filter(id__in=defectIds)} - # 过滤并统计相关数据 - filtered_bw_qs = bw_qs.filter( - ftest__defect_main__id__in=defects_map.keys() - ).values('ftest__defect_main__id').annotate(xcount=Count('id')) - # 整理结果 - for defect_data in filtered_bw_qs: - defect_id = defect_data['ftest__defect_main__id'] - xcount = defect_data['xcount'] - if xcount > 0: - defect = defects_map[defect_id] - m_outs_list.append((item.material_out, item.batch, xcount, 0, defect, item)) + for item in mlogb_out_qs: + if item.qct is not None: + if MlogbDefect.objects.filter(mlogb=item).exists(): + pass + else: + Mlogbw.cal_count_notok(item) + for itemx in MlogbDefect.objects.filter(mlogb=item): + 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() + # defects_map = {d.id: d for d in Defect.objects.filter(id__in=defectIds)} + # # 过滤并统计相关数据 + # filtered_bw_qs = bw_qs.filter( + # ftest__defect_main__id__in=defects_map.keys() + # ).values('ftest__defect_main__id').annotate(xcount=Count('id')) + # # 整理结果 + # for defect_data in filtered_bw_qs: + # defect_id = defect_data['ftest__defect_main__id'] + # xcount = defect_data['xcount'] + # if xcount > 0: + # defect = defects_map[defect_id] + # m_outs_list.append((item.material_out, item.batch, xcount, 0, defect, item)) else: for f in Mlogb._meta.fields: if 'count_n_' in f.name and getattr(item, f.name) > 0: @@ -358,29 +364,36 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): if material_out or is_fix: # 产物退回 # 有多个产物的情况 # 需要考虑不合格品退回的情况 - m_outs = Mlogb.objects.filter(mlog=mlog, material_out__isnull=False) - if m_outs.exists(): + mlogb_out_qs = Mlogb.objects.filter(mlog=mlog, material_out__isnull=False) + if mlogb_out_qs.exists(): m_outs_list = [ (mo.material_out, mo.batch if mo.batch else mlog.batch, mo.count_ok, mlog.count_real_eweight, None, mo) - for mo in m_outs.all()] + for mo in mlogb_out_qs.all()] if stored_notok: - for item in m_outs: - if item.material_out.tracking == Material.MA_TRACKING_SINGLE: - # 获取所有主要的不合格项 - 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() - defects_map = {d.id: d for d in Defect.objects.filter(id__in=defectIds)} - # 过滤并统计相关数据 - filtered_bw_qs = bw_qs.filter( - ftest__defect_main__id__in=defects_map.keys() - ).values('ftest__defect_main__id').annotate(xcount=Count('id')) - # 整理结果 - for defect_data in filtered_bw_qs: - defect_id = defect_data['ftest__defect_main__id'] - xcount = defect_data['xcount'] - if xcount > 0: - defect = defects_map[defect_id] - m_outs_list.append((item.material_out, item.batch, xcount, 0, defect, item)) + for item in mlogb_out_qs: + if item.qct is not None: + if MlogbDefect.objects.filter(mlogb=item).exists(): + pass + else: + Mlogbw.cal_count_notok(item) + for itemx in MlogbDefect.objects.filter(mlogb=item): + 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) + # defectIds= Ftest.objects.filter(mlogbw_ftest__in=bw_qs).exclude(defect_main=None).values_list("defect_main__id", flat=True).distinct() + # defects_map = {d.id: d for d in Defect.objects.filter(id__in=defectIds)} + # # 过滤并统计相关数据 + # filtered_bw_qs = bw_qs.filter( + # ftest__defect_main__id__in=defects_map.keys() + # ).values('ftest__defect_main__id').annotate(xcount=Count('id')) + # # 整理结果 + # for defect_data in filtered_bw_qs: + # defect_id = defect_data['ftest__defect_main__id'] + # xcount = defect_data['xcount'] + # if xcount > 0: + # defect = defects_map[defect_id] + # m_outs_list.append((item.material_out, item.batch, xcount, 0, defect, item)) else: for f in Mlogb._meta.fields: if 'count_n_' in f.name and getattr(item, f.name) > 0: diff --git a/apps/wpm/views.py b/apps/wpm/views.py index f8da0bab..7c76c189 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -592,13 +592,17 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust d_count_real = mlogbin.count_use d_count_ok = mlogbin.count_use # 找寻质检表 - if material_out.tracking == Material.MA_TRACKING_SINGLE: - if is_fix and mgroup.process.type == Process.PRO_PROD: - # 如果是生产返修,则忽略质检 - pass - else: - qctmat = QctMat.objects.filter(material=material_out, qct__is_deleted=False).order_by("-create_time").first() - mlogbout.qct = qctmat.qct if qctmat else None + if is_fix and mgroup.process.type == Process.PRO_PROD: + # 如果是生产返修,则忽略质检 + pass + elif mlogbout.qct: + try: + qctmat = QctMat.objects.get(material=material_out, qct__is_deleted=False, qct__tags__contains="process").order_by("-create_time") + except QctMat.DoesNotExist: + qctmat = None + except QctMat.MultipleObjectsReturned: + raise ParseError("存在多个质检表,请手动选择") + mlogbout.qct = qctmat.qct if qctmat else None mlogbout.count_real = d_count_real mlogbout.count_ok = d_count_ok mlogbout.save() @@ -672,27 +676,6 @@ class MlogbwViewSet(CustomModelViewSet): raise ParseError('请指定所属消耗/产出明细') return super().filter_queryset(queryset) - def cal_mlogb_count(self, mlogb): - count = Mlogbw.objects.filter(mlogb=mlogb).count() - # 此处先不管检验问题 - if mlogb.material_in: - mlogb.count_use = count - elif mlogb.material_out: - mlogb.count_real = count - count_notok = 0 - tqs = Mlogbw.objects.filter(mlogb=mlogb, ftest__is_ok=False).values("defect_main").annotate(xcount=Count('id')) - md_ids = [] - for t in tqs: - md, _ = MlogbDefect.objects.get_or_create(mlogb=mlogb, defect=t["defect_main"]) - md.count = t["xcount"] - md.save() - md_ids.append(md.id) - count_notok += t["xcount"] - MlogbDefect.objects.filter(mlogb=mlogb).exclude(id__in=md_ids).delete() - mlogb.count_notok = count_notok - mlogb.count_ok = count - mlogb.count_notok - mlogb.save() - @transaction.atomic def perform_create(self, serializer): ins:Mlogbw = serializer.save() @@ -715,7 +698,7 @@ class MlogbwViewSet(CustomModelViewSet): @transaction.atomic def perform_update(self, serializer): mlogbw = serializer.save() - self.cal_mlogb_count(mlogbw.mlogb) + Mlogbw.cal_count_notok(mlogbw.mlogb) @transaction.atomic def perform_destroy(self, instance:Mlogbw): @@ -736,4 +719,4 @@ class MlogbwViewSet(CustomModelViewSet): mbws = Mlogbw.objects.filter(Q(wpr=instance.wpr)|Q(number__contains=instance.number), mlogb=mlogb_to) Ftest.objects.filter(id__in=mbws.values_list('ftest__id', flat=True)).delete() mbws.delete() - self.cal_mlogb_count(mlogb_to) + Mlogbw.cal_count_notok(mlogb) From c5bcfb4e538440051f4018f5e3e9f1c7b646ba9c Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 28 Feb 2025 15:38:36 +0800 Subject: [PATCH 08/32] =?UTF-8?q?feat:=20mlogboutupdate=E5=85=BC=E5=AE=B9?= =?UTF-8?q?=E6=80=A7=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/serializers.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index 3a29fb50..8e428df9 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -748,11 +748,10 @@ class MlogbOutUpdateSerializer(CustomModelSerializer): def validate(self, attrs): mlogbdefect = attrs.get("mlogbdefect", []) if mlogbdefect: - pass - # attrs.pop("count_notok_json") - # for i in attrs: - # if 'count_n_' in i: - # attrs.pop(i, None) + attrs.pop("count_notok_json") + for i in attrs: + if 'count_n_' in i: + attrs.pop(i, None) else: count_notok_json = attrs.get('count_notok_json', []) # count_notok_json字段处理 @@ -782,10 +781,10 @@ class MlogbOutUpdateSerializer(CustomModelSerializer): raise ParseError(f'{i}不存在') count_notok = count_notok + attrs[i] attrs['count_notok'] = count_notok - if attrs['count_real'] >= attrs['count_ok'] + attrs['count_notok']: - pass - else: - raise ParseError('生产数量不能小于合格数量') + if attrs['count_real'] >= attrs['count_ok'] + attrs['count_notok']: + pass + else: + raise ParseError('生产数量不能小于合格数量') return attrs class MlogRevertSerializer(serializers.Serializer): From 31e8b92d294886ec8fc49fac81c7b749858e46ef Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 28 Feb 2025 15:50:23 +0800 Subject: [PATCH 09/32] =?UTF-8?q?feat:=20mlogbin=20create=E6=97=B6?= =?UTF-8?q?=E5=A4=84=E7=90=86qct?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/serializers.py | 2 +- apps/wpm/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index 8e428df9..633aa545 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -701,7 +701,7 @@ class MlogbOutUpdateSerializer(CustomModelSerializer): model = Mlogb fields = "__all__" read_only_fields = EXCLUDE_FIELDS_BASE + ['mlog', 'mtask', 'wm_in', 'material_in', 'material_out', - 'count_use', 'count_break', 'count_pn_jgqbl', 'mlogbdefect'] + 'count_use', 'count_break', 'count_pn_jgqbl', 'mlogbdefect', "qct"] def create(self, validated_data): material_out:Material = validated_data["material_out"] diff --git a/apps/wpm/views.py b/apps/wpm/views.py index 7c76c189..da095ae8 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -595,7 +595,7 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust if is_fix and mgroup.process.type == Process.PRO_PROD: # 如果是生产返修,则忽略质检 pass - elif mlogbout.qct: + elif mlogbout.qct is None: try: qctmat = QctMat.objects.get(material=material_out, qct__is_deleted=False, qct__tags__contains="process").order_by("-create_time") except QctMat.DoesNotExist: From a2fb5598f86fa97e8a7b22f6495d0a521c0d2d63 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 28 Feb 2025 16:23:24 +0800 Subject: [PATCH 10/32] =?UTF-8?q?fix:=20get=5Falldata=5Fwith=5Fbatch=20?= =?UTF-8?q?=E9=99=A40bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/services_2.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/wpm/services_2.py b/apps/wpm/services_2.py index 7fea0f5c..914a7490 100644 --- a/apps/wpm/services_2.py +++ b/apps/wpm/services_2.py @@ -74,7 +74,10 @@ def get_alldata_with_batch(batch: str): data["棒料成型_出料人"] = ";".join([item.name for item in data["棒料成型_出料人"]]) data["棒料成型_切料人"] = list(set(data["棒料成型_切料人"])) data["棒料成型_切料人"] = ";".join([item.name for item in data["棒料成型_切料人"]]) - data["棒料成型_合格率"] = round((data["棒料成型_count_ok"] * 100/ data["棒料成型_count_real"]), 1) + try: + data["棒料成型_合格率"] = round((data["棒料成型_count_ok"] * 100/ data["棒料成型_count_real"]), 1) + except ZeroDivisionError: + data["棒料成型_合格率"] = 0 # 管料成型数据 mgroup_glcx = Mgroup.objects.get(name="管料成型") From 9eb3abefc413eaa81f84aa7520e3efab27ec4373 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 28 Feb 2025 16:27:23 +0800 Subject: [PATCH 11/32] fix: mlogbw create bug --- apps/wpm/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wpm/views.py b/apps/wpm/views.py index da095ae8..b8e762de 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -681,7 +681,7 @@ class MlogbwViewSet(CustomModelViewSet): ins:Mlogbw = serializer.save() route:Route = ins.mlogb.mlog.route mlogb:Mlogb = ins.mlogb - self.cal_mlogb_count(mlogb) + Mlogbw.cal_count_notok(mlogb) # 如果是输入且输出追踪到个,需同步创建 material_in:Material = mlogb.material_in if material_in is not None: From 12416a4f61c6859ad0d1f56f893db7cb1bbc6b00 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 28 Feb 2025 16:30:47 +0800 Subject: [PATCH 12/32] fix: mlogbw create bug2 --- apps/wpm/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wpm/views.py b/apps/wpm/views.py index b8e762de..41464351 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -597,7 +597,7 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust pass elif mlogbout.qct is None: try: - qctmat = QctMat.objects.get(material=material_out, qct__is_deleted=False, qct__tags__contains="process").order_by("-create_time") + qctmat = QctMat.objects.get(material=material_out, qct__is_deleted=False, qct__tags__contains="process") except QctMat.DoesNotExist: qctmat = None except QctMat.MultipleObjectsReturned: From 278e55c08a7f331b886a5f72dc185897db235241 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 28 Feb 2025 16:39:49 +0800 Subject: [PATCH 13/32] =?UTF-8?q?fix:=20cal=5Fcount=5Fnotok=20=E6=98=AF?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E7=B1=BB=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wpm/models.py b/apps/wpm/models.py index ee86b37a..843dfad9 100644 --- a/apps/wpm/models.py +++ b/apps/wpm/models.py @@ -383,7 +383,7 @@ class Mlogbw(BaseModel): note = models.TextField('备注', null=True, blank=True) @classmethod - def cal_count_notok(mlogb: Mlog): + def cal_count_notok(cls, mlogb: Mlog): count = Mlogbw.objects.filter(mlogb=mlogb).count() if mlogb.material_in: mlogb.count_use = count From 2311e1773ff5af632e593e3f58743c5e8621e9ef Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 28 Feb 2025 16:49:08 +0800 Subject: [PATCH 14/32] fix: cal_count_notok bug --- apps/wpm/models.py | 5 +++-- apps/wpm/services.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/wpm/models.py b/apps/wpm/models.py index 843dfad9..9db7b352 100644 --- a/apps/wpm/models.py +++ b/apps/wpm/models.py @@ -10,6 +10,7 @@ from apps.pum.models import Supplier from django.db.models import Sum, Subquery from django.utils.translation import gettext_lazy as _ from rest_framework.exceptions import ParseError +from django.db.models import Count # Create your models here. @@ -390,10 +391,10 @@ class Mlogbw(BaseModel): elif mlogb.material_out: mlogb.count_real = count count_notok = 0 - tqs = Mlogbw.objects.filter(mlogb=mlogb, ftest__is_ok=False).values("defect_main").annotate(xcount=Count('id')) + tqs = Mlogbw.objects.filter(mlogb=mlogb, ftest__is_ok=False).values("ftest__defect_main").annotate(xcount=Count('id')) md_ids = [] for t in tqs: - md, _ = MlogbDefect.objects.get_or_create(mlogb=mlogb, defect=t["defect_main"]) + md, _ = MlogbDefect.objects.get_or_create(mlogb=mlogb, defect=t["ftest__defect_main"]) md.count = t["xcount"] md.save() md_ids.append(md.id) diff --git a/apps/wpm/services.py b/apps/wpm/services.py index 59edc78f..60a15adf 100644 --- a/apps/wpm/services.py +++ b/apps/wpm/services.py @@ -243,7 +243,7 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): if item.qct is not None: if MlogbDefect.objects.filter(mlogb=item).exists(): pass - else: + elif item.material_out.tracking == Material.MA_TRACKING_SINGLE: Mlogbw.cal_count_notok(item) for itemx in MlogbDefect.objects.filter(mlogb=item): m_outs_list.append((item.material_out, item.batch, itemx.count, 0, itemx.defect, item)) @@ -374,7 +374,7 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): if item.qct is not None: if MlogbDefect.objects.filter(mlogb=item).exists(): pass - else: + elif item.material_out.tracking == Material.MA_TRACKING_SINGLE: Mlogbw.cal_count_notok(item) for itemx in MlogbDefect.objects.filter(mlogb=item): m_outs_list.append((item.material_out, item.batch, itemx.count, 0, itemx.defect, item)) From 7c7dcee19442f2df89f3148449a78d2a8a4f212c Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 28 Feb 2025 16:57:46 +0800 Subject: [PATCH 15/32] fix: cal_count_notok bug2 --- apps/wpm/models.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/wpm/models.py b/apps/wpm/models.py index 9db7b352..9ba8cb03 100644 --- a/apps/wpm/models.py +++ b/apps/wpm/models.py @@ -385,16 +385,19 @@ class Mlogbw(BaseModel): @classmethod def cal_count_notok(cls, mlogb: Mlog): + from apps.qm.models import Defect count = Mlogbw.objects.filter(mlogb=mlogb).count() if mlogb.material_in: mlogb.count_use = count elif mlogb.material_out: mlogb.count_real = count count_notok = 0 - tqs = Mlogbw.objects.filter(mlogb=mlogb, ftest__is_ok=False).values("ftest__defect_main").annotate(xcount=Count('id')) + 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))} md_ids = [] - for t in tqs: - md, _ = MlogbDefect.objects.get_or_create(mlogb=mlogb, defect=t["ftest__defect_main"]) + for t in tqs_a: + md, _ = MlogbDefect.objects.get_or_create(mlogb=mlogb, defect=defects[t["ftest__defect_main"]]) md.count = t["xcount"] md.save() md_ids.append(md.id) From 6ad6670a9092890e501dbe7524cf3dff48f14a13 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 28 Feb 2025 17:06:50 +0800 Subject: [PATCH 16/32] =?UTF-8?q?feat:=20mlogbdetail=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E8=BF=94=E5=9B=9Emlogbdefect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/serializers.py | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index 633aa545..0ba2f91b 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -216,15 +216,6 @@ class MlogbSerializer(CustomModelSerializer): extra_kwargs = { 'material_out': {'required': True, 'allow_null': False}} -class MlogbDetailSerializer(CustomModelSerializer): - material_out_name = serializers.StringRelatedField( - source='material_out', read_only=True) - material_in_name = serializers.StringRelatedField(source='material_in', read_only=True) - material_out_tracking = serializers.IntegerField(source="material_out.tracking", read_only=True) - - class Meta: - model = Mlogb - fields = '__all__' class MlogListSerializer(CustomModelSerializer): mstate_json = serializers.JSONField(source='mgroup.process.mstate_json', read_only=True) @@ -270,6 +261,24 @@ class MlogListSerializer(CustomModelSerializer): model = Mlog fields = '__all__' +class MlogbDefectSerializer(CustomModelSerializer): + defect_name = serializers.CharField(source="defect.name", read_only=True) + class Meta: + model = MlogbDefect + fields = ["id", "defect_name", "count", "mlogb", "defect"] + + +class MlogbDetailSerializer(CustomModelSerializer): + material_out_name = serializers.StringRelatedField( + source='material_out', read_only=True) + material_in_name = serializers.StringRelatedField(source='material_in', read_only=True) + material_out_tracking = serializers.IntegerField(source="material_out.tracking", read_only=True) + mlogbdefect = MlogbDefectSerializer(many=True, read_only=True) + + class Meta: + model = Mlogb + fields = '__all__' + class MlogSerializer(CustomModelSerializer): mstate_json = serializers.JSONField(source='mgroup.process.mstate_json', read_only=True) supplier_name = serializers.CharField(source='supplier.name', read_only=True) @@ -688,12 +697,6 @@ class MlogbwCreateUpdateSerializer(CustomModelSerializer): mlogbw = self.save_ftest(mlogbw, ftest_data) return mlogbw -class MlogbDefectSerializer(CustomModelSerializer): - defect_name = serializers.CharField(source="defect.name", read_only=True) - class Meta: - model = MlogbDefect - fields = ["id", "defect_name", "count", "mlogb", "defect"] - class MlogbOutUpdateSerializer(CustomModelSerializer): mlogbdefect = MlogbDefectSerializer(many=True, required=False) From bbed6fa6468a49d1f92c6d3ac0bfe9a567186f0f Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 3 Mar 2025 14:50:26 +0800 Subject: [PATCH 17/32] =?UTF-8?q?feat:=20mlogbdefect=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=AD=97=E6=AE=B5floor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/migrations/0090_mlogbdefect_floor.py | 18 ++++++++++++++++++ apps/wpm/models.py | 1 + apps/wpm/serializers.py | 7 ++++--- 3 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 apps/wpm/migrations/0090_mlogbdefect_floor.py diff --git a/apps/wpm/migrations/0090_mlogbdefect_floor.py b/apps/wpm/migrations/0090_mlogbdefect_floor.py new file mode 100644 index 00000000..3add2b32 --- /dev/null +++ b/apps/wpm/migrations/0090_mlogbdefect_floor.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2025-03-03 06:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wpm', '0089_mlogbdefect'), + ] + + operations = [ + migrations.AddField( + model_name='mlogbdefect', + name='floor', + field=models.IntegerField(blank=True, null=True, verbose_name='层数'), + ), + ] diff --git a/apps/wpm/models.py b/apps/wpm/models.py index 9ba8cb03..322c959e 100644 --- a/apps/wpm/models.py +++ b/apps/wpm/models.py @@ -367,6 +367,7 @@ class Mlogb(BaseModel): class MlogbDefect(BaseModel): mlogb = models.ForeignKey(Mlogb, verbose_name='生产记录', on_delete=models.CASCADE) defect = models.ForeignKey("qm.Defect", verbose_name='缺陷', on_delete=models.CASCADE) + floor = models.IntegerField("层数", null=True, blank=True) count = models.PositiveIntegerField('数量', default=0) class Mlogbw(BaseModel): diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index 0ba2f91b..5ed2f485 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -265,7 +265,8 @@ class MlogbDefectSerializer(CustomModelSerializer): defect_name = serializers.CharField(source="defect.name", read_only=True) class Meta: model = MlogbDefect - fields = ["id", "defect_name", "count", "mlogb", "defect"] + fields = ["id", "defect_name", "count", "mlogb", "defect", "floor"] + read_only_fields = EXCLUDE_FIELDS_BASE + ["mlogb"] class MlogbDetailSerializer(CustomModelSerializer): @@ -716,7 +717,7 @@ class MlogbOutUpdateSerializer(CustomModelSerializer): mlogbdefect_new = [item for item in mlogbdefect if item["count"] > 0] for item in mlogbdefect_new: defect:Defect = item["defect"] - Mlogb.objects.create(mlogb=ins, defect=defect, count=item["count"]) + MlogbDefect.objects.create(mlogb=ins, **item) if defect.cate == Defect.DEFECT_NOTOK: count_notok +=1 ins.count_notok = count_notok @@ -736,7 +737,7 @@ class MlogbOutUpdateSerializer(CustomModelSerializer): md_ids = [] for item in mlogbdefect: defect = item["defect"] - insb, _ = MlogbDefect.objects.get_or_create(mlogb=ins, defect=defect) + insb, _ = MlogbDefect.objects.get_or_create(mlogb=ins, defect=defect, floor=item.get('floor', None)) insb.count = item["count"] insb.save(update_fields=["count"]) if defect.cate == Defect.DEFECT_NOTOK: From 666fbd306b1f378bcb1b05b1ccd0544b29ca6bb0 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 3 Mar 2025 15:13:28 +0800 Subject: [PATCH 18/32] =?UTF-8?q?fix:=20=E8=8E=B7=E5=8F=96qct=E6=8F=90?= =?UTF-8?q?=E5=87=BA=E5=88=B0model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/qm/models.py | 10 ++++++++++ apps/qm/serializers.py | 12 +++++++++--- apps/wpm/serializers.py | 2 +- apps/wpm/views.py | 10 ++-------- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/apps/qm/models.py b/apps/qm/models.py index b5557dfb..48893123 100644 --- a/apps/qm/models.py +++ b/apps/qm/models.py @@ -168,6 +168,16 @@ class Qct(CommonAModel): def qct_mats(self): return QctMat.objects.filter(qct=self) + @classmethod + def get(self, material:Material, cate:str): + try: + qctmat = QctMat.objects.get(material=material, qct__is_deleted=False, qct__tags__contains=cate) + except QctMat.DoesNotExist: + qctmat = None + except QctMat.MultipleObjectsReturned: + raise ParseError("存在多个质检表,请手动选择") + return qctmat.qct if qctmat else None + class QctTestItem(BaseModel): qct = models.ForeignKey(Qct, verbose_name="质检模板", on_delete=models.CASCADE, related_name="qcttestitem") testitem = models.ForeignKey(TestItem, verbose_name="检测项", on_delete=models.CASCADE, null=True, blank=True) diff --git a/apps/qm/serializers.py b/apps/qm/serializers.py index df6088c8..9ee1ffa5 100644 --- a/apps/qm/serializers.py +++ b/apps/qm/serializers.py @@ -199,24 +199,30 @@ class FtestWorkCreateUpdateSerializer(CustomModelSerializer): ftestworkdefect = validated_data.pop("ftestworkdefect", []) with transaction.atomic(): ins: FtestWork = super().create(validated_data) + qct = Qct.get(ins.material, "inm") + if qct: + ins.qct = qct + ins.save() for ftestworkdefect in ftestworkdefect: FtestworkDefect.objects.create(ftestwork=ins, **ftestworkdefect) - if ftestworkdefect: + if ins.qct or ftestworkdefect: ins.cal_count() return ins def update(self, instance, validated_data): ftestworkdefect = validated_data.pop("ftestworkdefect", []) - ins = super().update(instance, validated_data) + ins:FtestWork = super().update(instance, validated_data) with transaction.atomic(): for item in ftestworkdefect: try: ins = FtestworkDefect.objects.get(ftestwork=ins, defect=item["defect"]) except FtestworkDefect.DoesNotExist: raise ParseError("新的缺陷项!") + except FtestworkDefect.MultipleObjectsReturned: + raise ParseError("缺陷项重复!") ins.count = item["count"] ins.save() - if ftestworkdefect: + if ins.qct or ftestworkdefect: ins.cal_count() return ins diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index 5ed2f485..3ff30245 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -752,7 +752,7 @@ class MlogbOutUpdateSerializer(CustomModelSerializer): def validate(self, attrs): mlogbdefect = attrs.get("mlogbdefect", []) if mlogbdefect: - attrs.pop("count_notok_json") + attrs.pop("count_notok_json", None) for i in attrs: if 'count_n_' in i: attrs.pop(i, None) diff --git a/apps/wpm/views.py b/apps/wpm/views.py index 41464351..330b3e86 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -30,7 +30,7 @@ from .services import mlog_submit, handover_submit, mlog_revert from apps.wpm.services import mlog_submit_validate, generate_new_batch from apps.wf.models import State from apps.wpmw.models import Wpr -from apps.qm.models import QctMat, Ftest +from apps.qm.models import Qct, Ftest from apps.enm.models import EnStat from django.db.models import Q from apps.utils.tools import convert_ordereddict @@ -596,13 +596,7 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust # 如果是生产返修,则忽略质检 pass elif mlogbout.qct is None: - try: - qctmat = QctMat.objects.get(material=material_out, qct__is_deleted=False, qct__tags__contains="process") - except QctMat.DoesNotExist: - qctmat = None - except QctMat.MultipleObjectsReturned: - raise ParseError("存在多个质检表,请手动选择") - mlogbout.qct = qctmat.qct if qctmat else None + mlogbout.qct = Qct.get(mlogbout.material_out, "process") mlogbout.count_real = d_count_real mlogbout.count_ok = d_count_ok mlogbout.save() From 7e4059666589a194cb4b9fdc0293f6f7e2ce2699 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 3 Mar 2025 15:47:53 +0800 Subject: [PATCH 19/32] fix: mlogbupdate bug --- apps/wpm/serializers.py | 58 ++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index 3ff30245..e550f3f5 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -707,45 +707,45 @@ class MlogbOutUpdateSerializer(CustomModelSerializer): read_only_fields = EXCLUDE_FIELDS_BASE + ['mlog', 'mtask', 'wm_in', 'material_in', 'material_out', 'count_use', 'count_break', 'count_pn_jgqbl', 'mlogbdefect', "qct"] - def create(self, validated_data): - material_out:Material = validated_data["material_out"] - mlogbdefect = validated_data.pop("mlogbdefect", []) - with transaction.atomic(): - ins = super().create(validated_data) - if mlogbdefect and material_out.tracking == Material.MA_TRACKING_BATCH: - count_notok = 0 - mlogbdefect_new = [item for item in mlogbdefect if item["count"] > 0] - for item in mlogbdefect_new: - defect:Defect = item["defect"] - MlogbDefect.objects.create(mlogb=ins, **item) - if defect.cate == Defect.DEFECT_NOTOK: - count_notok +=1 - ins.count_notok = count_notok - ins.count_ok = ins.count_real - ins.count_notok - ins.save() - else: - raise ParseError("mlogbdefect仅支持批次件") - return ins + # def create(self, validated_data): + # material_out:Material = validated_data["material_out"] + # mlogbdefect = validated_data.pop("mlogbdefect", []) + # with transaction.atomic(): + # ins = super().create(validated_data) + # if mlogbdefect and material_out.tracking == Material.MA_TRACKING_BATCH: + # count_notok = 0 + # mlogbdefect_new = [item for item in mlogbdefect if item["count"] > 0] + # for item in mlogbdefect_new: + # defect:Defect = item["defect"] + # MlogbDefect.objects.create(mlogb=ins, **item) + # if defect.cate == Defect.DEFECT_NOTOK: + # count_notok +=1 + # ins.count_notok = count_notok + # ins.count_ok = ins.count_real - ins.count_notok + # ins.save() + # else: + # raise ParseError("mlogbdefect仅支持批次件") + # return ins def update(self, instance, validated_data): - material_out:Material = instance.material_out mlogbdefect = validated_data.pop("mlogbdefect", []) with transaction.atomic(): ins:Mlogb = super().update(instance, validated_data) - if mlogbdefect and material_out.tracking == Material.MA_TRACKING_BATCH: + if (instance.qct or mlogbdefect) and instance.material_out.tracking == Material.MA_TRACKING_BATCH: count_notok = 0 md_ids = [] for item in mlogbdefect: - defect = item["defect"] - insb, _ = MlogbDefect.objects.get_or_create(mlogb=ins, defect=defect, floor=item.get('floor', None)) - insb.count = item["count"] - insb.save(update_fields=["count"]) - if defect.cate == Defect.DEFECT_NOTOK: - count_notok +=1 + defect:Defect = item["defect"] + if item["count"] > 0: + insb, _ = MlogbDefect.objects.get_or_create(mlogb=ins, defect=defect, floor=item.get('floor', None)) + insb.count = item["count"] + insb.save(update_fields=["count"]) + if defect.okcate == Defect.DEFECT_NOTOK: + count_notok += item["count"] md_ids.append(insb.id) - Mlogbw.objects.filter(mlogb=ins).exclude(id__in=md_ids).delete() + MlogbDefect.objects.filter(mlogb=ins).exclude(id__in=md_ids).delete() ins.count_notok = count_notok - ins.count_ok = ins.count_real + ins.count_ok = ins.count_real - ins.count_notok ins.save() return ins From 8fdd56555f7fc0cfe7f9d832a09056088cc617b2 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 3 Mar 2025 15:53:00 +0800 Subject: [PATCH 20/32] =?UTF-8?q?feat:=20testitem=20description=E5=8F=AF?= =?UTF-8?q?=E4=B8=BA=E7=A9=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../0046_alter_testitem_description.py | 18 ++++++++++++++++++ apps/qm/models.py | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 apps/qm/migrations/0046_alter_testitem_description.py diff --git a/apps/qm/migrations/0046_alter_testitem_description.py b/apps/qm/migrations/0046_alter_testitem_description.py new file mode 100644 index 00000000..87e3870e --- /dev/null +++ b/apps/qm/migrations/0046_alter_testitem_description.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2025-03-03 07:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('qm', '0045_alter_defect_code'), + ] + + operations = [ + migrations.AlterField( + model_name='testitem', + name='description', + field=models.TextField(blank=True, null=True, verbose_name='描述'), + ), + ] diff --git a/apps/qm/models.py b/apps/qm/models.py index 48893123..4142c22b 100644 --- a/apps/qm/models.py +++ b/apps/qm/models.py @@ -130,7 +130,7 @@ class TestItem(CommonAModel): tags = models.JSONField('检测类型', default=list, blank=True) mcate_tags = models.JSONField('物料系列标签', default=list, blank=True) sort = models.FloatField('排序', default=1) - description = models.TextField('描述', default='') + description = models.TextField('描述', null=True, blank=True) readonly = models.BooleanField('只读', default=False) formula = models.TextField('计算公式', null=True, blank=True) affects = models.JSONField('影响项列表', default=list, blank=True) From 5245689ec22260c95da88376bd620cdf23ef6383 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 3 Mar 2025 16:17:52 +0800 Subject: [PATCH 21/32] feat: ftestwork create update serializer --- apps/qm/serializers.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/apps/qm/serializers.py b/apps/qm/serializers.py index 9ee1ffa5..e27dacf5 100644 --- a/apps/qm/serializers.py +++ b/apps/qm/serializers.py @@ -199,12 +199,9 @@ class FtestWorkCreateUpdateSerializer(CustomModelSerializer): ftestworkdefect = validated_data.pop("ftestworkdefect", []) with transaction.atomic(): ins: FtestWork = super().create(validated_data) - qct = Qct.get(ins.material, "inm") - if qct: - ins.qct = qct - ins.save() for ftestworkdefect in ftestworkdefect: - FtestworkDefect.objects.create(ftestwork=ins, **ftestworkdefect) + if ftestworkdefect['count'] > 0: + FtestworkDefect.objects.create(ftestwork=ins, **ftestworkdefect) if ins.qct or ftestworkdefect: ins.cal_count() return ins @@ -213,15 +210,19 @@ class FtestWorkCreateUpdateSerializer(CustomModelSerializer): ftestworkdefect = validated_data.pop("ftestworkdefect", []) ins:FtestWork = super().update(instance, validated_data) with transaction.atomic(): + fd_ids = [] for item in ftestworkdefect: - try: - ins = FtestworkDefect.objects.get(ftestwork=ins, defect=item["defect"]) - except FtestworkDefect.DoesNotExist: - raise ParseError("新的缺陷项!") - except FtestworkDefect.MultipleObjectsReturned: - raise ParseError("缺陷项重复!") - ins.count = item["count"] - ins.save() + if item["count"] > 0: + try: + ins = FtestworkDefect.objects.get(ftestwork=ins, defect=item["defect"]) + except FtestworkDefect.DoesNotExist: + raise ParseError("新的缺陷项!") + except FtestworkDefect.MultipleObjectsReturned: + raise ParseError("缺陷项重复!") + ins.count = item["count"] + ins.save() + fd_ids.append(ins.id) + FtestworkDefect.objects.filter(ftestwork=ins).exclude(id__in=fd_ids).delete() if ins.qct or ftestworkdefect: ins.cal_count() return ins From 9907bf7c43552915a17e313ece84bd465689a3f4 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 3 Mar 2025 18:21:03 +0800 Subject: [PATCH 22/32] =?UTF-8?q?feat:=20mlog=E5=A1=AB=E5=86=99=E6=94=AF?= =?UTF-8?q?=E6=8C=81defect=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/qm/models.py | 19 ++++ .../wpm/migrations/0091_auto_20250303_1708.py | 25 ++++++ apps/wpm/models.py | 19 +++- apps/wpm/serializers.py | 87 ++++++++++++------- apps/wpm/views.py | 6 +- 5 files changed, 120 insertions(+), 36 deletions(-) create mode 100644 apps/wpm/migrations/0091_auto_20250303_1708.py diff --git a/apps/qm/models.py b/apps/qm/models.py index 4142c22b..b10422df 100644 --- a/apps/qm/models.py +++ b/apps/qm/models.py @@ -316,6 +316,25 @@ class Ftest(CommonBDModel): FtestDefect.objects.create(ftest=ftest, defect=defect) return ftest + # @classmethod + # def cal_count_notok(cls, ftestwork: FtestWork): + # count = Ftest.objects.filter(ftest_work=ftestwork).count() + # mlogb.count_real = count + # count_notok = 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))} + # md_ids = [] + # for t in tqs_a: + # md, _ = MlogbDefect.objects.get_or_create(mlogb=mlogb, defect=defects[t["ftest__defect_main"]]) + # md.count = t["xcount"] + # md.save() + # md_ids.append(md.id) + # count_notok += t["xcount"] + # MlogbDefect.objects.filter(mlogb=mlogb).exclude(id__in=md_ids).delete() + # mlogb.count_notok = count_notok + # mlogb.count_ok = count - mlogb.count_notok + # mlogb.save() class FtestItem(BaseModel): """ diff --git a/apps/wpm/migrations/0091_auto_20250303_1708.py b/apps/wpm/migrations/0091_auto_20250303_1708.py new file mode 100644 index 00000000..d2d49439 --- /dev/null +++ b/apps/wpm/migrations/0091_auto_20250303_1708.py @@ -0,0 +1,25 @@ +# Generated by Django 3.2.12 on 2025-03-03 09:08 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('qm', '0046_alter_testitem_description'), + ('wpm', '0090_mlogbdefect_floor'), + ] + + operations = [ + migrations.AddField( + model_name='mlog', + name='qct', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='qm.qct', verbose_name='所用质检表'), + ), + migrations.AddField( + model_name='mlogbdefect', + name='count_test', + field=models.IntegerField(blank=True, null=True, verbose_name='抽检数'), + ), + ] diff --git a/apps/wpm/models.py b/apps/wpm/models.py index 322c959e..ac409394 100644 --- a/apps/wpm/models.py +++ b/apps/wpm/models.py @@ -172,6 +172,7 @@ class Mlog(CommonADModel): fill_way = models.PositiveSmallIntegerField("填写方式", default=10, help_text='10:仅二级;20:二三级;30:一二级') mtype = models.PositiveSmallIntegerField('生产类型', default=10, help_text='10:自生产;20:外协生产', choices=((10, '自生产'), (20, '外协生产'))) is_fix = models.BooleanField('是否用于返修', default=False) + qct = models.ForeignKey("qm.qct", verbose_name='所用质检表', on_delete=models.SET_NULL, null=True, blank=True) supplier = models.ForeignKey(Supplier, verbose_name='外协供应商', on_delete=models.SET_NULL, null=True, blank=True) work_start_time = models.DateTimeField('生产开始时间', null=True, blank=True) work_end_time = models.DateTimeField('生产结束时间', null=True, blank=True) @@ -288,7 +289,17 @@ class Mlog(CommonADModel): def audit_ignore_fields(self): return ['create_by', 'update_by', 'create_time', 'update_time', 'id'] - + + @property + def mlogdefect(self): + return MlogbDefect.objects.filter(mlogb__mlog=self) + + def cal_count_notok(self): + count_notok = MlogbDefect.objects.filter(defect__okcate=30, mlogb__mlog=self).aggregate(total=Sum("count"))["total"] or 0 + self.count_notok = count_notok + self.count_ok = self.count_real - count_notok + self.save(update_fields=["count_ok", "count_notok"]) + @classmethod def count_fields(cls): mlog_count_fields = [] @@ -363,10 +374,16 @@ class Mlogb(BaseModel): def mlogbdefect(self): return MlogbDefect.objects.filter(mlogb=self) + def cal_count_notok(self): + count_notok = MlogbDefect.objects.filter(defect__okcate=30, mlogb=self).aggregate(total=Sum("count"))["total"] or 0 + self.count_notok = count_notok + self.count_ok = self.count_real - count_notok + self.save(update_fields=["count_ok", "count_notok"]) class MlogbDefect(BaseModel): mlogb = models.ForeignKey(Mlogb, verbose_name='生产记录', on_delete=models.CASCADE) defect = models.ForeignKey("qm.Defect", verbose_name='缺陷', on_delete=models.CASCADE) + count_test = models.IntegerField("抽检数", null=True, blank=True) floor = models.IntegerField("层数", null=True, blank=True) count = models.PositiveIntegerField('数量', default=0) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index e550f3f5..c0588f4a 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -18,7 +18,7 @@ from django.db import transaction from django.utils import timezone from django.core.cache import cache from django.utils.timezone import localdate -from apps.qm.models import NotOkOption, Ftest +from apps.qm.models import NotOkOption, Qct from apps.wf.serializers import TicketSimpleSerializer from apps.wpmw.models import Wpr from apps.qm.serializers import FtestProcessSerializer @@ -202,12 +202,19 @@ class WMaterialSerializer(CustomModelSerializer): ret['count_cando'] = ret['count'] - ret['count_working'] return ret +class MlogbDefectSerializer(CustomModelSerializer): + defect_name = serializers.CharField(source="defect.name", read_only=True) + class Meta: + model = MlogbDefect + fields = ["id", "defect_name", "count", "mlogb", "defect", "floor", "count_test"] + read_only_fields = EXCLUDE_FIELDS_BASE + ["mlogb"] class MlogbSerializer(CustomModelSerializer): material_out_ = MaterialSimpleSerializer( source='material_out', read_only=True) material_out_name = serializers.StringRelatedField( source='material_out', read_only=True) + mlogbdefect = MlogbDefectSerializer(many=True) class Meta: model = Mlogb @@ -261,13 +268,6 @@ class MlogListSerializer(CustomModelSerializer): model = Mlog fields = '__all__' -class MlogbDefectSerializer(CustomModelSerializer): - defect_name = serializers.CharField(source="defect.name", read_only=True) - class Meta: - model = MlogbDefect - fields = ["id", "defect_name", "count", "mlogb", "defect", "floor"] - read_only_fields = EXCLUDE_FIELDS_BASE + ["mlogb"] - class MlogbDetailSerializer(CustomModelSerializer): material_out_name = serializers.StringRelatedField( @@ -324,6 +324,7 @@ class MlogSerializer(CustomModelSerializer): ticket_ = TicketSimpleSerializer(source='ticket', read_only=True) test_user_name = serializers.CharField(source='test_user.name', read_only=True) + mlogdefect = MlogbDefectSerializer(many=True) class Meta: model = Mlog fields = '__all__' @@ -338,6 +339,7 @@ class MlogSerializer(CustomModelSerializer): def create(self, validated_data): material_out = validated_data['material_out'] mtask:Mtask = validated_data.get('mtask', None) + mlogdefect = validated_data.pop('mlogdefect', []) if mtask and mtask.state != Mtask.MTASK_ASSGINED: raise ParseError('该任务非下达中不可选择') with transaction.atomic(): @@ -393,13 +395,25 @@ class MlogSerializer(CustomModelSerializer): 'count_ok': instance.count_ok, 'count_notok': instance.count_notok, 'count_break_t': instance.count_break_t } - for f in Mlogb._meta.fields: - if 'count_n_' in f.name: - add_dict_2[f.name] = getattr(instance, f.name) + need_mdfect = False + if instance.qct or mlogdefect: + need_mdfect = True + else: + for f in Mlogb._meta.fields: + if 'count_n_' in f.name: + add_dict_2[f.name] = getattr(instance, f.name) ddict = {} if wm_in: ddict = {"batch_ofrom": wm_in.batch_ofrom, "material_ofrom": wm_in.material_ofrom} - Mlogb.objects.get_or_create(**add_dict_2, defaults=ddict) + mlogb, _ = Mlogb.objects.get_or_create(**add_dict_2, defaults=ddict) + if need_mdfect: + mlogb_defect_objects = [ + MlogbDefect(**{**item, "mlogb": mlogb}) + for item in mlogdefect if item["count"] > 0 + ] + if mlogb_defect_objects: + MlogbDefect.objects.bulk_create(mlogb_defect_objects) + instance.cal_count_notok() return instance def update(self, instance, validated_data): @@ -407,6 +421,7 @@ class MlogSerializer(CustomModelSerializer): raise ParseError('不支持的填写类型') validated_data.pop('mtask', None) validated_data.pop('mgroup', None) + mlogdefect = validated_data.pop('mlogdefect', []) if instance.mtask: validated_data.pop('handle_date', None) # validated_data.pop('handle_user', None) @@ -463,11 +478,24 @@ class MlogSerializer(CustomModelSerializer): if wm_in: mox.batch_ofrom = wm_in.batch mox.material_ofrom = wm_in.material_ofrom - for f in Mlogb._meta.fields: - if 'count_n_' in f.name: - setattr(mox, f.name, getattr(instance, f.name)) + need_mdefect=False + if instance.qct or mlogdefect: + need_mdefect = True + else: + for f in Mlogb._meta.fields: + if 'count_n_' in f.name: + setattr(mox, f.name, getattr(instance, f.name)) mox.save() Mlogb.objects.filter(mlog=instance, material_out__isnull=False).exclude(id=mox.id).delete() + if need_mdefect: + MlogbDefect.objects.filter(mlogb__mlog=instance).delete() + mlogb_defect_objects = [ + MlogbDefect(**{**item, "mlogb": mlogb}) + for item in mlogdefect if item["count"] > 0 + ] + if mlogb_defect_objects: + MlogbDefect.objects.bulk_create(mlogb_defect_objects) + instance.cal_count_notok() return instance def validate(self, attrs): @@ -576,6 +604,8 @@ class MlogInitSerializer(CustomModelSerializer): raise ParseError('外协必须选择外协单位') if attrs.get('work_end_time', None): attrs['handle_date'] = localdate(attrs['work_end_time']) + if attrs["material_out"]: + attrs["qct"] = Qct.get(attrs["material_out"], "process") return attrs class MlogChangeSerializer(CustomModelSerializer): @@ -726,27 +756,20 @@ class MlogbOutUpdateSerializer(CustomModelSerializer): # else: # raise ParseError("mlogbdefect仅支持批次件") # return ins - + @transaction.atomic def update(self, instance, validated_data): mlogbdefect = validated_data.pop("mlogbdefect", []) with transaction.atomic(): ins:Mlogb = super().update(instance, validated_data) - if (instance.qct or mlogbdefect) and instance.material_out.tracking == Material.MA_TRACKING_BATCH: - count_notok = 0 - md_ids = [] - for item in mlogbdefect: - defect:Defect = item["defect"] - if item["count"] > 0: - insb, _ = MlogbDefect.objects.get_or_create(mlogb=ins, defect=defect, floor=item.get('floor', None)) - insb.count = item["count"] - insb.save(update_fields=["count"]) - if defect.okcate == Defect.DEFECT_NOTOK: - count_notok += item["count"] - md_ids.append(insb.id) - MlogbDefect.objects.filter(mlogb=ins).exclude(id__in=md_ids).delete() - ins.count_notok = count_notok - ins.count_ok = ins.count_real - ins.count_notok - ins.save() + if (ins.qct or mlogbdefect) and ins.material_out.tracking == Material.MA_TRACKING_BATCH: + MlogbDefect.objects.filter(mlogb=ins).delete() + mlogb_defect_objects = [ + MlogbDefect(**{**item, "mlogb": ins}) + for item in mlogbdefect if item["count"] > 0 + ] + if mlogb_defect_objects: + MlogbDefect.objects.bulk_create(mlogb_defect_objects) + ins.cal_count_notok() return ins def validate(self, attrs): diff --git a/apps/wpm/views.py b/apps/wpm/views.py index 330b3e86..37354a13 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -596,7 +596,7 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust # 如果是生产返修,则忽略质检 pass elif mlogbout.qct is None: - mlogbout.qct = Qct.get(mlogbout.material_out, "process") + mlogbout.qct = Qct.get(mlogbout.material_out, "process") if mlog.qct is None else mlog.qct mlogbout.count_real = d_count_real mlogbout.count_ok = d_count_ok mlogbout.save() @@ -628,11 +628,11 @@ class MlogbOutViewSet(UpdateModelMixin, CustomGenericViewSet): serializer_class = MlogbOutUpdateSerializer -class FmlogViewSet(CustomModelViewSet): +class FmlogViewSet(CustomListModelMixin, BulkCreateModelMixin, BulkDestroyModelMixin, CustomGenericViewSet): perms_map = {'get': '*', 'post': 'mlog.create', 'put': 'mlog.update', 'delete': 'mlog.delete'} queryset = Fmlog.objects.all() serializer_class = FmlogSerializer - update_serializer_class = FmlogUpdateSerializer + # update_serializer_class = FmlogUpdateSerializer filterset_fields = ['mtask', 'mgroup', 'mtask__route'] select_related_fields = ['mtask', 'mgroup', 'mtask__route', 'mtask__route__routepack'] From de27865a3ba6e3d6143818a0818a8aa20a54730f Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 3 Mar 2025 18:29:36 +0800 Subject: [PATCH 23/32] fix: swagger bug --- apps/wpm/serializers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index c0588f4a..ac77d880 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -214,7 +214,6 @@ class MlogbSerializer(CustomModelSerializer): source='material_out', read_only=True) material_out_name = serializers.StringRelatedField( source='material_out', read_only=True) - mlogbdefect = MlogbDefectSerializer(many=True) class Meta: model = Mlogb From 07f60a1a29b40f80cc60daba9225185af0acbd99 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 3 Mar 2025 19:00:51 +0800 Subject: [PATCH 24/32] =?UTF-8?q?feat:=20mlogbdefect=E8=BF=94=E5=9B=9Edefe?= =?UTF-8?q?ct=5Fokcate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/serializers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index ac77d880..f1bdc72e 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -204,9 +204,10 @@ class WMaterialSerializer(CustomModelSerializer): class MlogbDefectSerializer(CustomModelSerializer): defect_name = serializers.CharField(source="defect.name", read_only=True) + defect_okcate = serializers.CharField(source="defect.okcate", read_only=True) class Meta: model = MlogbDefect - fields = ["id", "defect_name", "count", "mlogb", "defect", "floor", "count_test"] + fields = ["id", "defect_name", "count", "mlogb", "defect", "floor", "count_test", "defect_okcate"] read_only_fields = EXCLUDE_FIELDS_BASE + ["mlogb"] class MlogbSerializer(CustomModelSerializer): From eca0f266238cd290d2db35f3232f47a7087a861a Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 3 Mar 2025 19:05:27 +0800 Subject: [PATCH 25/32] fix: bulk create bug --- apps/wpm/serializers.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index f1bdc72e..8508f2d2 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -24,6 +24,7 @@ from apps.wpmw.models import Wpr from apps.qm.serializers import FtestProcessSerializer import logging from apps.qm.models import Defect +from apps.utils.snowflake import idWorker mylogger = logging.getLogger("log") class OtherLogSerializer(CustomModelSerializer): @@ -408,7 +409,7 @@ class MlogSerializer(CustomModelSerializer): mlogb, _ = Mlogb.objects.get_or_create(**add_dict_2, defaults=ddict) if need_mdfect: mlogb_defect_objects = [ - MlogbDefect(**{**item, "mlogb": mlogb}) + MlogbDefect(**{**item, "mlogb": mlogb, "id": idWorker.get_id()}) for item in mlogdefect if item["count"] > 0 ] if mlogb_defect_objects: @@ -490,7 +491,7 @@ class MlogSerializer(CustomModelSerializer): if need_mdefect: MlogbDefect.objects.filter(mlogb__mlog=instance).delete() mlogb_defect_objects = [ - MlogbDefect(**{**item, "mlogb": mlogb}) + MlogbDefect(**{**item, "mlogb": mlogb, "id": idWorker.get_id()}) for item in mlogdefect if item["count"] > 0 ] if mlogb_defect_objects: @@ -764,7 +765,7 @@ class MlogbOutUpdateSerializer(CustomModelSerializer): if (ins.qct or mlogbdefect) and ins.material_out.tracking == Material.MA_TRACKING_BATCH: MlogbDefect.objects.filter(mlogb=ins).delete() mlogb_defect_objects = [ - MlogbDefect(**{**item, "mlogb": ins}) + MlogbDefect(**{**item, "mlogb": ins, "id": idWorker.get_id()}) for item in mlogbdefect if item["count"] > 0 ] if mlogb_defect_objects: From 46d586430eae2c3541c96a8f6092f9ec52a41d80 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 3 Mar 2025 19:09:17 +0800 Subject: [PATCH 26/32] fix: FmlogViewSet test --- apps/wpm/views.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/wpm/views.py b/apps/wpm/views.py index 37354a13..d36cd22f 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -182,6 +182,9 @@ class MlogViewSet(CustomModelViewSet): 'material_out__name', 'material_out__number', 'material_out__specification', 'material_out__model',] def add_info_for_list(self, data): + czx_dict = {} + for item in data: + czx_dict.update(item.get("oinfo_json", {})) if self.request.query_params.get('with_mlogb', False): data_dict = {item['id']: {**item, "mlogb_full": [], "mlogb": []} for item in data} mlogb_qs = Mlogb.objects.filter(mlog__id__in=data_dict.keys()).select_related("material_in", "material_out").order_by("create_time") @@ -192,7 +195,7 @@ class MlogViewSet(CustomModelViewSet): data_dict[item_dict["mlog"]]["mlogb_full"].append(item_dict) if item.get("material_out", None): data_dict[item_dict["mlog"]]["mlogb"].append(item_dict) - return list(data_dict.values()) + data = list(data_dict.values()) return data @transaction.atomic @@ -628,7 +631,7 @@ class MlogbOutViewSet(UpdateModelMixin, CustomGenericViewSet): serializer_class = MlogbOutUpdateSerializer -class FmlogViewSet(CustomListModelMixin, BulkCreateModelMixin, BulkDestroyModelMixin, CustomGenericViewSet): +class FmlogViewSet(CustomModelViewSet): perms_map = {'get': '*', 'post': 'mlog.create', 'put': 'mlog.update', 'delete': 'mlog.delete'} queryset = Fmlog.objects.all() serializer_class = FmlogSerializer From 71cbda504c18b649ca68a5c463922883cf9c2656 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 4 Mar 2025 09:07:30 +0800 Subject: [PATCH 27/32] fix: ftestwork validate bug --- apps/qm/serializers.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/qm/serializers.py b/apps/qm/serializers.py index e27dacf5..68da83c8 100644 --- a/apps/qm/serializers.py +++ b/apps/qm/serializers.py @@ -155,6 +155,7 @@ class FtestWorkCreateUpdateSerializer(CustomModelSerializer): def validate(self, attrs): qct = attrs.get("qct", None) + ftestworkdefect = attrs.get("ftestworkdefect", []) type2 = attrs.get('type2', 20) if type2 == 20: # 如果是全检 attrs['count_sampling'] = attrs['count'] @@ -168,7 +169,9 @@ class FtestWorkCreateUpdateSerializer(CustomModelSerializer): attrs['material'] = wm.material attrs['batch'] = wm.batch count_notok_json = attrs.get('count_notok_json', None) - if qct is None: # 没有qct时按原有逻辑处理 + if qct or ftestworkdefect: # 没有qct时按原有逻辑处理 + pass + else: if count_notok_json is None: raise ValidationError('不合格项不能为空') count_notok = 0 From aa32047bf7bd436a291fed3c6bd4284c1d0a7f0e Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 4 Mar 2025 12:33:51 +0800 Subject: [PATCH 28/32] =?UTF-8?q?fix:=20=E4=B8=80=E4=BA=9Bbug=E8=B0=83?= =?UTF-8?q?=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/mtm/serializers.py | 8 ++++---- apps/qm/serializers.py | 16 ++++++++-------- apps/qm/services.py | 3 ++- apps/wpm/serializers.py | 2 +- apps/wpm/services_2.py | 2 +- apps/wpm/views.py | 14 ++++++++++++-- 6 files changed, 28 insertions(+), 17 deletions(-) diff --git a/apps/mtm/serializers.py b/apps/mtm/serializers.py index 161bea6e..e65a4943 100644 --- a/apps/mtm/serializers.py +++ b/apps/mtm/serializers.py @@ -144,10 +144,10 @@ class RoutePackSerializer(CustomModelSerializer): fields = '__all__' read_only_fields = EXCLUDE_FIELDS + ['state', 'ticket'] - def update(self, instance, validated_data): - if validated_data['material'] != instance.material: - raise ParseError('不可变更产品') - return super().update(instance, validated_data) + # def update(self, instance, validated_data): + # if validated_data['material'] != instance.material: + # raise ParseError('不可变更产品') + # return super().update(instance, validated_data) class RouteSerializer(CustomModelSerializer): diff --git a/apps/qm/serializers.py b/apps/qm/serializers.py index 68da83c8..bb71fbfa 100644 --- a/apps/qm/serializers.py +++ b/apps/qm/serializers.py @@ -16,15 +16,15 @@ class DefectSerializer(CustomModelSerializer): fields = '__all__' read_only_fields = EXCLUDE_FIELDS - def create(self, validated_data): - code = validated_data["code"] - if Defect.objects.get_queryset(all=True).filter(code=code).exists(): - raise ValidationError("缺陷标识已存在") - return super().create(validated_data) + # def create(self, validated_data): + # code = validated_data["code"] + # if Defect.objects.get_queryset(all=True).filter(code=code).exists(): + # raise ValidationError("缺陷标识已存在") + # return super().create(validated_data) - def update(self, instance, validated_data): - validated_data.pop("code", None) - return super().update(instance, validated_data) + # def update(self, instance, validated_data): + # validated_data.pop("code", None) + # return super().update(instance, validated_data) class TestItemSerializer(CustomModelSerializer): process_name = serializers.CharField(source="process.name", read_only=True) diff --git a/apps/qm/services.py b/apps/qm/services.py index c2f19360..babdfca6 100644 --- a/apps/qm/services.py +++ b/apps/qm/services.py @@ -23,8 +23,9 @@ def ftestwork_submit_validate(ins: FtestWork): def ftestwork_submit(ins:FtestWork, user: User): wm:WMaterial = ins.wm + fwd_qs = FtestworkDefect.objects.filter(ftestwork=ins) if ins.need_update_wm: - if ins.qct is None: + if ins.qct is None or not fwd_qs.exists(): if wm.state == WMaterial.WM_TEST: # 更新对应的车间库存 wm.count = wm.count - ins.count diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index 8508f2d2..8cfd02e5 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -491,7 +491,7 @@ class MlogSerializer(CustomModelSerializer): if need_mdefect: MlogbDefect.objects.filter(mlogb__mlog=instance).delete() mlogb_defect_objects = [ - MlogbDefect(**{**item, "mlogb": mlogb, "id": idWorker.get_id()}) + MlogbDefect(**{**item, "mlogb": mox, "id": idWorker.get_id()}) for item in mlogdefect if item["count"] > 0 ] if mlogb_defect_objects: diff --git a/apps/wpm/services_2.py b/apps/wpm/services_2.py index 914a7490..5911613f 100644 --- a/apps/wpm/services_2.py +++ b/apps/wpm/services_2.py @@ -31,7 +31,7 @@ def get_alldata_with_batch_gx(batch: str): """ 光芯获取batch的统计数据 """ - pass + return None, None def get_alldata_with_batch(batch: str): """ diff --git a/apps/wpm/views.py b/apps/wpm/views.py index d36cd22f..1111acb9 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -30,7 +30,7 @@ from .services import mlog_submit, handover_submit, mlog_revert from apps.wpm.services import mlog_submit_validate, generate_new_batch from apps.wf.models import State from apps.wpmw.models import Wpr -from apps.qm.models import Qct, Ftest +from apps.qm.models import Qct, Ftest, TestItem from apps.enm.models import EnStat from django.db.models import Q from apps.utils.tools import convert_ordereddict @@ -181,10 +181,20 @@ class MlogViewSet(CustomModelViewSet): 'material_in__number', 'material_in__specification', 'batch', 'material_in__model', 'material_out__name', 'material_out__number', 'material_out__specification', 'material_out__model',] + def add_info_for_item(self, data): + if data.get("oinfo_json", {}): + czx_dict = dict(TestItem.objects.filter(id__in=data.get("oinfo_json", {}).keys()).values_list('id', 'name')) + data["oinfo_json_"] = {czx_dict.get(k, k): v for k, v in data.get("oinfo_json", {}).items()} + return data + def add_info_for_list(self, data): czx_dict = {} for item in data: czx_dict.update(item.get("oinfo_json", {})) + czx_dict = dict(TestItem.objects.filter(id__in=czx_dict.keys()).values_list('id', 'name')) + for item in data: + if item.get("oinfo_json", None): + item["oinfo_json_"] = {czx_dict.get(k, k): v for k, v in item.get("oinfo_json", {}).items()} if self.request.query_params.get('with_mlogb', False): data_dict = {item['id']: {**item, "mlogb_full": [], "mlogb": []} for item in data} mlogb_qs = Mlogb.objects.filter(mlog__id__in=data_dict.keys()).select_related("material_in", "material_out").order_by("create_time") @@ -635,7 +645,7 @@ class FmlogViewSet(CustomModelViewSet): perms_map = {'get': '*', 'post': 'mlog.create', 'put': 'mlog.update', 'delete': 'mlog.delete'} queryset = Fmlog.objects.all() serializer_class = FmlogSerializer - # update_serializer_class = FmlogUpdateSerializer + update_serializer_class = FmlogUpdateSerializer filterset_fields = ['mtask', 'mgroup', 'mtask__route'] select_related_fields = ['mtask', 'mgroup', 'mtask__route', 'mtask__route__routepack'] From df427d60b2f75e0c7454677daa342d0a46033000 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 4 Mar 2025 15:45:56 +0800 Subject: [PATCH 29/32] =?UTF-8?q?fix:=20mlog=20ftestwork=20submit=E5=85=B3?= =?UTF-8?q?=E4=BA=8E=E5=90=88=E6=A0=BCb=E7=B1=BB=E7=9A=84=E8=B0=83?= =?UTF-8?q?=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../0047_ftestwork_count_ok_full.py | 18 ++++++++++ apps/qm/models.py | 4 +++ apps/qm/services.py | 9 +++-- .../wpm/migrations/0092_auto_20250304_1433.py | 23 +++++++++++++ apps/wpm/models.py | 20 +++++++---- apps/wpm/serializers.py | 10 +++--- apps/wpm/services.py | 34 +++++++++++-------- apps/wpm/views.py | 3 ++ 8 files changed, 93 insertions(+), 28 deletions(-) create mode 100644 apps/qm/migrations/0047_ftestwork_count_ok_full.py create mode 100644 apps/wpm/migrations/0092_auto_20250304_1433.py diff --git a/apps/qm/migrations/0047_ftestwork_count_ok_full.py b/apps/qm/migrations/0047_ftestwork_count_ok_full.py new file mode 100644 index 00000000..e3ce222d --- /dev/null +++ b/apps/qm/migrations/0047_ftestwork_count_ok_full.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2025-03-04 07:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('qm', '0046_alter_testitem_description'), + ] + + operations = [ + migrations.AddField( + model_name='ftestwork', + name='count_ok_full', + field=models.PositiveIntegerField(blank=True, null=True, verbose_name='完全合格数量'), + ), + ] diff --git a/apps/qm/models.py b/apps/qm/models.py index b10422df..dc0c3458 100644 --- a/apps/qm/models.py +++ b/apps/qm/models.py @@ -235,6 +235,7 @@ class FtestWork(CommonBDModel): count_sampling = models.PositiveIntegerField('抽检数量', default=0) count_sampling_ok = models.PositiveIntegerField('抽检合格数量', default=0) count_ok = models.PositiveIntegerField('合格数量', default=0) + count_ok_full = models.PositiveIntegerField('完全合格数量', null=True, blank=True) count_notok = models.PositiveIntegerField('不合格数量', default=0) need_update_wm = models.BooleanField('是否更新车间库存', default=True) count_notok_json = models.JSONField('不合格项数量统计', default=dict, null=False, blank=True) @@ -260,7 +261,10 @@ class FtestWork(CommonBDModel): def cal_count(self): self.count_notok = FtestworkDefect.objects.filter( ftestwork=self, defect__okcate=30).aggregate(total=Sum("count"))["total"] or 0 + count_notok_full = FtestworkDefect.objects.filter( + ftestwork=self).exclude(defect__okcate=10).aggregate(total=Sum("count"))["total"] or 0 self.count_ok = self.count - self.count_notok + self.count_ok_full = self.count - count_notok_full if self.type2 == 20: #全检 self.count_sampling = self.count self.count_sampling_ok = self.count_ok diff --git a/apps/qm/services.py b/apps/qm/services.py index babdfca6..3a390340 100644 --- a/apps/qm/services.py +++ b/apps/qm/services.py @@ -102,16 +102,20 @@ def ftestwork_submit(ins:FtestWork, user: User): # 此时调用了qct表 for item in FtestworkDefect.objects.filter(ftestwork=ins): item:FtestworkDefect = item - if item.count > 0 and item.defect.okcate == Defect.DEFECT_NOTOK: + if item.count > 0: wm.count = wm.count - item.count if wm.count < 0: raise ParseError("数量不足,扣减失败") + wm.save() + wmstate = WMaterial.WM_OK + if item.defect.okcate == Defect.DEFECT_NOTOK: + wmstate = WMaterial.WM_NOTOK wmx, new_create = WMaterial.objects.get_or_create( material=wm.material, batch=wm.batch, mgroup=wm.mgroup, belong_dept=wm.belong_dept, - state=WMaterial.WM_NOTOK, + state=wmstate, notok_sign=None, defect=item.defect, defaults={ @@ -121,7 +125,6 @@ def ftestwork_submit(ins:FtestWork, user: User): if not new_create: wmx.count = wmx.count + item.count wmx.save() - wm.save() ins.submit_user = user ins.submit_time = timezone.now() ins.save() diff --git a/apps/wpm/migrations/0092_auto_20250304_1433.py b/apps/wpm/migrations/0092_auto_20250304_1433.py new file mode 100644 index 00000000..f23aea30 --- /dev/null +++ b/apps/wpm/migrations/0092_auto_20250304_1433.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.12 on 2025-03-04 06:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wpm', '0091_auto_20250303_1708'), + ] + + operations = [ + migrations.AddField( + model_name='mlog', + name='count_ok_full', + field=models.PositiveIntegerField(blank=True, null=True, verbose_name='完全合格数'), + ), + migrations.AddField( + model_name='mlogb', + name='count_ok_full', + field=models.PositiveIntegerField(blank=True, null=True, verbose_name='完全合格数'), + ), + ] diff --git a/apps/wpm/models.py b/apps/wpm/models.py index ac409394..a55a8997 100644 --- a/apps/wpm/models.py +++ b/apps/wpm/models.py @@ -207,6 +207,7 @@ class Mlog(CommonADModel): count_break = models.PositiveIntegerField('加工碎料数', default=0) count_ok = models.PositiveIntegerField('合格数', default=0) + count_ok_full = models.PositiveIntegerField('完全合格数', null=True, blank=True) count_notok = models.PositiveIntegerField('不合格数', default=0) count_break_t = models.PositiveIntegerField('检验碎料数', default=0) @@ -294,12 +295,6 @@ class Mlog(CommonADModel): def mlogdefect(self): return MlogbDefect.objects.filter(mlogb__mlog=self) - def cal_count_notok(self): - count_notok = MlogbDefect.objects.filter(defect__okcate=30, mlogb__mlog=self).aggregate(total=Sum("count"))["total"] or 0 - self.count_notok = count_notok - self.count_ok = self.count_real - count_notok - self.save(update_fields=["count_ok", "count_notok"]) - @classmethod def count_fields(cls): mlog_count_fields = [] @@ -344,6 +339,7 @@ class Mlogb(BaseModel): count_break_t = models.PositiveIntegerField('检验碎料数', default=0) count_real = models.PositiveIntegerField('实际生产数', default=0) count_ok = models.PositiveIntegerField('合格数量', default=0) + count_ok_full = models.PositiveIntegerField('完全合格数', null=True, blank=True) count_notok = models.PositiveIntegerField('不合格数', default=0) count_pn_jgqbl = models.PositiveIntegerField('加工前不良', default=0) @@ -376,9 +372,19 @@ class Mlogb(BaseModel): def cal_count_notok(self): count_notok = MlogbDefect.objects.filter(defect__okcate=30, mlogb=self).aggregate(total=Sum("count"))["total"] or 0 + count_notok_full = MlogbDefect.objects.filter(mlogb=self).exclude(defect__okcate=10).aggregate(total=Sum("count"))["total"] or 0 self.count_notok = count_notok self.count_ok = self.count_real - count_notok - self.save(update_fields=["count_ok", "count_notok"]) + self.count_ok_full = self.count_real - count_notok_full + self.save(update_fields=["count_ok", "count_notok", "count_ok_full"]) + mlog = self.mlog + if mlog: + count_notok = MlogbDefect.objects.filter(defect__okcate=30, mlogb__mlog=mlog).aggregate(total=Sum("count"))["total"] or 0 + count_notok_full = MlogbDefect.objects.filter(mlogb__mlog=mlog).exclude(defect__okcate=10).aggregate(total=Sum("count"))["total"] or 0 + mlog.count_ok_full = self.count_real - count_notok_full + mlog.count_notok = count_notok + mlog.count_ok = self.count_real - count_notok + mlog.save(update_fields=["count_ok", "count_notok", "count_ok_full"]) class MlogbDefect(BaseModel): mlogb = models.ForeignKey(Mlogb, verbose_name='生产记录', on_delete=models.CASCADE) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index 8cfd02e5..eb642089 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -394,7 +394,8 @@ class MlogSerializer(CustomModelSerializer): 'mtask': instance.mtask, 'material_out': instance.material_out, 'count_real': instance.count_real, 'count_ok': instance.count_ok, 'count_notok': instance.count_notok, - 'count_break_t': instance.count_break_t + 'count_break_t': instance.count_break_t, + 'qct': instance.qct } need_mdfect = False if instance.qct or mlogdefect: @@ -414,7 +415,7 @@ class MlogSerializer(CustomModelSerializer): ] if mlogb_defect_objects: MlogbDefect.objects.bulk_create(mlogb_defect_objects) - instance.cal_count_notok() + mlogb.cal_count_notok() return instance def update(self, instance, validated_data): @@ -450,6 +451,7 @@ class MlogSerializer(CustomModelSerializer): minx.count_use = instance.count_use minx.count_break = instance.count_break minx.count_pn_jgqbl = instance.count_pn_jgqbl + minx.qct = instance.qct minx.save() Mlogb.objects.filter(mlog=instance, material_in__isnull=False).exclude(id=minx.id).delete() @@ -496,7 +498,7 @@ class MlogSerializer(CustomModelSerializer): ] if mlogb_defect_objects: MlogbDefect.objects.bulk_create(mlogb_defect_objects) - instance.cal_count_notok() + mox.cal_count_notok() return instance def validate(self, attrs): @@ -904,7 +906,7 @@ class HandoverSerializer(CustomModelSerializer): # raise ParseError(f'第{ind+1}物料与交接部门不一致') if attrs["material"] != wm.material: raise ParseError(f'第{ind+1}物料与交接物料不一致') - if (wm.notok_sign or wm.defect) and attrs['type'] in [Handover.H_NORMAL, Handover.H_TEST]: + if wm.state != WMaterial.WM_OK and attrs['type'] in [Handover.H_NORMAL, Handover.H_TEST]: raise ParseError(f'第{ind+1}物料不合格,不能进行正常/检验交接') if wm.count_xtest is not None: raise ParseError(f'第{ind+1}物料检验中,不能进行交接') diff --git a/apps/wpm/services.py b/apps/wpm/services.py index 60a15adf..8ac20a0c 100644 --- a/apps/wpm/services.py +++ b/apps/wpm/services.py @@ -237,13 +237,12 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): stored_notok = need_store_notok stored_mgroup = need_store_notok if mlogb_out_qs.exists(): - m_outs_list = [(mo.material_out, mo.batch if mo.batch else mlog.batch, mo.count_ok, mlog.count_real_eweight, None, mo) for mo in mlogb_out_qs.all()] + m_outs_list = [(mo.material_out, mo.batch if mo.batch else mlog.batch, mo.count_ok_full if mo.count_ok_full is not None else mo.count_ok, mlog.count_real_eweight, None, mo) for mo in mlogb_out_qs.all()] if need_store_notok: for item in mlogb_out_qs: - if item.qct is not None: - if MlogbDefect.objects.filter(mlogb=item).exists(): - pass - elif item.material_out.tracking == Material.MA_TRACKING_SINGLE: + mbd_qs = MlogbDefect.objects.filter(mlogb=item) + if item.qct is not None or mbd_qs.exists(): + if item.material_out.tracking == Material.MA_TRACKING_SINGLE: Mlogbw.cal_count_notok(item) for itemx in MlogbDefect.objects.filter(mlogb=item): m_outs_list.append((item.material_out, item.batch, itemx.count, 0, itemx.defect, item)) @@ -281,9 +280,13 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): if process.type == Process.PRO_PROD: wm_state = WMaterial.WM_REPAIRED # 返修只有返修完成品 elif process.type == Process.PRO_TEST: - wm_state = WMaterial.WM_OK if notok_sign_or_defect is None else WMaterial.WM_NOTOK + wm_state = WMaterial.WM_OK if notok_sign_or_defect is None or ( + isinstance(notok_sign_or_defect, Defect) and notok_sign_or_defect.okcate in [Defect.DEFECT_OK, Defect.DEFECT_OK_B] + ) else WMaterial.WM_NOTOK else: - wm_state = WMaterial.WM_OK if notok_sign_or_defect is None else WMaterial.WM_NOTOK + wm_state = WMaterial.WM_OK if notok_sign_or_defect is None or ( + isinstance(notok_sign_or_defect, Defect) and notok_sign_or_defect.okcate in [Defect.DEFECT_OK, Defect.DEFECT_OK_B] + ) else WMaterial.WM_NOTOK lookup = {'batch': mo_batch, 'material': mo_ma, 'mgroup': None, 'notok_sign': None, 'defect': None, 'state': wm_state} if isinstance(notok_sign_or_defect, Defect): @@ -367,14 +370,13 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): mlogb_out_qs = Mlogb.objects.filter(mlog=mlog, material_out__isnull=False) if mlogb_out_qs.exists(): m_outs_list = [ - (mo.material_out, mo.batch if mo.batch else mlog.batch, mo.count_ok, mlog.count_real_eweight, None, mo) + (mo.material_out, mo.batch if mo.batch else mlog.batch, mo.count_ok_full if mo.count_ok_full is not None else mo.count_ok, mlog.count_real_eweight, None, mo) for mo in mlogb_out_qs.all()] if stored_notok: for item in mlogb_out_qs: - if item.qct is not None: - if MlogbDefect.objects.filter(mlogb=item).exists(): - pass - elif item.material_out.tracking == Material.MA_TRACKING_SINGLE: + mbd_qs = MlogbDefect.objects.filter(mlogb=item) + if item.qct is not None or mbd_qs.exists(): + if item.material_out.tracking == Material.MA_TRACKING_SINGLE: Mlogbw.cal_count_notok(item) for itemx in MlogbDefect.objects.filter(mlogb=item): m_outs_list.append((item.material_out, item.batch, itemx.count, 0, itemx.defect, item)) @@ -412,9 +414,13 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): if process.type == Process.PRO_PROD: wm_state = WMaterial.WM_REPAIRED else: # 检验工序正常生成 - wm_state = WMaterial.WM_OK if notok_sign_or_defect is None else WMaterial.WM_NOTOK + wm_state = WMaterial.WM_OK if notok_sign_or_defect is None or ( + isinstance(notok_sign_or_defect, Defect) and notok_sign_or_defect.okcate in [Defect.DEFECT_OK, Defect.DEFECT_OK_B] + ) else WMaterial.WM_NOTOK else: - wm_state = WMaterial.WM_OK if notok_sign_or_defect is None else WMaterial.WM_NOTOK + wm_state = WMaterial.WM_OK if notok_sign_or_defect is None or ( + isinstance(notok_sign_or_defect, Defect) and notok_sign_or_defect.okcate in [Defect.DEFECT_OK, Defect.DEFECT_OK_B] + ) else WMaterial.WM_NOTOK lookup = {'batch': mo_batch, 'material': mo_ma, 'mgroup': None, 'notok_sign': None, 'defect': None, 'state': wm_state} if isinstance(notok_sign_or_defect, Defect): lookup['defect'] = notok_sign_or_defect diff --git a/apps/wpm/views.py b/apps/wpm/views.py index 1111acb9..de8e883f 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -610,6 +610,9 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust pass elif mlogbout.qct is None: mlogbout.qct = Qct.get(mlogbout.material_out, "process") if mlog.qct is None else mlog.qct + if mlogbout.qct is not None and mlog.qct is None: + mlog.qct = mlogbout.qct + mlog.save(update_fields=["qct"]) mlogbout.count_real = d_count_real mlogbout.count_ok = d_count_ok mlogbout.save() From a8ce6c39891cfcc3b5f7bf00c2aa20263dc69363 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 4 Mar 2025 16:26:35 +0800 Subject: [PATCH 30/32] =?UTF-8?q?feat:=20base=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E8=BA=AB=E4=BB=BD=E8=AF=81=E6=A0=A1=E9=AA=8C=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/utils/tools.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/apps/utils/tools.py b/apps/utils/tools.py index b23e977f..c7c58384 100755 --- a/apps/utils/tools.py +++ b/apps/utils/tools.py @@ -250,6 +250,23 @@ def check_id_number(idcard): return False, Errors[0] +def check_id_number_strict(id_card: str): + """校验 18 位中国身份证号码是否合法""" + if not re.match(r'^\d{17}[\dXx]$', id_card): + return False + + # 系数和校验码 + weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2] + check_codes = "10X98765432" + + # 计算校验码 + total = sum(int(id_card[i]) * weights[i] for i in range(17)) + expected_check_code = check_codes[total % 11] + + if expected_check_code == id_card[-1].upper(): + return id_card + raise ValidationError('身份证号校验失败') + def check_phone_e(phone): re_phone = r'^1\d{10}$' if not re.match(re_phone, phone): From 47976de657c94081ae9f8475ef98b0b7b309d457 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 4 Mar 2025 16:27:00 +0800 Subject: [PATCH 31/32] =?UTF-8?q?feat:=20=E4=BD=BF=E7=94=A8=E8=BA=AB?= =?UTF-8?q?=E4=BB=BD=E8=AF=81=E5=BC=BA=E6=A0=A1=E9=AA=8C=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/hrm/serializers.py | 4 ++-- apps/rpm/serializers.py | 4 ++-- apps/vm/serializers.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/hrm/serializers.py b/apps/hrm/serializers.py index 544a96a0..d76994b5 100755 --- a/apps/hrm/serializers.py +++ b/apps/hrm/serializers.py @@ -12,7 +12,7 @@ from apps.hrm.models import Certificate, ClockRecord, Employee, NotWorkRemark, A from apps.system.serializers import DeptSimpleSerializer, UserSimpleSerializer from django.db import transaction from django.core.cache import cache -from apps.utils.tools import check_id_number_e, get_info_from_id +from apps.utils.tools import check_id_number_strict, get_info_from_id from rest_framework.exceptions import ParseError from django.conf import settings import datetime @@ -54,7 +54,7 @@ class EmployeeSimpleSerializer(CustomModelSerializer): class EmployeeCreateUpdateSerializer(CustomModelSerializer): id_number = serializers.CharField( - label="身份证号", validators=[check_id_number_e]) + label="身份证号", validators=[check_id_number_strict]) class Meta: model = Employee diff --git a/apps/rpm/serializers.py b/apps/rpm/serializers.py index d1e09878..7bc92f40 100644 --- a/apps/rpm/serializers.py +++ b/apps/rpm/serializers.py @@ -12,7 +12,7 @@ from rest_framework.exceptions import ParseError, ValidationError from django.db import transaction from apps.third.dahua import dhClient from apps.third.tapis import dhapis -from apps.utils.tools import check_id_number_e, check_phone_e +from apps.utils.tools import check_id_number_strict, check_phone_e from apps.wf.serializers import TicketSimpleSerializer from apps.rpm.services import rpj_member_come, rpj_certificate_in from apps.rpm.models import RpjLog @@ -138,7 +138,7 @@ class RemployeeCreateSerializer(CustomModelSerializer): rparty = serializers.PrimaryKeyRelatedField(queryset=Rparty.objects.all(), label='相关方ID', required=False) phone = serializers.CharField(label="手机号", validators=[check_phone_e]) photo = serializers.CharField(label='照片地址', required=True) - id_number = serializers.CharField(label="身份证号", validators=[check_id_number_e], required=True) + id_number = serializers.CharField(label="身份证号", validators=[check_id_number_strict], required=True) class Meta: model = Remployee diff --git a/apps/vm/serializers.py b/apps/vm/serializers.py index 3c65f900..dd5ff87c 100644 --- a/apps/vm/serializers.py +++ b/apps/vm/serializers.py @@ -4,7 +4,7 @@ from apps.hrm.serializers import EmployeeSimpleSerializer from apps.system.models import Dept from apps.utils.fields import MyFilePathField from apps.utils.serializers import CustomModelSerializer -from apps.utils.tools import check_id_number_e, check_phone_e +from apps.utils.tools import check_id_number_strict, check_phone_e from apps.vm.models import Visit, Visitor, Vpeople from rest_framework import serializers from rest_framework.exceptions import ParseError @@ -59,7 +59,7 @@ class VisitorCreateSerializer(CustomModelSerializer): phone = serializers.CharField(label="手机号", validators=[check_phone_e]) photo = serializers.CharField(label='照片地址', required=True) id_number = serializers.CharField( - label="身份证号", validators=[check_id_number_e], required=True) + label="身份证号", validators=[check_id_number_strict], required=True) class Meta: model = Visitor From 62d2c8b21e870f09f6fdf497d6cc76a05764b2c9 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 4 Mar 2025 18:08:52 +0800 Subject: [PATCH 32/32] feat: inm batch textfield --- .../inm/migrations/0029_alter_mioitem_batch.py | 18 ++++++++++++++++++ apps/inm/models.py | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 apps/inm/migrations/0029_alter_mioitem_batch.py diff --git a/apps/inm/migrations/0029_alter_mioitem_batch.py b/apps/inm/migrations/0029_alter_mioitem_batch.py new file mode 100644 index 00000000..1d9cb796 --- /dev/null +++ b/apps/inm/migrations/0029_alter_mioitem_batch.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2025-03-04 08:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('inm', '0028_mioitem_count_tested'), + ] + + operations = [ + migrations.AlterField( + model_name='mioitem', + name='batch', + field=models.TextField(verbose_name='批次号'), + ), + ] diff --git a/apps/inm/models.py b/apps/inm/models.py index 2775c183..78032f08 100644 --- a/apps/inm/models.py +++ b/apps/inm/models.py @@ -116,7 +116,7 @@ class MIOItem(BaseModel): WareHouse, on_delete=models.CASCADE, verbose_name='仓库') material = models.ForeignKey( Material, verbose_name='物料', on_delete=models.CASCADE) - batch = models.CharField('批次号', max_length=50) + batch = models.TextField('批次号') count = models.DecimalField('出入数量', max_digits=12, decimal_places=3) count_tested = models.PositiveIntegerField('已检数', null=True, blank=True) test_date = models.DateField('检验日期', null=True, blank=True)