检验记录修改与提交

This commit is contained in:
caoqianming 2021-12-14 13:12:05 +08:00
parent 53428f022b
commit 1285fdb9a1
14 changed files with 317 additions and 63 deletions

View File

@ -0,0 +1,49 @@
# Generated by Django 3.2.9 on 2021-12-13 06:01
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('pm', '0016_auto_20211209_1638'),
]
operations = [
migrations.AlterField(
model_name='productionplan',
name='count',
field=models.PositiveIntegerField(default=1, verbose_name='生产数量'),
),
migrations.AlterField(
model_name='productionplan',
name='count_ok',
field=models.PositiveIntegerField(default=0, verbose_name='合格数'),
),
migrations.AlterField(
model_name='productionplan',
name='count_real',
field=models.PositiveIntegerField(default=0, verbose_name='实际产出数'),
),
migrations.AlterField(
model_name='subproductionplan',
name='main_count',
field=models.PositiveIntegerField(default=0, verbose_name='应产出数'),
),
migrations.AlterField(
model_name='subproductionplan',
name='main_count_ok',
field=models.PositiveIntegerField(default=0, verbose_name='合格数'),
),
migrations.AlterField(
model_name='subproductionplan',
name='main_count_real',
field=models.PositiveIntegerField(default=0, verbose_name='实际产出数'),
),
migrations.AlterField(
model_name='subproductionplan',
name='production_plan',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='subplan_plan', to='pm.productionplan', verbose_name='关联主生产计划'),
),
]

View File

@ -24,9 +24,9 @@ class ProductionPlan(CommonAModel):
number = models.CharField('编号', max_length=50, unique=True)
order = models.ForeignKey(Order, verbose_name='关联订单', null=True, blank=True, on_delete=models.SET_NULL)
product = models.ForeignKey(Material, verbose_name='生产产品', on_delete=models.CASCADE)
count = models.IntegerField('生产数量', default=1)
count_real = models.IntegerField('实际产出数', default=0)
count_ok = models.IntegerField('合格数', default=0)
count = models.PositiveIntegerField('生产数量', default=1)
count_real = models.PositiveIntegerField('实际产出数', default=0)
count_ok = models.PositiveIntegerField('合格数', default=0)
start_date = models.DateField('计划开工日期')
end_date = models.DateField('计划完工日期')
is_planed = models.BooleanField('是否已排产', default=False)
@ -63,9 +63,9 @@ class SubProductionPlan(CommonAModel):
process = models.ForeignKey(Process, verbose_name='关联大工序', on_delete=models.CASCADE)
main_product = models.ForeignKey(Material, verbose_name='主要产品', on_delete=models.CASCADE, null=True, blank=True)
main_count = models.IntegerField('应产出数', default=0)
main_count_real = models.IntegerField('实际产出数', default=0)
main_count_ok = models.IntegerField('合格数', default=0)
main_count = models.PositiveIntegerField('应产出数', default=0)
main_count_real = models.PositiveIntegerField('实际产出数', default=0)
main_count_ok = models.PositiveIntegerField('合格数', default=0)
steps = models.JSONField('工艺步骤', default=list)

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.9 on 2021-12-13 06:01
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('wpm', '0031_auto_20211209_1638'),
('qm', '0016_auto_20211210_1338'),
]
operations = [
migrations.AlterField(
model_name='testrecord',
name='wproduct',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='test_wproduct', to='wpm.wproduct', verbose_name='关联的动态产品'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2021-12-14 04:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('qm', '0017_alter_testrecord_wproduct'),
]
operations = [
migrations.AddField(
model_name='testrecord',
name='is_submited',
field=models.BooleanField(default=False, verbose_name='是否提交'),
),
]

View File

@ -1,7 +1,7 @@
from django.db import models
from django.db.models.enums import Choices
from apps.mtm.models import RecordForm, RecordFormField
from apps.system.models import CommonAModel, File
from apps.system.models import CommonADModel, CommonAModel, File
from utils.model import BaseModel
# Create your models here.
class Standard(CommonAModel):
@ -43,7 +43,7 @@ class AnalysisItem(CommonAModel):
class Meta:
verbose_name = '检验分析项'
class TestRecord(CommonAModel):
class TestRecord(CommonADModel):
"""
检验记录
"""
@ -70,6 +70,7 @@ class TestRecord(CommonAModel):
subproduction_plan = models.ForeignKey('pm.subproductionplan', verbose_name='关联的生产子计划', on_delete=models.CASCADE, null=True, blank=True)
fifo_item = models.ForeignKey('inm.fifoitem', verbose_name='关联的出入库批次', on_delete=models.CASCADE, null=True, blank=True)
origin_test = models.ForeignKey('self', verbose_name='原检验记录', on_delete=models.CASCADE, null=True, blank=True)
is_submited = models.BooleanField('是否提交', default=False)
remark = models.TextField('备注', default='')

View File

@ -3,6 +3,7 @@ from apps.mtm.models import RecordForm, RecordFormField
from apps.mtm.serializers import RecordFormFieldSerializer, RecordFormSimpleSerializer
from apps.system.serializers import FileSimpleSerializer
from .models import Standard, TestItem, TestRecord, TestRecordItem
from django.db import transaction
class StandardCreateUpdateSerializer(serializers.ModelSerializer):
class Meta:
@ -38,7 +39,13 @@ class AnalysisItemSerializer(serializers.ModelSerializer):
class TestRecordItemCreateSerializer(serializers.ModelSerializer):
class Meta:
model = TestRecordItem
fields = ['form_field', 'field_value']
fields = ['form_field', 'field_value', 'is_testok']
class TestRecordItemUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = TestRecordItem
fields = ['id', 'field_value', 'is_testok']
class TestRecordItemSerializer(serializers.ModelSerializer):
class Meta:
@ -56,24 +63,49 @@ class TestRecordListSerializer(serializers.ModelSerializer):
model = TestRecord
fields = '__all__'
class TestRecordDetailSerializer(serializers.ModelSerializer):
class TestRecordDetailBaseSerializer(serializers.ModelSerializer):
form_ = RecordFormSimpleSerializer(source='form', read_only=True)
record_data_ = TestRecordItemSerializer(source='item_test_record', read_only=True, many=True)
record_data = TestRecordItemSerializer(source='item_test_record', read_only=True, many=True)
class Meta:
model = TestRecord
fields = '__all__'
class TestRecordDetailSerializer(serializers.ModelSerializer):
form_ = RecordFormSimpleSerializer(source='form', read_only=True)
record_data = TestRecordItemSerializer(source='item_test_record', read_only=True, many=True)
# record_data_ = serializers.SerializerMethodField()
origin_test_ = TestRecordDetailBaseSerializer(source='origin_test', read_only=True)
class Meta:
model = TestRecord
fields = '__all__'
@staticmethod
def setup_eager_loading(queryset):
queryset = queryset.select_related('form','fifo_item')
return queryset
# def get_record_data_(self, obj):
# record_data = obj.record_data
# all_fields = RecordFormField.objects.filter(form=obj.form, is_deletd=False).order_by('sort')
# all_fields_l = RecordFormFieldSerializer(instance=all_fields, many=True).data
# for i in all_fields_l:
# key = i['field_key']
# i['field_value'] = record_data.get(key, None)
# return all_fields_l
# items_data = TestRecordItemSerializer(instance=obj.item_test_record, many=True).data
# if obj.origin_test and obj.type == TestRecord.TEST_PROCESS_RE:
# origin_
def to_representation(self, instance):
ret = super().to_representation(instance)
if instance.origin_test and instance.type == TestRecord.TEST_PROCESS_RE:
origin_test = ret['origin_test']
o_dict = {}
for i in origin_test['record_data']:
o_dict[i['field_key']] = i['field_value']
for i in ret['record_data']:
i['origin_value'] = o_dict[i['field_key']] if i['field_key'] in o_dict else None
return super().to_representation(instance)
class TestRecordUpdateSerializer(serializers.ModelSerializer):
record_data = TestRecordItemUpdateSerializer(source='item_test_record', many=True)
class Meta:
model = TestRecord
fields = ['is_testok', 'record_data']
def update(self, instance, validated_data):
with transaction.atomic():
record_data = validated_data.pop(validated_data)
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.save()
self.record_data.save()
return instance

View File

@ -1,5 +1,6 @@
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
from apps.qm.serializers import StandardCreateUpdateSerializer, StandardSerializer, TestItemCreateUpdateSerializer, TestItemSerializer, TestRecordCreateSerializer, TestRecordDetailSerializer, TestRecordListSerializer
from rest_framework import exceptions, serializers
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin
from apps.qm.serializers import StandardCreateUpdateSerializer, StandardSerializer, TestItemCreateUpdateSerializer, TestItemSerializer, TestRecordCreateSerializer, TestRecordDetailSerializer, TestRecordListSerializer, TestRecordUpdateSerializer
from apps.qm.models import Standard, TestItem, TestRecord, TestRecordItem
from django.shortcuts import render
from rest_framework.viewsets import GenericViewSet, ModelViewSet
@ -8,6 +9,10 @@ from rest_framework.exceptions import APIException
from rest_framework.response import Response
from rest_framework import status
from django.db import transaction
from rest_framework.decorators import action
from apps.wpm.models import WProduct
from apps.wpm.services import WpmServies
# Create your views here.
class StandardViewSet(CreateUpdateModelAMixin, ModelViewSet):
"""
@ -43,7 +48,7 @@ class TestItemViewSet(CreateUpdateModelAMixin, ModelViewSet):
return TestItemCreateUpdateSerializer
return TestItemSerializer
class TestRecordViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
class TestRecordViewSet(ListModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin, GenericViewSet):
"""
检测记录
"""
@ -58,8 +63,23 @@ class TestRecordViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
return TestRecordListSerializer
elif self.action == 'retrieve':
return TestRecordDetailSerializer
elif self.action == 'update':
return TestRecordUpdateSerializer
return super().get_serializer_class()
def destroy(self, request, *args, **kwargs):
obj = self.get_object()
if obj.is_submited:
raise exceptions.APIException('该记录已提交不可删除')
return super().destroy(request, *args, **kwargs)
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=serializers.Serializer)
def submit(self, request, pk=None):
obj = self.get_object()
with transaction.atomic():
WpmServies.update_wproduct_by_test(obj, request.user)
return Response()
# def create(self, request, *args, **kwargs):
# serializer = self.get_serializer(data=request.data)
# serializer.is_valid(raise_exception=True)

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2021-12-13 06:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wf', '0017_auto_20211203_1501'),
]
operations = [
migrations.AddField(
model_name='workflow',
name='key',
field=models.CharField(blank=True, max_length=20, null=True, unique=True, verbose_name='工作流标识'),
),
]

View File

@ -13,7 +13,7 @@ class Workflow(CommonAModel):
工作流
"""
name = models.CharField('名称', max_length=50)
key = models.CharField('工作流标识', unique=True, max_length=20)
key = models.CharField('工作流标识', unique=True, max_length=20, null=True, blank=True)
sn_prefix = models.CharField('流水号前缀', max_length=50, default='hb')
description = models.CharField('描述', max_length=200)
view_permission_check = models.BooleanField('查看权限校验', default=True, help_text='开启后,只允许工单的关联人(创建人、曾经的处理人)有权限查看工单')

View File

@ -0,0 +1,36 @@
# Generated by Django 3.2.9 on 2021-12-14 04:45
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('wf', '0018_workflow_key'),
('qm', '0018_testrecord_is_submited'),
('wpm', '0031_auto_20211209_1638'),
]
operations = [
migrations.AddField(
model_name='wproduct',
name='test',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wp_test', to='qm.testrecord', verbose_name='当前检验'),
),
migrations.AddField(
model_name='wproduct',
name='ticket',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wp_ticket', to='wf.ticket', verbose_name='当前工单'),
),
migrations.AlterField(
model_name='wproduct',
name='operation',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wp_operation', to='wpm.operation', verbose_name='当前操作'),
),
migrations.AlterField(
model_name='wprouctticket',
name='ticket',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='wt_ticket', to='wf.ticket', verbose_name='关联工单'),
),
]

View File

@ -1,4 +1,5 @@
import re
from rest_framework import exceptions
from django.db import models
from django.db.models.base import Model
import django.utils.timezone as timezone
@ -7,6 +8,7 @@ from apps.inm.models import FIFO, WareHouse
from apps.pm.models import ProductionPlan, SubProductionPlan, SubProductionProgress
from apps.qm.models import TestRecord
from apps.system.models import CommonADModel, CommonAModel, CommonBModel, Organization, User, Dict, File
from apps.wf.models import Ticket
from utils.model import SoftModel, BaseModel
from simple_history.models import HistoricalRecords
from apps.mtm.models import Material, Process, RecordFormField, Step, RecordForm, SubprodctionMaterial
@ -54,15 +56,54 @@ class WProduct(CommonAModel):
remark = models.CharField('备注', max_length=200, null=True, blank=True)
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='当前子生产计划', on_delete=models.CASCADE, related_name='wproduct_subplan')
warehouse = models.ForeignKey(WareHouse, verbose_name='所在仓库', on_delete=models.SET_NULL, null=True, blank=True)
operation = models.ForeignKey('wpm.operation', verbose_name='关联操作',
operation = models.ForeignKey('wpm.operation', verbose_name='当前操作',
on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_operation')
test = models.ForeignKey('qm.testrecord', verbose_name='当前检验',
on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_test')
ticket = models.ForeignKey('wf.ticket', verbose_name='当前工单',
on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_ticket')
@property
def last_process_test(self):
"""
最后的工序自检
最后提交的工序自检
"""
return self.test_wproduct.filter(type=TestRecord.TEST_PROCESS).order_by('-id').first()
return self.test_wproduct.filter(type=TestRecord.TEST_PROCESS, is_submited=True).order_by('-id').first()
# @property
# def last_test(self):
# """
# 最后提交的检验
# """
# return self.test_wproduct.filter(is_submited=True).order_by('-id').first()
# @property
# def current_test(self):
# """
# 当前未提交的检验
# """
# trs = self.test_wproduct.filter(is_submited=False).order_by('-id')
# if trs.count() == 1:
# return trs[0]
# elif trs.count() == 0:
# return None
# else:
# raise exceptions.APIException('存在多条未提交检验记录')
# @property
# def current_ticket(self):
# """
# 当前是否有进行中工单
# """
# tickets = Ticket.objects.filter(wt_ticket__wproduct=self, act_state=Ticket.TICKET_ACT_STATE_ONGOING).order_by('-id')
# if tickets.count() == 1:
# return tickets[0]
# elif tickets.count() == 0:
# return None
# else:
# raise exceptions.APIException('存在多条进行中工单')
class WprouctTicket(CommonAModel):
"""
玻璃审批工单
@ -72,7 +113,7 @@ class WprouctTicket(CommonAModel):
material = models.ForeignKey(Material, verbose_name='所在物料状态', on_delete=models.CASCADE)
step = models.ForeignKey(Step, verbose_name='所在步骤', on_delete=models.CASCADE)
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='所在子生产计划', on_delete=models.CASCADE)
ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单', on_delete=models.CASCADE)
ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单', on_delete=models.CASCADE, related_name='wt_ticket')
class Pick(CommonADModel):
"""

View File

@ -290,7 +290,7 @@ class WpmTestRecordCreateSerializer(serializers.ModelSerializer):
is_testok = serializers.BooleanField(required=False)
class Meta:
model = TestRecord
fields = ['form', 'record_data', 'is_testok', 'wproduct']
fields = ['form', 'record_data', 'is_testok', 'wproduct', 'is_submited']
class WpmTestFormInitSerializer(serializers.Serializer):
wproduct = serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all(), required=True)
@ -304,6 +304,7 @@ class WpmTestFormInitSerializer(serializers.Serializer):
raise exceptions.APIException('请指定检查表')
return super().validate(attrs)
class WplanPutInSerializer(serializers.Serializer):
warehouse = serializers.PrimaryKeyRelatedField(queryset=WareHouse.objects.all(), label="仓库ID")
remark = serializers.CharField(label="入库备注", required =False)

View File

@ -1,7 +1,10 @@
from typing import List
from apps.pm.models import SubProductionPlan
from apps.mtm.models import Step, SubprodctionMaterial
from apps.pm.models import SubProductionPlan, SubProductionProgress
from apps.mtm.models import Material, Step, SubprodctionMaterial
from apps.qm.models import TestRecord
from apps.system.models import User
from apps.wpm.models import WProduct
from utils.tools import ranstr
class WpmServies(object):
@classmethod
@ -31,4 +34,32 @@ class WpmServies(object):
"""
splans = SubProductionPlan.objects.filter(is_deleted=False,
subproduction__usedstep_subproduction__step=step, state=SubProductionPlan.SUBPLAN_STATE_WORKING)
return splans
return splans
@classmethod
def update_wproduct_by_test(cls, test:TestRecord, user:User):
"""
根据检测结果更新玻璃及相关状态
"""
is_testok = test.is_testok
wproduct = test.wproduct
if is_testok:
if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST: # 复检
wproduct.act_state = WProduct.WPR_ACT_STATE_DOWAIT
elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST and wproduct.material.type == Material.MA_TYPE_GOOD: # 成品检验
wproduct.act_state = WProduct.WPR_ACT_STATE_TOFINALTEST
else:
wproduct.act_state = WProduct.WPR_ACT_STATE_OK
if wproduct.number is None: # 产生半成品编号
wproduct.number = 'WP'+ranstr(7)
# 更新子计划状态
# 更新子计划主产品数
instance = SubProductionProgress.objects.get(subproduction_plan=wproduct.subproduction_plan,
is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT)
instance.count_ok = instance.count_ok + 1 # 这个地方可能会有问题
instance.save()
else:# 如果不合格
wproduct.act_state = WProduct.WPR_ACT_STATE_NOTOK
# 需要走不合格品审理的工单
wproduct.update_by = user
wproduct.save()

View File

@ -194,9 +194,10 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
vdata = serializer.validated_data
wproduct = vdata['wproduct']
form = vdata.get('form', None)
if wproduct.test:
raise exceptions.APIException('存在进行中检验')
# 如果是复检记录, 需要带入原数据
# 如果是复检, 需要带入原数据
if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST:
# 查找最近一条检验记录
trs = wproduct.last_process_test
@ -205,7 +206,7 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
data = RecordFormDetailSerializer(instance=trs.form).data
data['origin_test'] = origin_test
o_dict = {}
for i in origin_test['record_data_']:
for i in origin_test['record_data']:
o_dict[i['field_key']] = i['field_value']
for i in data['form_fields']:
i['origin_value'] = o_dict[i['field_key']] if i['field_key'] in o_dict else None
@ -221,7 +222,7 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
@transaction.atomic
def test(self, request, pk=None):
"""
检测
检测记录提交
"""
serializer = WpmTestRecordCreateSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
@ -237,6 +238,7 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
savedict = dict(create_by = self.request.user,
material=wproduct.material, number=wproduct.number, subproduction_plan=wproduct.subproduction_plan, step=wproduct.step)
if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST:
savedict['origin_test'] = wproduct.last_process_test
savedict['type'] = TestRecord.TEST_PROCESS_RE
elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOFINALTEST:
savedict['type'] = TestRecord.TEST_FINAL
@ -257,29 +259,13 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
TestRecordItem.objects.bulk_create(tris)
# 如果检测合格, 变更动态产品进行状态
if obj.is_testok:
if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST: # 复检
obj.origin_test = wproduct.last_process_test
obj.save()
wproduct.act_state = WProduct.WPR_ACT_STATE_DOWAIT
elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST and wproduct.material.type == Material.MA_TYPE_GOOD: # 成品检验
wproduct.act_state = WProduct.WPR_ACT_STATE_TOFINALTEST
else:
wproduct.act_state = WProduct.WPR_ACT_STATE_OK
if wproduct.number is None: # 产生半成品编号
wproduct.number = 'WP'+ranstr(7)
# 更新子计划状态
# 更新子计划主产品数
instance = SubProductionProgress.objects.get(subproduction_plan=wproduct.subproduction_plan,
is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT)
instance.count_ok = instance.count_ok + 1 # 这个地方可能会有问题
instance.save()
else:# 如果不合格
wproduct.act_state = WProduct.WPR_ACT_STATE_NOTOK
# 需要走不合格品审理单
wproduct.update_by = request.user
wproduct.save()
if obj.is_submited:
WpmServies.update_wproduct_by_test(obj, request.user)
else:
# 保存当前检验
wproduct.test = obj
wproduct.update_by = request.user
wproduct.save()
return Response()
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=WproductPutInsSerializer)
@ -379,6 +365,7 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
wproduct.save()
return Response()
class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin, GenericViewSet):
"""
生产操作记录