diff --git a/apps/qm/migrations/0029_auto_20241219_1006.py b/apps/qm/migrations/0029_auto_20241219_1006.py new file mode 100644 index 00000000..2dddeeb1 --- /dev/null +++ b/apps/qm/migrations/0029_auto_20241219_1006.py @@ -0,0 +1,87 @@ +# Generated by Django 3.2.12 on 2024-12-19 02:06 + +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', '0048_auto_20241218_1431'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('qm', '0028_defect'), + ] + + operations = [ + migrations.CreateModel( + name='Qct', + 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='删除标记')), + ('name', models.CharField(max_length=50, verbose_name='名称')), + ('number', models.CharField(max_length=20, verbose_name='编号')), + ('tags', models.JSONField(blank=True, default=list, verbose_name='检测类型')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='qct_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='QctMat', + 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='删除标记')), + ('tracing', models.CharField(choices=[('test', '检测项'), ('defect', '缺陷项')], default='test', help_text="(('test', '检测项'), ('defect', '缺陷项'))", max_length=20, verbose_name='追溯层级')), + ('material', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='物料')), + ('qc', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='qm.qct', verbose_name='质检模板')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='QctItem', + 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='删除标记')), + ('note', models.TextField(blank=True, null=True, verbose_name='备注')), + ('rule_expression', models.TextField(blank=True, null=True, verbose_name='判定表达式')), + ('sort', models.FloatField(default=1, verbose_name='排序')), + ('defect', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='qm.defect', verbose_name='缺陷项')), + ('qc', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='qm.qct', verbose_name='质检模板')), + ('testitem', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='qm.testitem', verbose_name='检测项')), + ], + options={ + 'abstract': False, + }, + ), + migrations.AddField( + model_name='qct', + name='defects', + field=models.ManyToManyField(blank=True, through='qm.QctItem', to='qm.Defect', verbose_name='缺陷项'), + ), + migrations.AddField( + model_name='qct', + name='materials', + field=models.ManyToManyField(blank=True, through='qm.QctMat', to='mtm.Material', verbose_name='物料'), + ), + migrations.AddField( + model_name='qct', + name='testitems', + field=models.ManyToManyField(blank=True, through='qm.QctItem', to='qm.TestItem', verbose_name='检测项'), + ), + migrations.AddField( + model_name='qct', + name='update_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='qct_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'), + ), + ] diff --git a/apps/qm/models.py b/apps/qm/models.py index 931f183c..2611f7a5 100644 --- a/apps/qm/models.py +++ b/apps/qm/models.py @@ -21,6 +21,7 @@ class Defect(CommonAModel): def __str__(self): return self.name + class NotOkOption(models.TextChoices): # 不合格项 zw = "zw", _("炸纹") @@ -124,6 +125,37 @@ class TestItem(CommonAModel): ordering = ['sort', '-create_time'] +QC_T = 'test' +QC_D = 'defect' + +QC_TRACE_CHOICES = ( + (QC_T, '检测项'), + (QC_D, '缺陷项') +) + +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) + testitems = models.ManyToManyField(TestItem, verbose_name="检测项", blank=True, through='qm.qctItem') + defects = models.ManyToManyField(Defect, verbose_name="缺陷项", blank=True, through='qm.qctItem') + materials = models.ManyToManyField(Material, verbose_name="物料", blank=True, through='qm.qctmat') + +class QctItem(BaseModel): + qc = models.ForeignKey(Qct, verbose_name="质检模板", on_delete=models.CASCADE) + testitem = models.ForeignKey(TestItem, verbose_name="检测项", on_delete=models.CASCADE, null=True, blank=True) + note = models.TextField('备注', null=True, blank=True) + rule_expression = models.TextField('判定表达式', null=True, blank=True) + defect = models.ForeignKey(Defect, verbose_name="缺陷项", on_delete=models.CASCADE, null=True, blank=True) + sort = models.FloatField('排序', default=1) + +class QctMat(BaseModel): + qc = models.ForeignKey(Qct, verbose_name="质检模板", on_delete=models.CASCADE) + material = models.ForeignKey(Material, verbose_name="物料", on_delete=models.CASCADE) + tracing = models.CharField('追溯层级', default=QC_T, choices=QC_TRACE_CHOICES, max_length=20, help_text=str(QC_TRACE_CHOICES)) + + class QuaStat(CommonBDModel): """ 质量数据表 diff --git a/apps/qm/serializers.py b/apps/qm/serializers.py index 66f95b38..1a8892e9 100644 --- a/apps/qm/serializers.py +++ b/apps/qm/serializers.py @@ -1,4 +1,5 @@ -from apps.qm.models import QuaStat, TestItem, Ftest, FtestItem, FtestWork, Ptest, NotOkOption, Defect +from apps.qm.models import (QuaStat, TestItem, Ftest, FtestItem, FtestWork, Ptest, + NotOkOption, Defect, Qct, QctItem, QctMat) from apps.utils.constants import EXCLUDE_FIELDS, EXCLUDE_FIELDS_BASE from apps.utils.serializers import CustomModelSerializer from rest_framework import serializers @@ -29,6 +30,32 @@ class TestItemSerializer(CustomModelSerializer): fields = '__all__' read_only_fields = EXCLUDE_FIELDS +class QctSerializer(CustomModelSerializer): + class Meta: + model = Qct + fields = '__all__' + read_only_fields = EXCLUDE_FIELDS + +class QctItemSerializer(CustomModelSerializer): + defect_name = serializers.StringRelatedField(source='defect', read_only=True) + testitem_name = serializers.CharField(source='testitem.name', read_only=True) + class Meta: + model = Qct + fields = '__all__' + +class QctMatSerializer(CustomModelSerializer): + material_name = serializers.StringRelatedField(source='material', read_only=True) + class Meta: + model = QctMat + fields = '__all__' + +class QctDetailSerializer(CustomModelSerializer): + testitems_ = QctItemSerializer(source="testitems", many=True, read_only=True) + defects_ = DefectSerializer(source="defects", many=True, read_only=True) + class Meta: + model = Qct + fields = '__all__' + read_only_fields = EXCLUDE_FIELDS class QuaStatSerializer(CustomModelSerializer): sflog = serializers.PrimaryKeyRelatedField( diff --git a/apps/qm/urls.py b/apps/qm/urls.py index a0f5c777..843cb006 100644 --- a/apps/qm/urls.py +++ b/apps/qm/urls.py @@ -3,7 +3,7 @@ from rest_framework.routers import DefaultRouter from apps.qm.views import (QuaStatViewSet, TestItemViewSet, FtestWorkViewSet, FtestViewSet, PtestViewSet, - NotOkOptionView, DefectViewSet) + NotOkOptionView, DefectViewSet, QctViewSet, QctItemViewSet, QctMatViewSet) API_BASE_URL = 'api/qm/' HTML_BASE_URL = 'qm/' @@ -15,6 +15,9 @@ router.register('ftest', FtestViewSet, basename='ftest') router.register('ftestwork', FtestWorkViewSet, basename='ftestwork') router.register('ptest', PtestViewSet, basename='ptest') router.register("defect", DefectViewSet, basename="defect") +router.register('qct', QctViewSet, basename='qct') +router.register('qctitem', QctItemViewSet, basename='qctitem') +router.register('qctmat', QctMatViewSet, basename='qctmat') urlpatterns = [ path(API_BASE_URL, include(router.urls)), path(API_BASE_URL + 'notok_option/', NotOkOptionView.as_view()), diff --git a/apps/qm/views.py b/apps/qm/views.py index fa803a8c..938b9f31 100644 --- a/apps/qm/views.py +++ b/apps/qm/views.py @@ -5,7 +5,8 @@ from rest_framework.views import APIView from rest_framework.serializers import Serializer from apps.qm.models import QuaStat, TestItem, Ftest, Ptest, FtestWork from apps.qm.serializers import QuaStatSerializer, TestItemSerializer, QuaStatUpdateSerializer, FtestSerializer, PtestSerializer, \ - FtestWorkCreateUpdateSerializer, FtestWorkSerializer, DefectSerializer + FtestWorkCreateUpdateSerializer, FtestWorkSerializer, DefectSerializer, QctSerializer, QctItemSerializer, QctMatSerializer, \ + QctDetailSerializer from apps.qm.tasks import cal_quastat_sflog from rest_framework.response import Response from apps.utils.mixins import BulkUpdateModelMixin @@ -15,7 +16,7 @@ from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet from apps.wpm.models import SfLog from apps.qm.filters import QuaStatFilter, TestItemFilter, FtestWorkFilter from django.db import transaction -from apps.qm.models import NotOkOption, Defect +from apps.qm.models import NotOkOption, Defect, Qct, QctItem, QctMat from apps.qm.services import ftestwork_submit from apps.utils.thread import MyThread from apps.wpm.services_2 import get_alldata_with_batch_and_store @@ -24,7 +25,7 @@ from apps.wf.models import State class DefectViewSet(CustomModelViewSet): """ - list:缺陷项 + list: 缺陷项 缺陷项 """ @@ -33,6 +34,41 @@ class DefectViewSet(CustomModelViewSet): filterset_fields = ["cate", "okcate"] search_fields = ["name", "code"] +class QctViewSet(CustomModelViewSet): + """ + list: 检测模板 + + 检测模板 + """ + queryset = Qct.objects.all() + serializer_class = QctSerializer + retrieve_serializer_class = QctDetailSerializer + filterset_fields = ["tags", "testitems", "defects"] + search_fields = ["name", "number"] + +class QctItemViewSet(CustomModelViewSet): + """ + list: 检测模板项 + + 检测模板项 + """ + perms_map = {"get": "*", "post": "qct.update", "put": "qct.update", "delete": "qct.update"} + queryset = QctItem.objects.all() + serializer_class = QctItemSerializer + filterset_fields = ["qct", "testitem", "defect"] + ordering = ["qc", "sort"] + +class QctMatViewSet(CustomModelViewSet): + """ + list: 检测物料 + + 检测物料 + """ + perms_map = {"get": "*", "post": "qct.update", "put": "qct.update", "delete": "qct.update"} + queryset = QctMat.objects.all() + serializer_class = QctMatSerializer + filterset_fields = ["qct", "material"] + class NotOkOptionView(APIView): perms_map = {'get': '*'}