feat: 增加产品检验
This commit is contained in:
parent
c11132e38f
commit
e5ba779b00
|
@ -1,7 +1,9 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from apps.qm.models import TestItem
|
from apps.qm.models import TestItem
|
||||||
# Register your models here.
|
# Register your models here.
|
||||||
|
|
||||||
|
|
||||||
@admin.register(TestItem)
|
@admin.register(TestItem)
|
||||||
class TestItemAdmin(admin.ModelAdmin):
|
class TestItemAdmin(admin.ModelAdmin):
|
||||||
date_hierarchy = 'create_time'
|
date_hierarchy = 'create_time'
|
||||||
list_display = ('id', 'name')
|
list_display = ('id', 'name', 'tags')
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
# Generated by Django 3.2.12 on 2024-01-17 07:54
|
||||||
|
|
||||||
|
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 = [
|
||||||
|
('mtm', '0025_auto_20231120_1139'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('system', '0003_alter_permission_parent'),
|
||||||
|
('qm', '0011_testitem_tags'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='ftest',
|
||||||
|
name='test_numer',
|
||||||
|
field=models.CharField(default='', max_length=20, verbose_name='检测编号'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='ftest',
|
||||||
|
name='type',
|
||||||
|
field=models.CharField(choices=[('first', '首件检验'), ('prod', '成品检验')], default='first', max_length=20, verbose_name='检验类型'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='ftest',
|
||||||
|
name='check_user',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='ftest_check_user', to=settings.AUTH_USER_MODEL, verbose_name='专检人'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='FtestWork',
|
||||||
|
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='检验日期')),
|
||||||
|
('batch', models.CharField(max_length=20, verbose_name='生产批次')),
|
||||||
|
('count', models.IntegerField(verbose_name='总数量')),
|
||||||
|
('count_sampling', models.IntegerField(verbose_name='抽检数量')),
|
||||||
|
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='ftestwork_belong_dept', to='system.dept', verbose_name='所属部门')),
|
||||||
|
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='ftestwork_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||||
|
('material', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='产品')),
|
||||||
|
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='ftestwork_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='ftest',
|
||||||
|
name='ftest_work',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='qm.ftestwork', verbose_name='关联检验工作'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,5 +1,5 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from apps.system.models import CommonAModel, User
|
from apps.system.models import CommonAModel, CommonADModel, User
|
||||||
from apps.utils.models import CommonBDModel, BaseModel
|
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
|
||||||
|
@ -37,18 +37,38 @@ class QuaStat(CommonBDModel):
|
||||||
rate_pass = models.FloatField('合格率', null=True, blank=True)
|
rate_pass = models.FloatField('合格率', null=True, blank=True)
|
||||||
|
|
||||||
|
|
||||||
class Ftest(CommonBDModel):
|
class FtestWork(CommonBDModel):
|
||||||
"""
|
"""
|
||||||
首件检验
|
检验工作
|
||||||
"""
|
"""
|
||||||
test_date = models.DateField('检验日期')
|
test_date = models.DateField('检验日期')
|
||||||
|
material = models.ForeignKey(
|
||||||
|
Material, verbose_name='产品', on_delete=models.CASCADE)
|
||||||
|
batch = models.CharField('生产批次', max_length=20)
|
||||||
|
count = models.IntegerField('总数量')
|
||||||
|
count_sampling = models.IntegerField('抽检数量')
|
||||||
|
|
||||||
|
|
||||||
|
class Ftest(CommonBDModel):
|
||||||
|
"""
|
||||||
|
检验记录
|
||||||
|
"""
|
||||||
|
FTEST_TYPE_CHOICES = (
|
||||||
|
('first', '首件检验'),
|
||||||
|
('prod', '成品检验')
|
||||||
|
)
|
||||||
|
type = models.CharField('检验类型', max_length=20, choices=FTEST_TYPE_CHOICES)
|
||||||
|
test_date = models.DateField('检验日期')
|
||||||
|
test_numer = models.CharField('检测编号', max_length=20, default='')
|
||||||
test_group = models.CharField('检验工序集', max_length=20, default='')
|
test_group = models.CharField('检验工序集', max_length=20, default='')
|
||||||
test_user = models.ForeignKey(
|
test_user = models.ForeignKey(
|
||||||
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')
|
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=False)
|
||||||
note = models.TextField('备注', default='', blank=True)
|
note = models.TextField('备注', default='', blank=True)
|
||||||
|
ftest_work = models.ForeignKey(
|
||||||
|
FtestWork, verbose_name='关联检验工作', on_delete=models.CASCADE, null=True, blank=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ftestitems(self):
|
def ftestitems(self):
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from apps.qm.models import QuaStat, TestItem, Ftest, FtestItem, Ptest
|
from apps.qm.models import QuaStat, TestItem, Ftest, FtestItem, FtestWork, Ptest
|
||||||
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
|
||||||
|
@ -58,6 +58,21 @@ class QuaStatUpdateSerializer(CustomModelSerializer):
|
||||||
return super().validate(attrs)
|
return super().validate(attrs)
|
||||||
|
|
||||||
|
|
||||||
|
class FtestWorkCreateUpdateSerializer(CustomModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = FtestWork
|
||||||
|
fields = ['id', 'test_date', 'material', 'batch', 'count']
|
||||||
|
|
||||||
|
|
||||||
|
class FtestWorkSerializer(CustomModelSerializer):
|
||||||
|
material_name = serializers.StringRelatedField(
|
||||||
|
source='material', read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = FtestWork
|
||||||
|
fields = "__all__"
|
||||||
|
|
||||||
|
|
||||||
class FtestItemSerializer(CustomModelSerializer):
|
class FtestItemSerializer(CustomModelSerializer):
|
||||||
testitem_name = serializers.CharField(
|
testitem_name = serializers.CharField(
|
||||||
source='testitem.name', read_only=True)
|
source='testitem.name', read_only=True)
|
||||||
|
@ -71,8 +86,6 @@ class FtestItemSerializer(CustomModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class FtestSerializer(CustomModelSerializer):
|
class FtestSerializer(CustomModelSerializer):
|
||||||
belong_dept = serializers.PrimaryKeyRelatedField(
|
|
||||||
required=True, queryset=Dept.objects.all())
|
|
||||||
test_user_name = serializers.CharField(
|
test_user_name = serializers.CharField(
|
||||||
source='test_user.name', read_only=True)
|
source='test_user.name', read_only=True)
|
||||||
check_user_name = serializers.CharField(
|
check_user_name = serializers.CharField(
|
||||||
|
@ -82,7 +95,13 @@ class FtestSerializer(CustomModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Ftest
|
model = Ftest
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
read_only_fields = EXCLUDE_FIELDS
|
read_only_fields = EXCLUDE_FIELDS + ['type']
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
ftest_work = attrs.get('ftest_work', None)
|
||||||
|
if ftest_work:
|
||||||
|
attrs['type'] = 'prod' # 带检验工作的标记为成品检验
|
||||||
|
return attrs
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
ftestitems = validated_data.pop('ftestitems', [])
|
ftestitems = validated_data.pop('ftestitems', [])
|
||||||
|
@ -93,6 +112,7 @@ class FtestSerializer(CustomModelSerializer):
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
|
validated_data.pop('ftest_work', None)
|
||||||
ftestitems = validated_data.pop('ftestitems', [])
|
ftestitems = validated_data.pop('ftestitems', [])
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
instance = super().update(instance, validated_data)
|
instance = super().update(instance, validated_data)
|
||||||
|
|
|
@ -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, FtestViewSet, PtestViewSet
|
from apps.qm.views import QuaStatViewSet, TestItemViewSet, FtestWorkViewSet, FtestViewSet, PtestViewSet
|
||||||
|
|
||||||
API_BASE_URL = 'api/qm/'
|
API_BASE_URL = 'api/qm/'
|
||||||
HTML_BASE_URL = 'qm/'
|
HTML_BASE_URL = 'qm/'
|
||||||
|
@ -10,6 +10,7 @@ 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')
|
router.register('ftest', FtestViewSet, basename='ftest')
|
||||||
|
router.register('ftestwork', FtestWorkViewSet, basename='ftestwork')
|
||||||
router.register('ptest', PtestViewSet, basename='ptest')
|
router.register('ptest', PtestViewSet, basename='ptest')
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path(API_BASE_URL, include(router.urls)),
|
path(API_BASE_URL, include(router.urls)),
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
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, Ftest, Ptest
|
from apps.qm.models import QuaStat, TestItem, Ftest, Ptest, FtestWork
|
||||||
from apps.qm.serializers import QuaStatSerializer, TestItemSerializer, QuaStatUpdateSerializer, FtestSerializer, PtestSerializer
|
from apps.qm.serializers import QuaStatSerializer, TestItemSerializer, QuaStatUpdateSerializer, FtestSerializer, PtestSerializer, \
|
||||||
|
FtestWorkCreateUpdateSerializer, FtestWorkSerializer
|
||||||
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
|
||||||
|
@ -55,13 +56,14 @@ class QuaStatViewSet(ListModelMixin, BulkUpdateModelMixin, CustomGenericViewSet)
|
||||||
|
|
||||||
class FtestViewSet(CustomModelViewSet):
|
class FtestViewSet(CustomModelViewSet):
|
||||||
"""
|
"""
|
||||||
list:首件检验
|
list:首件/成品检验
|
||||||
|
|
||||||
首件检验
|
首件/成品检验
|
||||||
"""
|
"""
|
||||||
queryset = Ftest.objects.all()
|
queryset = Ftest.objects.all()
|
||||||
serializer_class = FtestSerializer
|
serializer_class = FtestSerializer
|
||||||
select_related_fields = ['test_user', 'check_user']
|
select_related_fields = ['test_user', 'check_user', 'ftest_work']
|
||||||
|
filterset_fields = ['type', 'ftest_work']
|
||||||
|
|
||||||
|
|
||||||
class PtestViewSet(CustomModelViewSet):
|
class PtestViewSet(CustomModelViewSet):
|
||||||
|
@ -74,3 +76,17 @@ class PtestViewSet(CustomModelViewSet):
|
||||||
serializer_class = PtestSerializer
|
serializer_class = PtestSerializer
|
||||||
select_related_fields = ['testitem']
|
select_related_fields = ['testitem']
|
||||||
filterset_fields = ['testitem', 'test_date']
|
filterset_fields = ['testitem', 'test_date']
|
||||||
|
|
||||||
|
|
||||||
|
class FtestWorkViewSet(CustomModelViewSet):
|
||||||
|
"""
|
||||||
|
list: 检验工作
|
||||||
|
|
||||||
|
检验工作
|
||||||
|
"""
|
||||||
|
queryset = FtestWork.objects.all()
|
||||||
|
serializer_class = FtestWorkSerializer
|
||||||
|
create_serializer_class = FtestWorkCreateUpdateSerializer
|
||||||
|
update_serializer_class = FtestWorkCreateUpdateSerializer
|
||||||
|
select_related_fields = ['material']
|
||||||
|
filterset_fields = ['material', 'batch']
|
||||||
|
|
Loading…
Reference in New Issue