feat: 增加首件检验
This commit is contained in:
parent
b2c201877f
commit
7def29d173
|
@ -0,0 +1,60 @@
|
||||||
|
# Generated by Django 3.2.12 on 2023-11-02 09:58
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('system', '0002_myschedule'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('qm', '0008_auto_20230725_1112'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Ftest',
|
||||||
|
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='删除标记')),
|
||||||
|
('test_date', models.DateField(verbose_name='检验日期')),
|
||||||
|
('test_group', models.CharField(default='', max_length=20, verbose_name='检验工序集')),
|
||||||
|
('is_ok', models.BooleanField(default=False, verbose_name='是否合格')),
|
||||||
|
('note', models.TextField(blank=True, default='', verbose_name='备注')),
|
||||||
|
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='ftest_belong_dept', to='system.dept', verbose_name='所属部门')),
|
||||||
|
('check_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ftest_check_user', to=settings.AUTH_USER_MODEL, verbose_name='专检人')),
|
||||||
|
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='ftest_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||||
|
('test_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ftest_test_user', to=settings.AUTH_USER_MODEL, verbose_name='操作人')),
|
||||||
|
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='ftest_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='testitem',
|
||||||
|
name='description',
|
||||||
|
field=models.TextField(default='', verbose_name='描述'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='FtestItem',
|
||||||
|
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='删除标记')),
|
||||||
|
('test_val', models.FloatField(blank=True, default=0, verbose_name='测量值')),
|
||||||
|
('check_val', models.FloatField(blank=True, null=True, verbose_name='专检测量值')),
|
||||||
|
('ftest', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='qm.ftest', verbose_name='关联检验')),
|
||||||
|
('testitem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='qm.testitem', verbose_name='质检项目')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,28 +1,65 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from apps.system.models import CommonAModel
|
from apps.system.models import CommonAModel, User
|
||||||
from apps.utils.models import CommonBDModel
|
from apps.utils.models import CommonBDModel, BaseModel
|
||||||
from apps.mtm.models import Material, Mgroup, Team
|
from apps.mtm.models import Material, Mgroup, Team
|
||||||
from apps.wpm.models import SfLog
|
from apps.wpm.models import SfLog
|
||||||
|
|
||||||
|
|
||||||
class TestItem(CommonAModel):
|
class TestItem(CommonAModel):
|
||||||
"""
|
"""
|
||||||
检验项目
|
检验项目
|
||||||
"""
|
"""
|
||||||
name = models.CharField('名称', max_length=100)
|
name = models.CharField('名称', max_length=100)
|
||||||
sort = models.PositiveSmallIntegerField('排序', default=1)
|
sort = models.PositiveSmallIntegerField('排序', default=1)
|
||||||
|
description = models.TextField('描述', default='')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['sort', '-create_time']
|
ordering = ['sort', '-create_time']
|
||||||
|
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
|
|
||||||
|
|
||||||
class QuaStat(CommonBDModel):
|
class QuaStat(CommonBDModel):
|
||||||
"""
|
"""
|
||||||
质量数据表
|
质量数据表
|
||||||
"""
|
"""
|
||||||
material = models.ForeignKey(Material, verbose_name='关联产物', on_delete=models.CASCADE)
|
material = models.ForeignKey(
|
||||||
sflog = models.ForeignKey(SfLog, verbose_name='关联值班记录', on_delete=models.CASCADE, null=True, blank=True)
|
Material, verbose_name='关联产物', on_delete=models.CASCADE)
|
||||||
testitem = models.ForeignKey(TestItem, verbose_name='质检项目', on_delete=models.CASCADE)
|
sflog = models.ForeignKey(
|
||||||
|
SfLog, verbose_name='关联值班记录', on_delete=models.CASCADE, null=True, blank=True)
|
||||||
|
testitem = models.ForeignKey(
|
||||||
|
TestItem, verbose_name='质检项目', on_delete=models.CASCADE)
|
||||||
val_avg = models.FloatField('平均值', null=True, blank=True)
|
val_avg = models.FloatField('平均值', null=True, blank=True)
|
||||||
num_test = models.PositiveSmallIntegerField('检测次数', null=True, blank=True)
|
num_test = models.PositiveSmallIntegerField('检测次数', null=True, blank=True)
|
||||||
num_ok = models.PositiveSmallIntegerField('合格次数', null=True, blank=True)
|
num_ok = models.PositiveSmallIntegerField('合格次数', null=True, blank=True)
|
||||||
rate_pass = models.FloatField('合格率', null=True, blank=True)
|
rate_pass = models.FloatField('合格率', null=True, blank=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Ftest(CommonBDModel):
|
||||||
|
"""
|
||||||
|
首件检验
|
||||||
|
"""
|
||||||
|
test_date = models.DateField('检验日期')
|
||||||
|
test_group = models.CharField('检验工序集', max_length=20, default='')
|
||||||
|
test_user = models.ForeignKey(
|
||||||
|
User, verbose_name='操作人', on_delete=models.CASCADE, related_name='ftest_test_user')
|
||||||
|
check_user = models.ForeignKey(
|
||||||
|
User, verbose_name='专检人', on_delete=models.CASCADE, related_name='ftest_check_user')
|
||||||
|
is_ok = models.BooleanField('是否合格', default=False)
|
||||||
|
note = models.TextField('备注', default='', blank=True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ftestitems(self):
|
||||||
|
return FtestItem.objects.filter(ftest=self)
|
||||||
|
|
||||||
|
|
||||||
|
class FtestItem(BaseModel):
|
||||||
|
"""
|
||||||
|
检测明细
|
||||||
|
"""
|
||||||
|
ftest = models.ForeignKey(
|
||||||
|
Ftest, verbose_name='关联检验', on_delete=models.CASCADE)
|
||||||
|
testitem = models.ForeignKey(
|
||||||
|
TestItem, verbose_name='质检项目', on_delete=models.CASCADE)
|
||||||
|
test_val = models.FloatField('测量值', default=0, blank=True)
|
||||||
|
check_val = models.FloatField('专检测量值', null=True, blank=True)
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
from apps.qm.models import QuaStat, TestItem
|
from apps.qm.models import QuaStat, TestItem, Ftest, FtestItem
|
||||||
from apps.utils.constants import EXCLUDE_FIELDS
|
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 apps.system.models import Dept, Dictionary
|
from apps.system.models import Dept, Dictionary
|
||||||
from apps.wpm.models import SfLog
|
from apps.wpm.models import SfLog
|
||||||
|
from django.db import transaction
|
||||||
|
|
||||||
|
|
||||||
class TestItemSerializer(CustomModelSerializer):
|
class TestItemSerializer(CustomModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -11,33 +13,94 @@ class TestItemSerializer(CustomModelSerializer):
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
read_only_fields = EXCLUDE_FIELDS
|
read_only_fields = EXCLUDE_FIELDS
|
||||||
|
|
||||||
|
|
||||||
class QuaStatSerializer(CustomModelSerializer):
|
class QuaStatSerializer(CustomModelSerializer):
|
||||||
sflog = serializers.PrimaryKeyRelatedField(label="值班记录", queryset=SfLog.objects.all(), required=True)
|
sflog = serializers.PrimaryKeyRelatedField(
|
||||||
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
|
label="值班记录", queryset=SfLog.objects.all(), required=True)
|
||||||
material_name = serializers.CharField(source='material.name', read_only=True)
|
belong_dept_name = serializers.CharField(
|
||||||
testitem_name = serializers.CharField(source='testitem.name', read_only=True)
|
source='belong_dept.name', read_only=True)
|
||||||
class Meta:
|
material_name = serializers.CharField(
|
||||||
model = QuaStat
|
source='material.name', read_only=True)
|
||||||
fields = '__all__'
|
testitem_name = serializers.CharField(
|
||||||
read_only_fields = EXCLUDE_FIELDS + ['belong_dept']
|
source='testitem.name', read_only=True)
|
||||||
extra_kwargs = {'val_avg': {'required': True, 'allow_null': False}, 'num_test':{'required': True, 'allow_null': False}, 'num_ok': {'required': True, 'allow_null': False}}
|
|
||||||
|
|
||||||
def validate(self, attrs):
|
|
||||||
attrs['rate_pass'] = attrs['num_ok']/attrs['num_test']
|
|
||||||
attrs['belong_dept'] = attrs['sflog'].mgroup.belong_dept
|
|
||||||
return attrs
|
|
||||||
|
|
||||||
class QuaStatUpdateSerializer(CustomModelSerializer):
|
|
||||||
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
|
|
||||||
material_name = serializers.CharField(source='material.name', read_only=True)
|
|
||||||
testitem_name = serializers.CharField(source='testitem.name', read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = QuaStat
|
model = QuaStat
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
read_only_fields = EXCLUDE_FIELDS + ['belong_dept', 'sflog', 'material', 'testitem']
|
read_only_fields = EXCLUDE_FIELDS + ['belong_dept']
|
||||||
extra_kwargs = {'val_avg': {'required': True, 'allow_null': False}, 'num_test':{'required': True, 'allow_null': False}, 'num_ok': {'required': True, 'allow_null': False}}
|
extra_kwargs = {'val_avg': {'required': True, 'allow_null': False}, 'num_test': {
|
||||||
|
'required': True, 'allow_null': False}, 'num_ok': {'required': True, 'allow_null': False}}
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
attrs['rate_pass'] = attrs['num_ok']/attrs['num_test']
|
attrs['rate_pass'] = attrs['num_ok']/attrs['num_test']
|
||||||
return super().validate(attrs)
|
attrs['belong_dept'] = attrs['sflog'].mgroup.belong_dept
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
|
class QuaStatUpdateSerializer(CustomModelSerializer):
|
||||||
|
belong_dept_name = serializers.CharField(
|
||||||
|
source='belong_dept.name', read_only=True)
|
||||||
|
material_name = serializers.CharField(
|
||||||
|
source='material.name', read_only=True)
|
||||||
|
testitem_name = serializers.CharField(
|
||||||
|
source='testitem.name', read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = QuaStat
|
||||||
|
fields = '__all__'
|
||||||
|
read_only_fields = EXCLUDE_FIELDS + \
|
||||||
|
['belong_dept', 'sflog', 'material', 'testitem']
|
||||||
|
extra_kwargs = {'val_avg': {'required': True, 'allow_null': False}, 'num_test': {
|
||||||
|
'required': True, 'allow_null': False}, 'num_ok': {'required': True, 'allow_null': False}}
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
attrs['rate_pass'] = attrs['num_ok']/attrs['num_test']
|
||||||
|
return super().validate(attrs)
|
||||||
|
|
||||||
|
|
||||||
|
class FtestItemSerializer(CustomModelSerializer):
|
||||||
|
testitem_name = serializers.CharField(
|
||||||
|
source='testitem.name', read_only=True)
|
||||||
|
testitem_description = serializers.CharField(
|
||||||
|
source='testitem.description', read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = FtestItem
|
||||||
|
fields = '__all__'
|
||||||
|
read_only_fields = EXCLUDE_FIELDS_BASE
|
||||||
|
|
||||||
|
|
||||||
|
class FtestSerializer(CustomModelSerializer):
|
||||||
|
belong_dept = serializers.PrimaryKeyRelatedField(
|
||||||
|
required=True, queryset=Dept.objects.all())
|
||||||
|
test_user_name = serializers.CharField(
|
||||||
|
source='test_user.name', read_only=True)
|
||||||
|
check_user_name = serializers.CharField(
|
||||||
|
source='check_user.name', read_only=True)
|
||||||
|
ftestitems = FtestItemSerializer(label='检验明细', many=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Ftest
|
||||||
|
fields = '__all__'
|
||||||
|
read_only_fields = EXCLUDE_FIELDS
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
ftestitems = validated_data.pop('ftestitems', [])
|
||||||
|
with transaction.atomic():
|
||||||
|
instance = super().create(validated_data)
|
||||||
|
for item in ftestitems:
|
||||||
|
FtestItem.objects.create(ftest=instance, **item)
|
||||||
|
return instance
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
ftestitems = validated_data.pop('ftestitems', [])
|
||||||
|
with transaction.atomic():
|
||||||
|
instance = super().update(instance, validated_data)
|
||||||
|
for item in ftestitems:
|
||||||
|
id = item.get('id', None)
|
||||||
|
if id:
|
||||||
|
ftestitem = FtestItem.objects.get(id=id)
|
||||||
|
ftestitem.test_val = item['test_val']
|
||||||
|
ftestitem.check_val = item['check_val']
|
||||||
|
ftestitem.save()
|
||||||
|
return instance
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
|
|
||||||
from apps.qm.views import QuaStatViewSet, TestItemViewSet
|
from apps.qm.views import QuaStatViewSet, TestItemViewSet, FtestViewSet
|
||||||
|
|
||||||
API_BASE_URL = 'api/qm/'
|
API_BASE_URL = 'api/qm/'
|
||||||
HTML_BASE_URL = 'qm/'
|
HTML_BASE_URL = 'qm/'
|
||||||
|
@ -9,6 +9,8 @@ HTML_BASE_URL = 'qm/'
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('quastat', QuaStatViewSet, basename='quastat')
|
router.register('quastat', QuaStatViewSet, basename='quastat')
|
||||||
router.register('testitem', TestItemViewSet, basename='testitem')
|
router.register('testitem', TestItemViewSet, basename='testitem')
|
||||||
|
router.register('ftest', FtestViewSet, basename='Ftest')
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path(API_BASE_URL, include(router.urls)),
|
path(API_BASE_URL, include(router.urls)),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelMixin
|
from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelMixin
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from apps.qm.models import QuaStat, TestItem
|
from apps.qm.models import QuaStat, TestItem, Ftest
|
||||||
from apps.qm.serializers import QuaStatSerializer, TestItemSerializer, QuaStatUpdateSerializer
|
from apps.qm.serializers import QuaStatSerializer, TestItemSerializer, QuaStatUpdateSerializer, FtestSerializer
|
||||||
from apps.qm.tasks import cal_quastat_sflog
|
from apps.qm.tasks import cal_quastat_sflog
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from apps.utils.mixins import BulkCreateModelMixin, BulkUpdateModelMixin
|
from apps.utils.mixins import BulkCreateModelMixin, BulkUpdateModelMixin
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from apps.utils.viewsets import CustomGenericViewSet
|
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
|
||||||
from apps.wpm.models import SfLog
|
from apps.wpm.models import SfLog
|
||||||
from apps.qm.filters import QuaStatFilter
|
from apps.qm.filters import QuaStatFilter
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
||||||
|
|
||||||
class TestItemViewSet(ListModelMixin, CustomGenericViewSet):
|
class TestItemViewSet(ListModelMixin, CustomGenericViewSet):
|
||||||
"""
|
"""
|
||||||
list:质检项目
|
list:质检项目
|
||||||
|
@ -24,6 +26,7 @@ class TestItemViewSet(ListModelMixin, CustomGenericViewSet):
|
||||||
filterset_fields = []
|
filterset_fields = []
|
||||||
ordering = ['id']
|
ordering = ['id']
|
||||||
|
|
||||||
|
|
||||||
class QuaStatViewSet(ListModelMixin, BulkUpdateModelMixin, CustomGenericViewSet):
|
class QuaStatViewSet(ListModelMixin, BulkUpdateModelMixin, CustomGenericViewSet):
|
||||||
"""
|
"""
|
||||||
list:质量数据统计
|
list:质量数据统计
|
||||||
|
@ -44,6 +47,18 @@ class QuaStatViewSet(ListModelMixin, BulkUpdateModelMixin, CustomGenericViewSet)
|
||||||
for i in objs:
|
for i in objs:
|
||||||
sflogIds.append(i['sflog'])
|
sflogIds.append(i['sflog'])
|
||||||
sflogIds = list(set(sflogIds))
|
sflogIds = list(set(sflogIds))
|
||||||
SfLog.objects.filter(id__in=sflogIds).update(last_test_time=now) # 更新质检记录时间
|
SfLog.objects.filter(id__in=sflogIds).update(
|
||||||
|
last_test_time=now) # 更新质检记录时间
|
||||||
for sflogId in sflogIds:
|
for sflogId in sflogIds:
|
||||||
cal_quastat_sflog.delay(sflogId)
|
cal_quastat_sflog.delay(sflogId)
|
||||||
|
|
||||||
|
|
||||||
|
class FtestViewSet(CustomModelViewSet):
|
||||||
|
"""
|
||||||
|
list:首件检验
|
||||||
|
|
||||||
|
首件检验
|
||||||
|
"""
|
||||||
|
queryset = Ftest.objects.all()
|
||||||
|
serializer_class = FtestSerializer
|
||||||
|
select_related_fields = ['test_user', 'check_user']
|
||||||
|
|
Loading…
Reference in New Issue