This commit is contained in:
zty 2025-09-05 11:10:16 +08:00
commit ca18fece21
19 changed files with 190 additions and 63 deletions

View File

@ -153,7 +153,9 @@ class MIOItemCreateSerializer(CustomModelSerializer):
validated_data["batch"] = wm.batch validated_data["batch"] = wm.batch
material: Material = validated_data['material'] material: Material = validated_data['material']
batch = validated_data['batch'] batch = validated_data.get("batch", None)
if not batch:
batch = ""
if material.is_hidden: if material.is_hidden:
raise ParseError('隐式物料不可出入库') raise ParseError('隐式物料不可出入库')
if mio.type in [MIO.MIO_TYPE_RETURN_IN, MIO.MIO_TYPE_BORROW_OUT]: if mio.type in [MIO.MIO_TYPE_RETURN_IN, MIO.MIO_TYPE_BORROW_OUT]:

View File

@ -398,7 +398,7 @@ class InmService:
if change_count < 0: if change_count < 0:
raise ParseError("存在负数!") raise ParseError("存在负数!")
state = WMaterial.WM_OK state = WMaterial.WM_OK
if defect: if defect and defect.okcate in [Defect.DEFECT_NOTOK]:
state = WMaterial.WM_NOTOK state = WMaterial.WM_NOTOK
mb, _ = MaterialBatch.objects.get_or_create( mb, _ = MaterialBatch.objects.get_or_create(
material=material, material=material,

View File

@ -138,13 +138,17 @@ def daoru_mioitems(path:str, mio:MIO):
mioitems = [] mioitems = []
ind = 2 ind = 2
while sheet[f"b{ind}"].value: while sheet[f"a{ind}"].value:
batch = sheet[f"b{ind}"].value batch = sheet[f"b{ind}"].value
material_number = sheet[f"a{ind}"].value material_number = sheet[f"a{ind}"].value
try: try:
material = Material.objects.get(number=material_number) material = Material.objects.get(number=material_number)
except Exception as e: except Exception as e:
raise ParseError(f"未找到物料:{material_number} {e}") raise ParseError(f"未找到物料:{material_number} {e}")
if batch:
pass
else:
batch = ""
count = sheet[f"c{ind}"].value count = sheet[f"c{ind}"].value
warehouse_name = sheet[f"d{ind}"].value warehouse_name = sheet[f"d{ind}"].value
try: try:

View File

@ -206,12 +206,13 @@ class MIOViewSet(CustomModelViewSet):
if ins.state != MIO.MIO_CREATE: if ins.state != MIO.MIO_CREATE:
raise ParseError('记录状态异常') raise ParseError('记录状态异常')
with transaction.atomic(): with transaction.atomic():
ins.submit_time = timezone.now() now = timezone.now()
ins.state = MIO.MIO_SUBMITED updated_count = MIO.objects.filter(id=ins.id, submit_time__isnull=True).update(
ins.submit_user = request.user submit_time=now, update_time=now, state=MIO.MIO_SUBMITED, submit_user=request.user, update_by=request.user)
ins.update_by = request.user if updated_count == 1:
ins.save() InmService.update_inm(ins)
InmService.update_inm(ins) else:
raise ParseError('记录正在处理中,请稍后再试')
InmService.update_material_count(ins) InmService.update_material_count(ins)
return Response(MIOListSerializer(instance=ins).data) return Response(MIOListSerializer(instance=ins).data)
@ -228,11 +229,12 @@ class MIOViewSet(CustomModelViewSet):
if ins.submit_user != user: if ins.submit_user != user:
raise ParseError('非提交人不可撤回') raise ParseError('非提交人不可撤回')
with transaction.atomic(): with transaction.atomic():
ins.submit_time = None updated_count = MIO.objects.filter(id=ins.id, submit_time__isnull=False).update(
ins.state = MIO.MIO_CREATE submit_time=None, update_time=timezone.now(), state=MIO.MIO_CREATE, submit_user=None, update_by=request.user)
ins.update_by = user if updated_count == 1:
ins.save() InmService.update_inm(ins, is_reverse=True)
InmService.update_inm(ins, is_reverse=True) else:
raise ParseError('记录正在处理中,请稍后再试')
InmService.update_material_count(ins) InmService.update_material_count(ins)
return Response() return Response()

View File

@ -58,5 +58,6 @@ class RouteFilter(filters.FilterSet):
"mgroup": ["exact", "in", "isnull"], "mgroup": ["exact", "in", "isnull"],
"mgroup__name": ["exact", "contains"], "mgroup__name": ["exact", "contains"],
"mgroup__belong_dept": ["exact"], "mgroup__belong_dept": ["exact"],
"mgroup__belong_dept__name": ["exact", "contains"] "mgroup__belong_dept__name": ["exact", "contains"],
"from_route": ["exact", "isnull"],
} }

View File

@ -0,0 +1,24 @@
# Generated by Django 3.2.12 on 2025-09-02 03:22
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('mtm', '0061_material_img'),
]
operations = [
migrations.AddField(
model_name='route',
name='from_route',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='route_f', to='mtm.route', verbose_name='来源路线'),
),
migrations.AlterField(
model_name='route',
name='parent',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='route_parent', to='mtm.route', verbose_name='上级路线'),
),
]

View File

@ -415,8 +415,9 @@ class Route(CommonADModel):
batch_bind = models.BooleanField('是否绑定批次', default=True) batch_bind = models.BooleanField('是否绑定批次', default=True)
materials = models.ManyToManyField(Material, verbose_name='关联辅助物料', related_name="route_materials", materials = models.ManyToManyField(Material, verbose_name='关联辅助物料', related_name="route_materials",
through="mtm.routemat", blank=True) through="mtm.routemat", blank=True)
parent = models.ForeignKey('self', verbose_name='上级路线', on_delete=models.CASCADE, null=True, blank=True) parent = models.ForeignKey('self', verbose_name='上级路线', on_delete=models.CASCADE, null=True, blank=True, related_name="route_parent")
params_json = models.JSONField('工艺参数', default=dict, blank=True) params_json = models.JSONField('工艺参数', default=dict, blank=True)
from_route = models.ForeignKey('self', verbose_name='来源路线', on_delete=models.SET_NULL, null=True, blank=True, related_name="route_f")
def __str__(self): def __str__(self):
x = "" x = ""

View File

@ -247,7 +247,7 @@ class RouteSerializer(CustomModelSerializer):
# if material and process and Route.objects.filter(material=material, process=process).exists(): # if material and process and Route.objects.filter(material=material, process=process).exists():
# raise ValidationError('已选择该工序!!') # raise ValidationError('已选择该工序!!')
with transaction.atomic(): with transaction.atomic():
instance = super().create(validated_data) instance:Route = super().create(validated_data)
material_out = instance.material_out material_out = instance.material_out
if material_out: if material_out:
if material_out.process is None: if material_out.process is None:
@ -263,12 +263,16 @@ class RouteSerializer(CustomModelSerializer):
if instance.material: if instance.material:
instance.material_out = RouteSerializer.gen_material_out(instance, material_out_tracking, user=self.request.user) instance.material_out = RouteSerializer.gen_material_out(instance, material_out_tracking, user=self.request.user)
instance.save() instance.save()
rx = Route.objects.filter(material_in=instance.material_in, material_out=instance.material_out, process=process).exclude(id=instance.id).first() rx = Route.objects.filter(
material_in=instance.material_in, material_out=instance.material_out,
process=process).exclude(id=instance.id).order_by("create_time").first()
if rx: if rx:
msg = "" instance.from_route = rx
if rx.routepack: instance.save()
msg = rx.routepack.name # msg = ""
raise ParseError(f"该工艺步骤已存在-{msg}") # if rx.routepack:
# msg = rx.routepack.name
# raise ParseError(f"该工艺步骤已存在-{msg}")
return instance return instance
def update(self, instance, validated_data): def update(self, instance, validated_data):
@ -294,12 +298,16 @@ class RouteSerializer(CustomModelSerializer):
if instance.material: if instance.material:
instance.material_out = RouteSerializer.gen_material_out(instance, material_out_tracking, user=self.request.user) instance.material_out = RouteSerializer.gen_material_out(instance, material_out_tracking, user=self.request.user)
instance.save() instance.save()
rx = Route.objects.filter(material_in=instance.material_in, material_out=instance.material_out, process=process).exclude(id=instance.id).first() rx = Route.objects.filter(
material_in=instance.material_in, material_out=instance.material_out,
process=process).exclude(id=instance.id).order_by("create_time").first()
if rx: if rx:
msg = "" instance.from_route = rx
if rx.routepack: instance.save()
msg = rx.routepack.name # msg = ""
raise ParseError(f"该工艺步骤已存在-{msg}") # if rx.routepack:
# msg = rx.routepack.name
# raise ParseError(f"该工艺步骤已存在-{msg}")
return instance return instance
def to_representation(self, instance): def to_representation(self, instance):
@ -331,6 +339,12 @@ class RouteMatSerializer(CustomModelSerializer):
fields = "__all__" fields = "__all__"
read_only_fields = EXCLUDE_FIELDS_BASE read_only_fields = EXCLUDE_FIELDS_BASE
def validate(self, attrs):
route:Route = attrs["route"]
if route.from_route is not None:
raise ParseError("该工艺步骤引用其他步骤,无法修改")
return attrs
class MaterialExportSerializer(CustomModelSerializer): class MaterialExportSerializer(CustomModelSerializer):
class Meta: class Meta:

View File

@ -155,12 +155,12 @@ def bind_routepack(ticket: Ticket, transition, new_ticket_data: dict):
raise ParseError('缺少步骤') raise ParseError('缺少步骤')
r_qs = Route.objects.filter(routepack=routepack).order_by('sort', 'process__sort', 'create_time') r_qs = Route.objects.filter(routepack=routepack).order_by('sort', 'process__sort', 'create_time')
first_route = r_qs.first() first_route = r_qs.first()
last_route = r_qs.last()
if first_route.batch_bind: if first_route.batch_bind:
first_route.batch_bind = False first_route.batch_bind = False
first_route.save(update_fields=['batch_bind']) first_route.save(update_fields=['batch_bind'])
if last_route.material_out != routepack.material: # last_route = r_qs.last()
raise ParseError('最后一步产出与工艺包不一致') # if last_route.material_out != routepack.material:
# raise ParseError('最后一步产出与工艺包不一致')
ticket_data = ticket.ticket_data ticket_data = ticket.ticket_data
ticket_data.update({ ticket_data.update({
't_model': 'routepack', 't_model': 'routepack',

View File

@ -368,12 +368,23 @@ class RouteViewSet(CustomModelViewSet):
select_related_fields = ['material', select_related_fields = ['material',
'process', 'material_in', 'material_out', 'mgroup', 'routepack'] 'process', 'material_in', 'material_out', 'mgroup', 'routepack']
def update(self, request, *args, **kwargs): @transaction.atomic
obj:Route = self.get_object() def perform_update(self, serializer):
routepack = obj.routepack ins:Route = self.get_object()
if ins.from_route is not None:
raise ParseError('该工艺步骤引用其他步骤, 无法编辑')
old_m_in, old_m_out, process = ins.material_in, ins.material_out, ins.process
routepack = ins.routepack
if routepack and routepack.state != RoutePack.RP_S_CREATE: if routepack and routepack.state != RoutePack.RP_S_CREATE:
raise ParseError('该状态下不可编辑') raise ParseError('该工艺路线非创建中不可编辑')
return super().update(request, *args, **kwargs) ins_n:Route = serializer.save()
if Route.objects.filter(from_route__id=ins.id).exists() and (ins_n.material_in != old_m_in or ins_n.material_out != old_m_out or ins_n.process != process):
raise ParseError("该工艺步骤被其他步骤引用, 无法修改关键信息")
def perform_destroy(self, instance:Route):
if Route.objects.filter(from_route=instance).exists():
raise ParseError('该工艺步骤被其他步骤引用,无法删除')
return super().perform_destroy(instance)
class SruleViewSet(CustomModelViewSet): class SruleViewSet(CustomModelViewSet):

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2025-09-05 01:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('qm', '0052_auto_20250718_1558'),
]
operations = [
migrations.AlterField(
model_name='ftest',
name='is_ok',
field=models.BooleanField(default=True, verbose_name='是否合格'),
),
]

View File

@ -337,7 +337,7 @@ class Ftest(CommonBDModel):
User, verbose_name='操作人', on_delete=models.CASCADE, related_name='ftest_test_user') User, verbose_name='操作人', on_delete=models.CASCADE, related_name='ftest_test_user')
check_user = models.ForeignKey( check_user = models.ForeignKey(
User, verbose_name='专检人', on_delete=models.CASCADE, related_name='ftest_check_user', null=True, blank=True) User, verbose_name='专检人', on_delete=models.CASCADE, related_name='ftest_check_user', null=True, blank=True)
is_ok = models.BooleanField('是否合格', default=False) is_ok = models.BooleanField('是否合格', default=True)
note = models.TextField('备注', default='', blank=True) note = models.TextField('备注', default='', blank=True)
ftest_work = models.ForeignKey( ftest_work = models.ForeignKey(
FtestWork, verbose_name='关联检验工作', on_delete=models.CASCADE, null=True, blank=True) FtestWork, verbose_name='关联检验工作', on_delete=models.CASCADE, null=True, blank=True)

View File

@ -322,7 +322,7 @@ class FtestWorkViewSet(CustomModelViewSet):
ins:FtestWork = self.get_object() ins:FtestWork = self.get_object()
if ins.ticket: if ins.ticket:
raise ParseError('该检验工作存在审批!') raise ParseError('该检验工作存在审批!')
if ins.wm is None or ins.mb is None: if ins.wm is None and ins.mb is None:
raise ParseError('该检验工作未关联库存') raise ParseError('该检验工作未关联库存')
if ins.submit_time is None: if ins.submit_time is None:
ftestwork_submit(ins, request.user) ftestwork_submit(ins, request.user)

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2025-09-04 08:47
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wpm', '0122_auto_20250827_1522'),
]
operations = [
migrations.AddField(
model_name='mlogbdefect',
name='count_has',
field=models.DecimalField(decimal_places=1, default=0, max_digits=11, verbose_name='含有该缺陷的数量'),
),
]

View File

@ -477,6 +477,7 @@ class MlogbDefect(BaseModel):
mlogb = models.ForeignKey(Mlogb, verbose_name='生产记录', on_delete=models.CASCADE) mlogb = models.ForeignKey(Mlogb, verbose_name='生产记录', on_delete=models.CASCADE)
defect = models.ForeignKey("qm.Defect", verbose_name='缺陷', on_delete=models.CASCADE, null=True, blank=True) defect = models.ForeignKey("qm.Defect", verbose_name='缺陷', on_delete=models.CASCADE, null=True, blank=True)
count = models.DecimalField('数量', default=0, max_digits=11, decimal_places=1) count = models.DecimalField('数量', default=0, max_digits=11, decimal_places=1)
count_has = models.DecimalField('含有该缺陷的数量', default=0, max_digits=11, decimal_places=1)
@classmethod @classmethod
def get_defect_qs(cls, ftype="all"): def get_defect_qs(cls, ftype="all"):
@ -512,7 +513,7 @@ class Mlogbw(BaseModel):
@classmethod @classmethod
def cal_count_notok(cls, mlogb: Mlogb): def cal_count_notok(cls, mlogb: Mlogb):
from apps.qm.models import Defect from apps.qm.models import Defect, FtestDefect
# 锁定mlogb以防止并发修改 # 锁定mlogb以防止并发修改
# mlogb:Mlogb = Mlogb.objects.select_for_update().get(pk=mlogb.pk) # mlogb:Mlogb = Mlogb.objects.select_for_update().get(pk=mlogb.pk)
count = Mlogbw.objects.filter(mlogb=mlogb).count() count = Mlogbw.objects.filter(mlogb=mlogb).count()
@ -523,9 +524,13 @@ class Mlogbw(BaseModel):
mlogb.count_real = count mlogb.count_real = count
count_notok = 0 count_notok = 0
count_notok_full = 0 count_notok_full = 0
tqs = Mlogbw.objects.filter(mlogb=mlogb, ftest__is_ok=False) # 个追踪的合格b类不分批
tqs_a = Mlogbw.objects.filter(mlogb=mlogb, ftest__is_ok=False).values("ftest__defect_main").annotate(xcount=Count('id')) tqs = Mlogbw.objects.filter(mlogb=mlogb, ftest__defect_main__isnull=False)
defects = {defect.id: defect for defect in Defect.objects.filter(id__in=tqs.values_list("ftest__defect_main", flat=True))} tqs_a = Mlogbw.objects.filter(mlogb=mlogb, ftest__defect_main__isnull=False).values("ftest__defect_main").annotate(xcount=Count('id'))
defect_ids = tqs.values_list("ftest__defect_main", flat=True)
tqs_has_a = FtestDefect.objects.filter(ftest__mlogbw_ftest__mlogb=mlogb, has=True).values("defect").annotate(xcount=Count('id'))
defect_ids = defect_ids.union(tqs_has_a.values_list("defect", flat=True))
defects = {defect.id: defect for defect in Defect.objects.filter(id__in=defect_ids)}
md_ids = [] md_ids = []
for t in tqs_a: for t in tqs_a:
md, _ = MlogbDefect.objects.get_or_create(mlogb=mlogb, defect=defects[t["ftest__defect_main"]]) md, _ = MlogbDefect.objects.get_or_create(mlogb=mlogb, defect=defects[t["ftest__defect_main"]])
@ -535,6 +540,13 @@ class Mlogbw(BaseModel):
count_notok += t["xcount"] count_notok += t["xcount"]
if defects[t["ftest__defect_main"]].okcate != 10: if defects[t["ftest__defect_main"]].okcate != 10:
count_notok_full += t["xcount"] count_notok_full += t["xcount"]
for t2 in tqs_has_a:
md, _ = MlogbDefect.objects.get_or_create(mlogb=mlogb, defect=defects[t2["defect"]], defaults={"count": 0})
md.count_has = t2["xcount"]
md.save()
md_ids.append(md.id)
MlogbDefect.objects.filter(mlogb=mlogb).exclude(id__in=md_ids).delete() MlogbDefect.objects.filter(mlogb=mlogb).exclude(id__in=md_ids).delete()
mlogb.count_notok = count_notok mlogb.count_notok = count_notok
mlogb.count_ok = count - mlogb.count_notok mlogb.count_ok = count - mlogb.count_notok

View File

@ -1,6 +1,6 @@
from apps.wpm.models import BatchSt from apps.wpm.models import BatchSt
import logging import logging
from apps.wpm.models import Mlogb, MlogbDefect from apps.wpm.models import Mlogb, Mlogbw, MlogbDefect
from apps.mtm.models import Mgroup from apps.mtm.models import Mgroup
import decimal import decimal
from django.db.models import Sum from django.db.models import Sum
@ -41,10 +41,14 @@ def main(batch: str, mgroup_obj:Mgroup=None):
for item in mlogb1_qs: for item in mlogb1_qs:
# 找到对应的输入 # 找到对应的输入
mlogb_from:Mlogb = item.mlogb_from mlogb_from:Mlogb = item.mlogb_from
mlogbw_from:Mlogbw = item.mlogbw_from
if mlogb_from: if mlogb_from:
mlogb_q_ids.append(mlogb_from.id) mlogb_q_ids.append(mlogb_from.id)
data[f"{mgroup_name}_count_use"] += mlogb_from.count_use data[f"{mgroup_name}_count_use"] += mlogb_from.count_use
data[f"{mgroup_name}_count_pn_jgqbl"] += mlogb_from.count_pn_jgqbl data[f"{mgroup_name}_count_pn_jgqbl"] += mlogb_from.count_pn_jgqbl
if mlogbw_from:
data[f"{mgroup_name}_count_use"] += 1
data[f"{mgroup_name}_count_pn_jgqbl"] += 0
if item.mlog.handle_user: if item.mlog.handle_user:
data[f"{mgroup_name}_操作人"].append(item.mlog.handle_user) data[f"{mgroup_name}_操作人"].append(item.mlog.handle_user)
if item.mlog.handle_date: if item.mlog.handle_date:

View File

@ -29,6 +29,7 @@ def main(batch: str, mgroup_obj):
if mlogb1_qs.exists(): if mlogb1_qs.exists():
data[f"{mgroup_name}_日期"] = [] data[f"{mgroup_name}_日期"] = []
data[f"{mgroup_name}_操作人"] = [] data[f"{mgroup_name}_操作人"] = []
data[f"{mgroup_name}_班次"] = []
data[f"{mgroup_name}_count_use"] = 0 data[f"{mgroup_name}_count_use"] = 0
data[f"{mgroup_name}_count_real"] = 0 data[f"{mgroup_name}_count_real"] = 0
data[f"{mgroup_name}_count_ok"] = 0 data[f"{mgroup_name}_count_ok"] = 0
@ -47,6 +48,8 @@ def main(batch: str, mgroup_obj):
data[f"{mgroup_name}_操作人"].append(item.mlog.handle_user) data[f"{mgroup_name}_操作人"].append(item.mlog.handle_user)
if item.mlog.handle_date: if item.mlog.handle_date:
data[f"{mgroup_name}_日期"].append(item.mlog.handle_date) data[f"{mgroup_name}_日期"].append(item.mlog.handle_date)
if item.mlog.shift:
data[f"{mgroup_name}_班次"].append(item.mlog.shift.name)
data[f"{mgroup_name}_count_real"] += item.count_real data[f"{mgroup_name}_count_real"] += item.count_real
data[f"{mgroup_name}_count_ok"] += item.count_ok data[f"{mgroup_name}_count_ok"] += item.count_ok
data[f"{mgroup_name}_count_ok_full"] += item.count_ok_full if item.count_ok_full else 0 data[f"{mgroup_name}_count_ok_full"] += item.count_ok_full if item.count_ok_full else 0
@ -80,6 +83,8 @@ def main(batch: str, mgroup_obj):
data[f"{mgroup_name}_日期"] = ";".join([item.strftime("%Y-%m-%d") for item in data[f"{mgroup_name}_日期"]]) data[f"{mgroup_name}_日期"] = ";".join([item.strftime("%Y-%m-%d") for item in data[f"{mgroup_name}_日期"]])
data[f"{mgroup_name}_操作人"] = list(set(data[f"{mgroup_name}_操作人"])) data[f"{mgroup_name}_操作人"] = list(set(data[f"{mgroup_name}_操作人"]))
data[f"{mgroup_name}_操作人"] = ";".join([item.name for item in data[f"{mgroup_name}_操作人"]]) data[f"{mgroup_name}_操作人"] = ";".join([item.name for item in data[f"{mgroup_name}_操作人"]])
data[f"{mgroup_name}_班次"] = list(set(data[f"{mgroup_name}_班次"]))
data[f"{mgroup_name}_班次"] = ";".join([item for item in data[f"{mgroup_name}_班次"]])
mlogb2_qs = Mlogb.objects.filter(mlog__submit_time__isnull=False, mlogb2_qs = Mlogb.objects.filter(mlog__submit_time__isnull=False,

View File

@ -216,12 +216,17 @@ class MlogbDefectSerializer(CustomModelSerializer):
defect_okcate = serializers.CharField(source="defect.okcate", read_only=True) defect_okcate = serializers.CharField(source="defect.okcate", read_only=True)
class Meta: class Meta:
model = MlogbDefect model = MlogbDefect
fields = ["id", "defect_name", "count", "mlogb", "defect", "defect_okcate"] fields = ["id", "defect_name", "count", "mlogb", "defect", "defect_okcate", "count_has"]
read_only_fields = EXCLUDE_FIELDS_BASE + ["mlogb"] read_only_fields = EXCLUDE_FIELDS_BASE + ["mlogb"]
extra_kwargs = {
'count_has': {'required': False},
}
def validate(self, attrs): def validate(self, attrs):
if attrs["count"] < 0: if attrs["count"] < 0:
raise serializers.ValidationError("存在负数!") raise serializers.ValidationError("存在负数!")
if "count_has" not in attrs or attrs["count_has"] < attrs["count"]:
attrs["count_has"] = attrs["count"]
return attrs return attrs
class MlogbSerializer(CustomModelSerializer): class MlogbSerializer(CustomModelSerializer):
@ -635,10 +640,12 @@ class MlogSerializer(CustomModelSerializer):
mgroup:Mgroup = attrs['mgroup'] mgroup:Mgroup = attrs['mgroup']
work_start_time:datetime = attrs['work_start_time'] work_start_time:datetime = attrs['work_start_time']
handle_date, attrs["shift"] = mgroup.get_shift(work_start_time) handle_date, attrs["shift"] = mgroup.get_shift(work_start_time)
if mtask.start_date == mtask.end_date: if mtask and mtask.start_date == mtask.end_date:
attrs['handle_date'] = mtask.end_date attrs['handle_date'] = mtask.end_date
if attrs['handle_date'] != handle_date: if attrs['handle_date'] != handle_date:
raise ParseError('任务日期与生产日期不一致') raise ParseError('任务日期与生产日期不一致')
else:
attrs['handle_date'] = handle_date
handle_user = attrs.get('handle_user', None) handle_user = attrs.get('handle_user', None)
if handle_user is None and hasattr(self, "request"): if handle_user is None and hasattr(self, "request"):
@ -684,8 +691,6 @@ class MlogInitSerializer(CustomModelSerializer):
supplier = attrs.get('supplier', None) supplier = attrs.get('supplier', None)
if not supplier: if not supplier:
raise ParseError('外协必须选择外协单位') raise ParseError('外协必须选择外协单位')
if attrs.get('work_start_time', None) and 'handle_date' not in attrs:
attrs['handle_date'] = localdate(attrs['work_end_time'])
# 如果已经确定产出则自动获取qct # 如果已经确定产出则自动获取qct
if attrs.get("material_out", None): if attrs.get("material_out", None):
attrs["qct"] = Qct.get(attrs["material_out"], "process", "out") attrs["qct"] = Qct.get(attrs["material_out"], "process", "out")
@ -696,12 +701,14 @@ class MlogInitSerializer(CustomModelSerializer):
class MlogChangeSerializer(CustomModelSerializer): class MlogChangeSerializer(CustomModelSerializer):
class Meta: class Meta:
model = Mlog model = Mlog
fields = ['id', 'work_end_time', 'handle_user', 'note', 'oinfo_json', 'test_file', 'test_user', 'test_time', 'equipment', "team"] fields = ['id', 'work_start_time', 'work_end_time', 'handle_user', 'note', 'oinfo_json', 'test_file', 'test_user', 'test_time', 'equipment', "team"]
# def validate(self, attrs): def update(self, instance, validated_data):
# if attrs.get('work_end_time', None): work_start_time = validated_data.get('work_start_time', None)
# attrs['handle_date'] = localdate(attrs['work_end_time']) if work_start_time:
# return attrs mgroup:Mgroup = instance.mgroup
validated_data["handle_date"], validated_data["shift"] = mgroup.get_shift(work_start_time)
return super().update(instance, validated_data)
class CountJsonSerializer(serializers.Serializer): class CountJsonSerializer(serializers.Serializer):
@ -824,10 +831,9 @@ class MlogbInUpdateSerializer(CustomModelSerializer):
MlogbDefect.objects.bulk_create(mlogb_defect_objects) MlogbDefect.objects.bulk_create(mlogb_defect_objects)
ins.cal_count_pn_jgqbl(cal_mlog=False) ins.cal_count_pn_jgqbl(cal_mlog=False)
# 只有普通工序的才可联动 # 只有普通工序的才可联动
material_out:Material = ins.mlog.material_out route:Route = ins.route if ins.route else ins.mlog.route
route:Route = mlog.route if route and route.process and route.process.mtype == Process.PRO_NORMAL:
if material_out.tracking == Material.MA_TRACKING_BATCH: if route.material_out.tracking == Material.MA_TRACKING_BATCH:
if route and route.process and route.process.mtype == Process.PRO_NORMAL:
mlogbout_qs = Mlogb.objects.filter(mlog=ins.mlog, mlogb_from=ins) mlogbout_qs = Mlogb.objects.filter(mlog=ins.mlog, mlogb_from=ins)
if mlogbout_qs.count() == 1: if mlogbout_qs.count() == 1:
mlogbout = mlogbout_qs.first() mlogbout = mlogbout_qs.first()
@ -910,7 +916,7 @@ class MlogbwCreateUpdateSerializer(CustomModelSerializer):
class MlogbwStartTestSerializer(serializers.Serializer): class MlogbwStartTestSerializer(serializers.Serializer):
mlogbw_ids = serializers.ListField(child=serializers.CharField(), label="mlogbwId列表") mlogbw_ids = serializers.ListField(child=serializers.CharField(), label="mlogbwId列表")
test_equip = serializers.CharField(label="测试设备", allow_null=True, required=False) test_equip = serializers.CharField(label="测试设备", allow_null=True, required=False, allow_blank=True)
test_user = serializers.CharField(label="测试人员") test_user = serializers.CharField(label="测试人员")
defects = serializers.ListField(child=serializers.CharField(), required=False, allow_null=True, label="缺陷项列表") defects = serializers.ListField(child=serializers.CharField(), required=False, allow_null=True, label="缺陷项列表")
testitems = serializers.ListField(child=serializers.CharField(), required=False, allow_null=True, label="测试项列表") testitems = serializers.ListField(child=serializers.CharField(), required=False, allow_null=True, label="测试项列表")
@ -950,7 +956,7 @@ class MlogbwStartTestSerializer(serializers.Serializer):
if mlogbw.ftest: if mlogbw.ftest:
existing_ftests[mlogbw.ftest_id] = mlogbw.ftest existing_ftests[mlogbw.ftest_id] = mlogbw.ftest
else: else:
ftest = Ftest(mlogbw=mlogbw, test_date=test_date, qct=qct) ftest = Ftest(test_date=test_date, qct=qct, test_user=test_user, type="process", id=idWorker.get_id(), is_ok=True)
new_ftests.append(ftest) new_ftests.append(ftest)
mlogbws_to_update.append(mlogbw) mlogbws_to_update.append(mlogbw)
@ -988,7 +994,7 @@ class MlogbwStartTestSerializer(serializers.Serializer):
else: else:
# 新记录,需要创建 # 新记录,需要创建
defects_to_create.append( defects_to_create.append(
FtestDefect(ftest=ftest, defect=defect, test_user=test_user) FtestDefect(ftest=ftest, defect=defect, test_user=test_user, id=idWorker.get_id())
) )
# 批量创建新记录 # 批量创建新记录
@ -1027,7 +1033,8 @@ class MlogbwStartTestSerializer(serializers.Serializer):
ftest=ftest, ftest=ftest,
testitem=testitem, testitem=testitem,
test_user=test_user, test_user=test_user,
test_equip=test_equip test_equip=test_equip,
id=idWorker.get_id()
) )
) )

View File

@ -152,6 +152,8 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
""" """
生产日志提交后需要执行的操作 生产日志提交后需要执行的操作
""" """
if mlog.work_start_time > timezone.now():
raise ParseError('操作开始时间不能晚于当前时间')
if mlog.work_start_time and mlog.work_end_time and mlog.work_end_time < mlog.work_start_time: if mlog.work_start_time and mlog.work_end_time and mlog.work_end_time < mlog.work_start_time:
raise ParseError('操作结束时间不能早于操作开始时间') raise ParseError('操作结束时间不能早于操作开始时间')
if mlog.count_real == 0: if mlog.count_real == 0:
@ -203,7 +205,7 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
for item in m_ins: for item in m_ins:
mbd_qs = MlogbDefect.get_defect_qs_from_mlogb(item) mbd_qs = MlogbDefect.get_defect_qs_from_mlogb(item)
for itemx in mbd_qs: for itemx in mbd_qs:
if itemx.defect: if itemx.defect and itemx.count > 0:
m_ins_bl_list.append((item.material_in, item.batch, itemx.count, itemx.defect, item)) m_ins_bl_list.append((item.material_in, item.batch, itemx.count, itemx.defect, item))
else: else:
m_ins_list = [(material_in, mlog.batch, mlog.count_use, None, mlog)] m_ins_list = [(material_in, mlog.batch, mlog.count_use, None, mlog)]
@ -282,7 +284,8 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
# if item.material_out.tracking == Material.MA_TRACKING_SINGLE: # if item.material_out.tracking == Material.MA_TRACKING_SINGLE:
# Mlogbw.cal_count_notok(item) # Mlogbw.cal_count_notok(item)
for itemx in mbd_qs: for itemx in mbd_qs:
m_outs_list.append((item.material_out, item.batch, itemx.count, 0, itemx.defect, item)) if itemx.count > 0:
m_outs_list.append((item.material_out, item.batch, itemx.count, 0, itemx.defect, item))
# # 获取所有主要的不合格项/先暂时保留 # # 获取所有主要的不合格项/先暂时保留
# bw_qs = Mlogbw.objects.filter(mlogb=item) # 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() # defectIds= Ftest.objects.filter(mlogbw_ftest__in=bw_qs).exclude(defect_main=None).values_list("defect_main__id", flat=True).distinct()
@ -435,7 +438,8 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
# if item.material_out.tracking == Material.MA_TRACKING_SINGLE: # if item.material_out.tracking == Material.MA_TRACKING_SINGLE:
# Mlogbw.cal_count_notok(item) # Mlogbw.cal_count_notok(item)
for itemx in mbd_qs: for itemx in mbd_qs:
m_outs_list.append((item.material_out, item.batch, itemx.count, 0, itemx.defect, item)) if itemx.count > 0:
m_outs_list.append((item.material_out, item.batch, itemx.count, 0, itemx.defect, item))
# if item.material_out.tracking == Material.MA_TRACKING_SINGLE: # if item.material_out.tracking == Material.MA_TRACKING_SINGLE:
# # 获取所有主要的不合格项 # # 获取所有主要的不合格项
# bw_qs = Mlogbw.objects.filter(mlogb=item) # bw_qs = Mlogbw.objects.filter(mlogb=item)
@ -530,7 +534,7 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
for item in m_ins: for item in m_ins:
mbd_qs = MlogbDefect.get_defect_qs_from_mlogb(item) mbd_qs = MlogbDefect.get_defect_qs_from_mlogb(item)
for itemx in mbd_qs: for itemx in mbd_qs:
if itemx.defect: if itemx.defect and itemx.count > 0:
m_ins_bl_list.append((item.material_in, item.batch, itemx.count, itemx.defect, item)) m_ins_bl_list.append((item.material_in, item.batch, itemx.count, itemx.defect, item))
else: else:
m_ins_list = [(material_in, mlog.batch, mlog.count_use, mlog.wm_in, mlog)] m_ins_list = [(material_in, mlog.batch, mlog.count_use, mlog.wm_in, mlog)]