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']