Merge branch 'master' of https://e.coding.net/ctcdevteam/ehs/ehs_server
This commit is contained in:
commit
c8ba7dc563
|
@ -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
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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='批次号'),
|
||||
),
|
||||
]
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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='标识'),
|
||||
),
|
||||
]
|
|
@ -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='描述'),
|
||||
),
|
||||
]
|
|
@ -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='完全合格数量'),
|
||||
),
|
||||
]
|
|
@ -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, "不合格")),
|
||||
|
@ -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)
|
||||
|
@ -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')
|
||||
|
@ -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)
|
||||
|
@ -225,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)
|
||||
|
@ -250,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
|
||||
|
@ -306,6 +320,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):
|
||||
"""
|
||||
|
|
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -200,23 +203,30 @@ class FtestWorkCreateUpdateSerializer(CustomModelSerializer):
|
|||
with transaction.atomic():
|
||||
ins: FtestWork = super().create(validated_data)
|
||||
for ftestworkdefect in ftestworkdefect:
|
||||
FtestworkDefect.objects.create(ftestwork=ins, **ftestworkdefect)
|
||||
if ftestworkdefect:
|
||||
if ftestworkdefect['count'] > 0:
|
||||
FtestworkDefect.objects.create(ftestwork=ins, **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():
|
||||
fd_ids = []
|
||||
for item in ftestworkdefect:
|
||||
try:
|
||||
ins = FtestworkDefect.objects.get(ftestwork=ins, defect=item["defect"])
|
||||
except FtestworkDefect.DoesNotExist:
|
||||
raise ParseError("新的缺陷项!")
|
||||
ins.count = item["count"]
|
||||
ins.save()
|
||||
if ftestworkdefect:
|
||||
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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -101,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={
|
||||
|
@ -120,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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
),
|
||||
]
|
|
@ -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='层数'),
|
||||
),
|
||||
]
|
|
@ -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='抽检数'),
|
||||
),
|
||||
]
|
|
@ -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='完全合格数'),
|
||||
),
|
||||
]
|
|
@ -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.
|
||||
|
||||
|
@ -171,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)
|
||||
|
@ -205,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)
|
||||
|
||||
|
@ -287,7 +290,11 @@ 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)
|
||||
|
||||
@classmethod
|
||||
def count_fields(cls):
|
||||
mlog_count_fields = []
|
||||
|
@ -332,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)
|
||||
|
@ -357,6 +365,33 @@ 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)
|
||||
|
||||
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.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)
|
||||
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)
|
||||
|
||||
class Mlogbw(BaseModel):
|
||||
"""TN: 单个产品生产/检验日志
|
||||
|
@ -372,6 +407,29 @@ 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(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)
|
||||
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 Handover(CommonADModel):
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
|
@ -18,11 +18,13 @@ 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
|
||||
import logging
|
||||
from apps.qm.models import Defect
|
||||
from apps.utils.snowflake import idWorker
|
||||
mylogger = logging.getLogger("log")
|
||||
|
||||
class OtherLogSerializer(CustomModelSerializer):
|
||||
|
@ -201,6 +203,13 @@ 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)
|
||||
defect_okcate = serializers.CharField(source="defect.okcate", read_only=True)
|
||||
class Meta:
|
||||
model = MlogbDefect
|
||||
fields = ["id", "defect_name", "count", "mlogb", "defect", "floor", "count_test", "defect_okcate"]
|
||||
read_only_fields = EXCLUDE_FIELDS_BASE + ["mlogb"]
|
||||
|
||||
class MlogbSerializer(CustomModelSerializer):
|
||||
material_out_ = MaterialSimpleSerializer(
|
||||
|
@ -215,15 +224,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)
|
||||
|
@ -269,6 +269,18 @@ class MlogListSerializer(CustomModelSerializer):
|
|||
model = Mlog
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
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)
|
||||
|
@ -313,6 +325,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__'
|
||||
|
@ -327,6 +340,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():
|
||||
|
@ -380,15 +394,28 @@ 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
|
||||
}
|
||||
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, "id": idWorker.get_id()})
|
||||
for item in mlogdefect if item["count"] > 0
|
||||
]
|
||||
if mlogb_defect_objects:
|
||||
MlogbDefect.objects.bulk_create(mlogb_defect_objects)
|
||||
mlogb.cal_count_notok()
|
||||
return instance
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
|
@ -396,6 +423,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)
|
||||
|
@ -423,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()
|
||||
|
||||
|
@ -452,11 +481,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": mox, "id": idWorker.get_id()})
|
||||
for item in mlogdefect if item["count"] > 0
|
||||
]
|
||||
if mlogb_defect_objects:
|
||||
MlogbDefect.objects.bulk_create(mlogb_defect_objects)
|
||||
mox.cal_count_notok()
|
||||
return instance
|
||||
|
||||
def validate(self, attrs):
|
||||
|
@ -565,6 +607,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):
|
||||
|
@ -688,45 +732,89 @@ class MlogbwCreateUpdateSerializer(CustomModelSerializer):
|
|||
return mlogbw
|
||||
|
||||
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', "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
|
||||
@transaction.atomic
|
||||
def update(self, instance, validated_data):
|
||||
mlogbdefect = validated_data.pop("mlogbdefect", [])
|
||||
with transaction.atomic():
|
||||
ins:Mlogb = super().update(instance, validated_data)
|
||||
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, "id": idWorker.get_id()})
|
||||
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):
|
||||
count_notok_json = attrs.get('count_notok_json', [])
|
||||
# count_notok_json字段处理
|
||||
if count_notok_json:
|
||||
# 先置0字段
|
||||
mlogbdefect = attrs.get("mlogbdefect", [])
|
||||
if mlogbdefect:
|
||||
attrs.pop("count_notok_json", None)
|
||||
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 attrs['count_real'] >= attrs['count_ok'] + attrs['count_notok']:
|
||||
pass
|
||||
attrs.pop(i, None)
|
||||
else:
|
||||
raise ParseError('生产数量不能小于合格数量')
|
||||
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:
|
||||
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:
|
||||
raise ParseError('生产数量不能小于合格数量')
|
||||
return attrs
|
||||
|
||||
class MlogRevertSerializer(serializers.Serializer):
|
||||
|
@ -818,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}物料检验中,不能进行交接')
|
||||
|
|
|
@ -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
|
||||
|
@ -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)]
|
||||
|
@ -232,29 +233,34 @@ 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_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 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:
|
||||
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))
|
||||
# # 获取所有主要的不合格项/先暂时保留
|
||||
# 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:
|
||||
|
@ -274,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):
|
||||
|
@ -357,29 +367,35 @@ 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()]
|
||||
(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 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:
|
||||
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))
|
||||
# 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:
|
||||
|
@ -398,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
|
||||
|
@ -446,6 +466,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)]
|
||||
|
|
|
@ -31,7 +31,7 @@ def get_alldata_with_batch_gx(batch: str):
|
|||
"""
|
||||
光芯获取batch的统计数据
|
||||
"""
|
||||
pass
|
||||
return None, None
|
||||
|
||||
def get_alldata_with_batch(batch: str):
|
||||
"""
|
||||
|
@ -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="管料成型")
|
||||
|
@ -126,7 +129,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["七车间入库_仓库执行人"]])
|
||||
|
||||
# 十车间入库检验
|
||||
|
|
|
@ -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,
|
||||
|
@ -30,10 +30,11 @@ 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, TestItem
|
||||
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.
|
||||
|
@ -180,7 +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")
|
||||
|
@ -191,7 +205,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
|
||||
|
@ -591,13 +605,14 @@ 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 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()
|
||||
|
@ -671,23 +686,12 @@ 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
|
||||
mlogb.count_notok = Mlogbw.objects.filter(mlogb=mlogb, ftest__is_ok=False).count()
|
||||
mlogb.count_ok = count - mlogb.count_notok
|
||||
mlogb.save()
|
||||
|
||||
@transaction.atomic
|
||||
def perform_create(self, serializer):
|
||||
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:
|
||||
|
@ -704,7 +708,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):
|
||||
|
@ -725,4 +729,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)
|
||||
|
|
Loading…
Reference in New Issue