Merge branch 'master' of https://e.coding.net/ctcdevteam/ehs/ehs_server
This commit is contained in:
commit
4a3934b4fd
|
@ -235,6 +235,20 @@ class FtestWork(CommonBDModel):
|
||||||
ftestwork_count_fields.append(f.name)
|
ftestwork_count_fields.append(f.name)
|
||||||
return ftestwork_count_fields
|
return ftestwork_count_fields
|
||||||
|
|
||||||
|
# def cal_count(self):
|
||||||
|
# self.count_notok = FtestworkDefect.objects.filter(
|
||||||
|
# mlogb=self, defect__okcate=30).aggregate(total=Sum("count"))["total"] or 0
|
||||||
|
# self.count_ok = self.count_real - self.count_notok
|
||||||
|
# if self.count_ok < 0:
|
||||||
|
# raise ParseError('合格数量不能小于0')
|
||||||
|
# self.save(update_fields=['count_ok', 'count_notok'])
|
||||||
|
|
||||||
|
# class FtestworkDefect(BaseModel):
|
||||||
|
# ftestwork = models.ForeignKey(FtestWork, verbose_name='关联检验工作', on_delete=models.CASCADE,
|
||||||
|
# related_name='ftestwork_defect')
|
||||||
|
# defect = models.ForeignKey(Defect, verbose_name='缺陷', on_delete=models.CASCADE)
|
||||||
|
# count = models.PositiveIntegerField('数量', default=0)
|
||||||
|
|
||||||
class Ftest(CommonBDModel):
|
class Ftest(CommonBDModel):
|
||||||
"""
|
"""
|
||||||
检验记录
|
检验记录
|
||||||
|
|
|
@ -4,7 +4,7 @@ from apps.qm.models import (QuaStat, TestItem, Ftest, FtestItem, FtestWork, Ptes
|
||||||
from apps.utils.constants import EXCLUDE_FIELDS, EXCLUDE_FIELDS_BASE
|
from apps.utils.constants import EXCLUDE_FIELDS, EXCLUDE_FIELDS_BASE
|
||||||
from apps.utils.serializers import CustomModelSerializer
|
from apps.utils.serializers import CustomModelSerializer
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError, ParseError
|
||||||
from apps.wpm.models import SfLog, WMaterial
|
from apps.wpm.models import SfLog, WMaterial
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from apps.inm.serializers import MaterialBatchDetailSerializer
|
from apps.inm.serializers import MaterialBatchDetailSerializer
|
||||||
|
@ -270,7 +270,7 @@ class FtestProcessSerializer(CustomModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Ftest
|
model = Ftest
|
||||||
fields = ["id", "test_date",
|
fields = ["id", "test_date",
|
||||||
"test_user", "note", "ftestitems", "ftestdefects", "qct", "test_user_name", "wpr"]
|
"test_user", "note", "ftestitems", "ftestdefects", "qct", "test_user_name"]
|
||||||
extra_kwargs = {"qct": {"required": True}}
|
extra_kwargs = {"qct": {"required": True}}
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
|
@ -300,19 +300,21 @@ class FtestProcessSerializer(CustomModelSerializer):
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
instance = super().update(instance, validated_data)
|
instance = super().update(instance, validated_data)
|
||||||
for item in ftestitems:
|
for item in ftestitems:
|
||||||
if "id" in item and item["id"]:
|
try:
|
||||||
FtestItem.objects.filter(id=item["id"]).update(
|
ins = FtestItem.objects.get(testitem = item["testitem"], ftest=instance)
|
||||||
test_user = item["test_user"],
|
except FtestItem.DoesNotExist:
|
||||||
test_val_json = item["test_val_json"])
|
raise ParseError("新的检测项!")
|
||||||
else:
|
for k, v in item.items():
|
||||||
FtestItem.objects.create(ftest=instance, **item)
|
setattr(ins, k, v)
|
||||||
|
ins.save()
|
||||||
is_ok = True
|
is_ok = True
|
||||||
for item2 in ftestdefects:
|
for item2 in ftestdefects:
|
||||||
if "id" in item2 and item2["id"]:
|
try:
|
||||||
ins: FtestDefect = FtestDefect.objects.get(id=item2["id"])
|
ins = FtestDefect.objects.get(ftest=instance, defect=item2["defect"])
|
||||||
else:
|
except FtestDefect.DoesNotExist:
|
||||||
ins = FtestDefect.objects.create(ftest=instance, **item2)
|
raise ParseError("新的缺陷项!")
|
||||||
ins.has = item2["has"]
|
for k, v in item2.items():
|
||||||
|
setattr(ins, k, v)
|
||||||
ins.save()
|
ins.save()
|
||||||
if ins.has and ins.defect.okcate in [Defect.DEFECT_NOTOK]:
|
if ins.has and ins.defect.okcate in [Defect.DEFECT_NOTOK]:
|
||||||
is_ok = False
|
is_ok = False
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
# Generated by Django 3.2.12 on 2025-01-08 07:01
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
import django.utils.timezone
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('qm', '0034_auto_20250108_1016'),
|
|
||||||
('wpm', '0083_mlogbw_ftest'),
|
|
||||||
]
|
|
||||||
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='mlogb',
|
|
||||||
name='defects',
|
|
||||||
field=models.ManyToManyField(blank=True, through='wpm.MlogbDefect', to='qm.Defect', verbose_name='不合格缺陷'),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -344,33 +344,19 @@ class Mlogb(BaseModel):
|
||||||
count_n_zt = models.PositiveIntegerField('炸头', default=0)
|
count_n_zt = models.PositiveIntegerField('炸头', default=0)
|
||||||
count_n_qt = models.PositiveIntegerField('其他', default=0)
|
count_n_qt = models.PositiveIntegerField('其他', default=0)
|
||||||
count_notok_json = models.JSONField('不合格情况', default=list, blank=True)
|
count_notok_json = models.JSONField('不合格情况', default=list, blank=True)
|
||||||
defects = models.ManyToManyField("qm.defect", verbose_name='不合格缺陷',
|
|
||||||
through="wpm.mlogbdefect", blank=True)
|
|
||||||
def get_tracking(self):
|
def get_tracking(self):
|
||||||
if self.material_in:
|
if self.material_in:
|
||||||
return "in", self.material_in.tracking
|
return "in", self.material_in.tracking
|
||||||
elif self.material_out:
|
elif self.material_out:
|
||||||
return "out", self.material_out.tracking
|
return "out", self.material_out.tracking
|
||||||
|
|
||||||
def cal_count(self):
|
|
||||||
self.count_notok = MlogbDefect.objects.filter(
|
|
||||||
mlogb=self, defect__okcate=30).aggregate(total=Sum("count"))["total"] or 0
|
|
||||||
self.count_ok = self.count_real - self.count_notok
|
|
||||||
if self.count_ok < 0:
|
|
||||||
raise ParseError('合格数量不能小于0')
|
|
||||||
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 = models.PositiveIntegerField('数量', default=0)
|
|
||||||
|
|
||||||
class Mlogbw(BaseModel):
|
class Mlogbw(BaseModel):
|
||||||
number = models.TextField('单个编号')
|
number = models.TextField('单个编号')
|
||||||
mlogb = models.ForeignKey(Mlogb, verbose_name='生产记录', on_delete=models.CASCADE)
|
mlogb = models.ForeignKey(Mlogb, verbose_name='生产记录', on_delete=models.CASCADE)
|
||||||
wpr = models.ForeignKey("wpmw.wpr", verbose_name='关联产品', on_delete=models.SET_NULL
|
wpr = models.ForeignKey("wpmw.wpr", verbose_name='关联产品', on_delete=models.SET_NULL
|
||||||
, related_name='wpr_mlogbw', null=True, blank=True)
|
, related_name='wpr_mlogbw', null=True, blank=True)
|
||||||
ftest = models.OneToOneField("qm.ftest", verbose_name='关联检验', on_delete=models.PROTECT, null=True, blank=True)
|
ftest = models.OneToOneField("qm.ftest", verbose_name='关联检验',
|
||||||
|
on_delete=models.PROTECT, null=True, blank=True, related_name="mlogbw_ftest")
|
||||||
note = models.TextField('备注', null=True, blank=True)
|
note = models.TextField('备注', null=True, blank=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ from datetime import datetime
|
||||||
|
|
||||||
from .models import (SfLog, StLog, SfLogExp, WMaterial, Mlog,
|
from .models import (SfLog, StLog, SfLogExp, WMaterial, Mlog,
|
||||||
Handover, Handoverb, Mlogb, AttLog,
|
Handover, Handoverb, Mlogb, AttLog,
|
||||||
OtherLog, Fmlog, BatchSt, Mlogbw, Handoverbw, MlogbDefect)
|
OtherLog, Fmlog, BatchSt, Mlogbw, Handoverbw)
|
||||||
from apps.system.models import Dept, User
|
from apps.system.models import Dept, User
|
||||||
from apps.system.serializers import UserSimpleSerializer
|
from apps.system.serializers import UserSimpleSerializer
|
||||||
from apps.pm.models import Mtask, Mtaskb
|
from apps.pm.models import Mtask, Mtaskb
|
||||||
|
@ -591,14 +591,15 @@ class MlogbwCreateUpdateSerializer(CustomModelSerializer):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
def save_ftest(self, mlogbw, ftest_data):
|
def save_ftest(self, mlogbw, ftest_data):
|
||||||
if "id" not in ftest_data or not ftest_data["id"]:
|
ftest = mlogbw.ftest
|
||||||
|
if not ftest:
|
||||||
ftest_sr = FtestProcessSerializer(data=ftest_data)
|
ftest_sr = FtestProcessSerializer(data=ftest_data)
|
||||||
ftest = ftest_sr.create(ftest_data)
|
ftest = ftest_sr.create(ftest_data)
|
||||||
mlogbw.ftest = ftest
|
mlogbw.ftest = ftest
|
||||||
mlogbw.save()
|
mlogbw.save()
|
||||||
else:
|
else:
|
||||||
ftest_sr = FtestProcessSerializer()
|
ftest_sr = FtestProcessSerializer()
|
||||||
ftest_sr.update(instance=Ftest.objects.get(id=ftest_data["id"]), validated_data=ftest_data)
|
ftest_sr.update(instance=ftest, validated_data=ftest_data)
|
||||||
return mlogbw
|
return mlogbw
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
|
@ -908,16 +909,3 @@ class BatchStSerializer(CustomModelSerializer):
|
||||||
model = BatchSt
|
model = BatchSt
|
||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
|
|
||||||
class MlogbDefectSerializer(CustomModelSerializer):
|
|
||||||
defect_name = serializers.CharField(source='defect.name', read_only=True)
|
|
||||||
class Meta:
|
|
||||||
model = MlogbDefect
|
|
||||||
fields = "__all__"
|
|
||||||
read_only_fields = EXCLUDE_FIELDS_BASE
|
|
||||||
|
|
||||||
def create(self, validated_data):
|
|
||||||
defect = validated_data["defect"]
|
|
||||||
mlogb = validated_data["mlogb"]
|
|
||||||
if MlogbDefect.objects.filter(mlogb=mlogb, defect=defect).exists():
|
|
||||||
raise ParseError('该缺陷已填写')
|
|
||||||
return super().create(validated_data)
|
|
|
@ -201,6 +201,8 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
|
||||||
if mlogbws.count() != mi_count:
|
if mlogbws.count() != mi_count:
|
||||||
raise ParseError("日志与明细数量不一致,操作失败")
|
raise ParseError("日志与明细数量不一致,操作失败")
|
||||||
for item in mlogbws:
|
for item in mlogbws:
|
||||||
|
if item.test:
|
||||||
|
raise ParseError("不支持消耗物料的检验")
|
||||||
Wpr.change_or_new(wpr=item.wpr, old_wm=wm)
|
Wpr.change_or_new(wpr=item.wpr, old_wm=wm)
|
||||||
|
|
||||||
# 针对加工前不良的暂时额外处理
|
# 针对加工前不良的暂时额外处理
|
||||||
|
|
|
@ -5,7 +5,7 @@ from apps.wpm.views import (SfLogViewSet, StLogViewSet, SfLogExpViewSet,
|
||||||
WMaterialViewSet, MlogViewSet, HandoverViewSet,
|
WMaterialViewSet, MlogViewSet, HandoverViewSet,
|
||||||
AttlogViewSet, OtherLogViewSet, MlogbViewSet, MlogbInViewSet,
|
AttlogViewSet, OtherLogViewSet, MlogbViewSet, MlogbInViewSet,
|
||||||
MlogbOutViewSet, FmlogViewSet, BatchStViewSet,
|
MlogbOutViewSet, FmlogViewSet, BatchStViewSet,
|
||||||
MlogbwViewSet, MlogbDefectViewSet)
|
MlogbwViewSet)
|
||||||
from apps.wpm.datax import AnaViewSet
|
from apps.wpm.datax import AnaViewSet
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,7 +28,6 @@ router.register('otherlog', OtherLogViewSet, basename='otherlog')
|
||||||
router.register('ana', AnaViewSet, basename='ana')
|
router.register('ana', AnaViewSet, basename='ana')
|
||||||
router.register('batchst', BatchStViewSet, basename='batchst')
|
router.register('batchst', BatchStViewSet, basename='batchst')
|
||||||
router.register('mlogbw', MlogbwViewSet, basename='mlogbw')
|
router.register('mlogbw', MlogbwViewSet, basename='mlogbw')
|
||||||
router.register("mlogbdefect", MlogbDefectViewSet, basename='mlogbdefect')
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path(API_BASE_URL, include(router.urls)),
|
path(API_BASE_URL, include(router.urls)),
|
||||||
]
|
]
|
||||||
|
|
|
@ -16,7 +16,7 @@ from apps.utils.mixins import CustomListModelMixin, BulkCreateModelMixin, BulkDe
|
||||||
|
|
||||||
from .filters import StLogFilter, SfLogFilter, WMaterialFilter, MlogFilter, HandoverFilter, MlogbFilter, BatchStFilter
|
from .filters import StLogFilter, SfLogFilter, WMaterialFilter, MlogFilter, HandoverFilter, MlogbFilter, BatchStFilter
|
||||||
from .models import (SfLog, SfLogExp, StLog, WMaterial, Mlog, Handover, Mlogb,
|
from .models import (SfLog, SfLogExp, StLog, WMaterial, Mlog, Handover, Mlogb,
|
||||||
Mlogbw, AttLog, OtherLog, Fmlog, BatchSt, MlogbDefect)
|
Mlogbw, AttLog, OtherLog, Fmlog, BatchSt)
|
||||||
from .serializers import (SflogExpSerializer, SfLogSerializer, StLogSerializer, WMaterialSerializer,
|
from .serializers import (SflogExpSerializer, SfLogSerializer, StLogSerializer, WMaterialSerializer,
|
||||||
MlogRevertSerializer,
|
MlogRevertSerializer,
|
||||||
MlogSerializer, MlogRelatedSerializer, DeptBatchSerializer, HandoverSerializer,
|
MlogSerializer, MlogRelatedSerializer, DeptBatchSerializer, HandoverSerializer,
|
||||||
|
@ -25,7 +25,7 @@ from .serializers import (SflogExpSerializer, SfLogSerializer, StLogSerializer,
|
||||||
AttLogSerializer, OtherLogSerializer, MlogInitSerializer, MlogChangeSerializer,
|
AttLogSerializer, OtherLogSerializer, MlogInitSerializer, MlogChangeSerializer,
|
||||||
MlogbDetailSerializer, MlogbInSerializer, MlogbInUpdateSerializer,
|
MlogbDetailSerializer, MlogbInSerializer, MlogbInUpdateSerializer,
|
||||||
MlogbOutUpdateSerializer, FmlogSerializer, FmlogUpdateSerializer, BatchStSerializer,
|
MlogbOutUpdateSerializer, FmlogSerializer, FmlogUpdateSerializer, BatchStSerializer,
|
||||||
MlogbwCreateUpdateSerializer, MlogbDefectSerializer)
|
MlogbwCreateUpdateSerializer)
|
||||||
from .services import mlog_submit, handover_submit, mlog_revert
|
from .services import mlog_submit, handover_submit, mlog_revert
|
||||||
from apps.wpm.services import mlog_submit_validate, generate_new_batch
|
from apps.wpm.services import mlog_submit_validate, generate_new_batch
|
||||||
from apps.wf.models import State
|
from apps.wf.models import State
|
||||||
|
@ -507,7 +507,7 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust
|
||||||
# 找寻质检表
|
# 找寻质检表
|
||||||
qctmat0 = QctMat.objects.filter(material=mlogbin.material_in).order_by("-create_time").first()
|
qctmat0 = QctMat.objects.filter(material=mlogbin.material_in).order_by("-create_time").first()
|
||||||
if qctmat0:
|
if qctmat0:
|
||||||
mlogbin.qct = qctmat0
|
mlogbin.qct = qctmat0.qct
|
||||||
mlogbin.save()
|
mlogbin.save()
|
||||||
mlog:Mlog = mlogbin.mlog
|
mlog:Mlog = mlogbin.mlog
|
||||||
route:Route = mlog.route
|
route:Route = mlog.route
|
||||||
|
@ -618,8 +618,8 @@ class MlogbwViewSet(CustomModelViewSet):
|
||||||
mlogb.count_use = count
|
mlogb.count_use = count
|
||||||
elif mlogb.material_out:
|
elif mlogb.material_out:
|
||||||
mlogb.count_real = count
|
mlogb.count_real = count
|
||||||
mlogb.count_ok = count
|
mlogb.count_notok = Mlogbw.objects.filter(mlogb=mlogb, ftest__is_ok=False).count()
|
||||||
mlogb.count_notok = 0
|
mlogb.count_ok = count - mlogb.count_notok
|
||||||
mlogb.save()
|
mlogb.save()
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
|
@ -639,13 +639,15 @@ class MlogbwViewSet(CustomModelViewSet):
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def perform_update(self, serializer):
|
def perform_update(self, serializer):
|
||||||
mlogbw = serializer.save()
|
mlogbw = serializer.save()
|
||||||
|
self.cal_mlogb_count(mlogbw.mlogb)
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def perform_destroy(self, instance:Mlogbw):
|
def perform_destroy(self, instance:Mlogbw):
|
||||||
mlogb = instance.mlogb
|
mlogb = instance.mlogb
|
||||||
if instance.ftest:
|
ftest = instance.ftest
|
||||||
instance.ftest.delete()
|
|
||||||
instance.delete()
|
instance.delete()
|
||||||
|
if ftest:
|
||||||
|
ftest.delete()
|
||||||
self.cal_mlogb_count(mlogb)
|
self.cal_mlogb_count(mlogb)
|
||||||
# 如果是输入且输出追踪到个,需同步删除
|
# 如果是输入且输出追踪到个,需同步删除
|
||||||
material_in: Material = mlogb.material_in
|
material_in: Material = mlogb.material_in
|
||||||
|
@ -658,28 +660,3 @@ class MlogbwViewSet(CustomModelViewSet):
|
||||||
mbw.ftest.delete()
|
mbw.ftest.delete()
|
||||||
mbw.delete()
|
mbw.delete()
|
||||||
self.cal_mlogb_count(mlogb_to)
|
self.cal_mlogb_count(mlogb_to)
|
||||||
|
|
||||||
|
|
||||||
class MlogbDefectViewSet(CustomListModelMixin, BulkCreateModelMixin,
|
|
||||||
BulkDestroyModelMixin, CustomGenericViewSet):
|
|
||||||
perms_map = {"get": "*", "post": "mlog.update", "delete": "mlog.update"}
|
|
||||||
queryset = MlogbDefect.objects.all()
|
|
||||||
serializer_class = MlogbDefectSerializer
|
|
||||||
filterset_fields = ["mlogb", "defect"]
|
|
||||||
ordering = ["create_time"]
|
|
||||||
|
|
||||||
def filter_queryset(self, queryset):
|
|
||||||
if not self.detail and not self.request.query_params.get('mlogb', None):
|
|
||||||
raise ParseError('请指定所属日志明细')
|
|
||||||
return super().filter_queryset(queryset)
|
|
||||||
|
|
||||||
@transaction.atomic
|
|
||||||
def perform_create(self, serializer):
|
|
||||||
ins:MlogbDefect = serializer.save()
|
|
||||||
ins.mlogb.cal_count()
|
|
||||||
|
|
||||||
@transaction.atomic
|
|
||||||
def perform_destroy(self, instance):
|
|
||||||
mlogb = instance.mlogb
|
|
||||||
instance.delete()
|
|
||||||
mlogb.cal_count()
|
|
Loading…
Reference in New Issue