增加记录表格表

This commit is contained in:
caoqianming 2021-09-01 14:47:10 +08:00
parent cc9006b695
commit 32459d8710
6 changed files with 216 additions and 25 deletions

View File

@ -70,15 +70,27 @@ class Step(CommonAModel):
def __str__(self):
return self.name
# class StepTable(CommonAModel):
# """
# 过程记录表格
# """
class StepOperationItem(CommonAModel):
class RecordForm(CommonAModel):
"""
操作记录条目
记录表格
"""
type_choices=(
(1, '生产记录'),
)
name = models.CharField('表格名称', max_length=100, unique=True)
type = models.IntegerField('表格类型', choices=type_choices, default=1)
step = models.ForeignKey(Step, verbose_name='关联子工序', on_delete=models.CASCADE)
class Meta:
verbose_name = '记录表格'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
class RecordFormField(CommonAModel):
"""
记录字段表
"""
field_type_choices = (
('string', '字符串'),
@ -93,7 +105,7 @@ class StepOperationItem(CommonAModel):
('selects', '多选下拉'),
('textarea', '文本域'),
)
step = models.ForeignKey(Step, on_delete=models.CASCADE, verbose_name='关联步骤')
form = models.ForeignKey(RecordForm, on_delete=models.CASCADE, verbose_name='关联表格')
field_type = models.CharField('类型', max_length=50, choices=field_type_choices)
field_key = models.CharField('字段标识', max_length=50, help_text='字段类型请尽量特殊,避免与系统中关键字冲突')
field_name = models.CharField('字段名称', max_length=50)
@ -103,7 +115,7 @@ class StepOperationItem(CommonAModel):
help_text='radio,checkbox,select,multiselect类型可供选择的选项格式为json如:{"1":"中国", "2":"美国"},注意数字也需要引号')
sort = models.IntegerField('排序号', default=1)
class Meta:
verbose_name = '操作记录条目'
verbose_name = '记录表格字段'
verbose_name_plural = verbose_name
def __str__(self):

View File

@ -1,7 +1,7 @@
from apps.em.serializers import EquipmentSerializer, EquipmentSimpleSerializer
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from .models import InputMaterial, Material, OutputMaterial, Process, ProductProcess, Step, UsedStep
from .models import InputMaterial, Material, OutputMaterial, Process, ProductProcess, RecordForm, RecordFormField, Step, UsedStep
from apps.system.serializers import FileSimpleSerializer, OrganizationSimpleSerializer
@ -137,3 +137,53 @@ class UsedStepListSerializer(serializers.ModelSerializer):
""" Perform necessary eager loading of data. """
queryset = queryset.select_related('step')
return queryset
class RecordFormSerializer(serializers.ModelSerializer):
step_ = StepSimpleSerializer(source='step', read_only=True)
"""
记录表格序列化
"""
class Meta:
model = RecordForm
fields = '__all__'
@staticmethod
def setup_eager_loading(queryset):
""" Perform necessary eager loading of data. """
queryset = queryset.select_related('step')
return queryset
class RecordFormCreateSerializer(serializers.ModelSerializer):
class Meta:
model = RecordForm
fields = ['name', 'type', 'step']
class RecordFormUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = RecordForm
fields = ['name', 'type']
class RecordFormFieldSerializer(serializers.ModelSerializer):
class Meta:
model = RecordForm
fields = '__all__'
class RecordFormFieldCreateSerializer(serializers.ModelSerializer):
class Meta:
model = RecordFormField
fields = ['form', 'field_type', 'field_key', 'field_name', 'boolean_field_display', 'field_choice', 'sort']
def validate(self, data):
if RecordFormField.objects.filter(field_key=data['field_key'], form=data['form'], is_deleted=False).exists():
raise ValidationError('字段key已存在!')
return data
class RecordFormFieldUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = RecordFormField
fields = ['field_type', 'field_name', 'boolean_field_display', 'field_choice', 'sort']
class RecordFormFieldSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = RecordFormField
fields = '__all__'

View File

@ -1,6 +1,6 @@
from django.db.models import base
from rest_framework import urlpatterns
from apps.mtm.views import InputMaterialViewSet, MaterialViewSet, OutputMaterialViewSet, ProcessViewSet, StepViewSet, UsedStepViewSet
from apps.mtm.views import InputMaterialViewSet, MaterialViewSet, OutputMaterialViewSet, ProcessViewSet, RecordFormFieldViewSet, RecordFormViewSet, StepViewSet, UsedStepViewSet
from django.urls import path, include
from rest_framework.routers import DefaultRouter
@ -12,6 +12,8 @@ router.register('step', StepViewSet, basename='step')
router.register('inputmaterial', InputMaterialViewSet, basename='inputmaterial')
router.register('outputmaterial', OutputMaterialViewSet, basename='outputmaterial')
router.register('usedstep', UsedStepViewSet, basename='usedstep')
router.register('recordform', RecordFormViewSet, basename='recordform')
router.register('recordform-field', RecordFormFieldViewSet, basename='recordform-field')
urlpatterns = [
path('', include(router.urls)),
]

View File

@ -2,8 +2,8 @@ from django.shortcuts import render
from rest_framework.viewsets import ModelViewSet, GenericViewSet
from rest_framework.mixins import CreateModelMixin, ListModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin
from apps.mtm.models import InputMaterial, Material, OutputMaterial, Process, ProductProcess, Step, UsedStep
from apps.mtm.serializers import InputMaterialListSerializer, InputMaterialSerializer, InputMaterialUpdateSerializer, MaterialDetailSerializer, MaterialSerializer, MaterialSimpleSerializer, OutputMaterialListSerializer, OutputMaterialSerializer, OutputMaterialUpdateSerializer, ProductProcessListSerializer, ProductProcessUpdateSerializer, ProcessSerializer, StepDetailSerializer, StepSerializer, UsedStepCreateSerializer, UsedStepListSerializer
from apps.mtm.models import InputMaterial, Material, OutputMaterial, Process, ProductProcess, RecordForm, RecordFormField, Step, UsedStep
from apps.mtm.serializers import InputMaterialListSerializer, InputMaterialSerializer, InputMaterialUpdateSerializer, MaterialDetailSerializer, MaterialSerializer, MaterialSimpleSerializer, OutputMaterialListSerializer, OutputMaterialSerializer, OutputMaterialUpdateSerializer, ProductProcessListSerializer, ProductProcessUpdateSerializer, ProcessSerializer, RecordFormCreateSerializer, RecordFormFieldCreateSerializer, RecordFormFieldSerializer, RecordFormFieldUpdateSerializer, RecordFormSerializer, RecordFormUpdateSerializer, StepDetailSerializer, StepSerializer, UsedStepCreateSerializer, UsedStepListSerializer
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
from rest_framework.decorators import action
from rest_framework.response import Response
@ -127,7 +127,7 @@ class OutputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet):
return OutputMaterialUpdateSerializer
return OutputMaterialSerializer
class UsedStepViewSet(OptimizationMixin, CreateModelMixin, DestroyModelMixin, ListModelMixin, GenericViewSet):
class UsedStepViewSet(OptimizationMixin, CreateUpdateModelAMixin, CreateModelMixin, DestroyModelMixin, ListModelMixin, GenericViewSet):
"""
产品生产子工序表
"""
@ -139,4 +139,37 @@ class UsedStepViewSet(OptimizationMixin, CreateModelMixin, DestroyModelMixin, Li
def get_serializer_class(self):
if self.action =='create':
return UsedStepCreateSerializer
return UsedStepListSerializer
return UsedStepListSerializer
class RecordFormViewSet(OptimizationMixin, CreateUpdateModelAMixin, ModelViewSet):
"""
记录表格增删改查
"""
perms_map = {'*':'*'}
queryset = RecordForm.objects.all()
filterset_fields = ['step', 'type']
search_fields = ['name']
def get_serializer_class(self):
if self.action =='create':
return RecordFormCreateSerializer
elif self.action == 'update':
return RecordFormUpdateSerializer
return RecordFormSerializer
class RecordFormFieldViewSet(OptimizationMixin, CreateUpdateModelAMixin, ModelViewSet):
"""
表格字段表 增删改查
"""
perms_map = {'*':'*'}
queryset = RecordFormField.objects.all()
filterset_fields = ['field_type', 'form']
search_fields = ['field_name', 'field_key']
def get_serializer_class(self):
if self.action =='create':
return RecordFormFieldCreateSerializer
elif self.action == 'update':
return RecordFormFieldUpdateSerializer
return RecordFormFieldSerializer

View File

@ -89,7 +89,7 @@ class WfService(object):
@classmethod
def get_next_state_id_by_transition_and_ticket_info(cls, ticket:Ticket, transition: Transition)->object:
def get_next_state_by_transition_and_ticket_info(cls, ticket:Ticket, transition: Transition)->object:
"""
获取下个节点状态
"""
@ -172,8 +172,37 @@ class WfService(object):
if ticket.in_add_node:
return dict(permission=False, msg="工单当前处于加签中,请加签完成后操作")
return dict(permission=True, msg="")
@classmethod
def check_dict_has_all_same_value(cls, dict_obj: object)->tuple:
"""
check whether all key are equal in a dict
:param dict_obj:
:return:
"""
value_list = []
for key, value in dict_obj.items():
value_list.append(value)
value_0 = value_list[0]
for value in value_list:
if value_0 != value:
return False
return True
@classmethod
def get_ticket_all_field_value(cls, ticket: Ticket)->dict:
"""
工单所有字段的值
get ticket's all field value
:param ticket:
:return:
"""
# 获取工单基础表中的字段中的字段信息
field_info_dict = ticket.get_dict()
# 获取自定义字段的值
custom_fields_queryset = cls.get_workflow_custom_fields(ticket.workflow)
for i in custom_fields_queryset:
field_info_dict[i.field_key] = ticket.ticket_data.get(i.field_key, None)
return field_info_dict

View File

@ -9,7 +9,7 @@ from rest_framework.decorators import action, api_view
from apps.wf.models import CustomField, Ticket, Workflow, State, Transition, TicketFlow
from apps.system.mixins import CreateUpdateCustomMixin, CreateUpdateModelAMixin, OptimizationMixin
from apps.wf.services import WfService
from rest_framework.exceptions import APIException
from rest_framework.exceptions import APIException, PermissionDenied
# Create your views here.
class WorkflowViewSet(CreateUpdateModelAMixin, ModelViewSet):
@ -117,10 +117,10 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
if key not in ticket_data or not ticket_data[key]:
raise APIException('字段{}必填'.format(key))
ticket = serializer.save(state=start_state) # 先创建出来
next_state = WfService.get_next_state_id_by_transition_and_ticket_info(ticket=ticket, transition=transition)
next_state = WfService.get_next_state_by_transition_and_ticket_info(ticket=ticket, transition=transition)
participant_info = WfService.get_ticket_state_participant_info(state=next_state, ticket=ticket, ticket_data=ticket.ticket_data)
destination_participant_type = participant_info.get('destination_participant_type', 0)
destination_participant = participant_info.get('destination_participant', '')
destination_participant = participant_info.get('destination_participant', None)
multi_all_person = participant_info.get('multi_all_person', {}) # 多人需要全部处理情况
sn = WfService.get_ticket_sn(ticket.workflow) # 流水号
if next_state.type == State.STATE_TYPE_END:
@ -144,9 +144,9 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
ticket.multi_all_person=multi_all_person
ticket.save()
# 新增流转记录
TicketFlow.objects.create(ticket=ticket, state=start_state, ticket_data=ticket_data,
TicketFlow.objects.create(ticket=ticket, state=start_state, ticket_data=WfService.get_ticket_all_field_value(ticket),
suggestion=rdata.get('suggestion',''), participant_type=State.PARTICIPANT_TYPE_PERSONAL,
participant=ticket.create_by, transition=transition)
participant=ticket.create_by, transition=transition, create_by=request.user)
return Response(TicketSerializer(instance=ticket).data)
@ -160,7 +160,72 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
except:
raise APIException('工单不存在')
data = request.data
result = WfService.ticket_handle_permission_check()
result = WfService.ticket_handle_permission_check(ticket, request.user)
source_state = ticket.state
source_ticket_data = ticket.ticket_data
if result.get('permission') is False:
raise PermissionDenied(result.get('msg'))
# 校验表单必填项目
transition = Transition.objects.get(pk=data['transition'])
ticket_data = data['ticket_data']
if transition.field_require_check:
for key, value in ticket.state.state_fields.items():
if value == State.STATE_FIELD_REQUIRED:
if key not in ticket_data or not ticket_data[key]:
raise APIException('字段{}必填'.format(key))
destination_state = WfService.get_next_state_by_transition_and_ticket_info(ticket, transition)
multi_all_person = ticket.multi_all_person
if multi_all_person:
multi_all_person[request.user.id] =dict(transition=transition.id)
# 判断所有人处理结果是否一致
if WfService.check_dict_has_all_same_value(multi_all_person):
participant_info = WfService.get_ticket_state_participant_info(destination_state, ticket, data['ticket_data'])
destination_participant_type = participant_info.get('destination_participant_type', 0)
destination_participant = participant_info.get('destination_participant', None)
multi_all_person = {}
else:
# 处理人没有没有全部处理完成或者处理动作不一致
destination_participant_type = ticket.participant_type
destination_state = ticket.state # 保持原状态
destination_participant = []
for key, value in multi_all_person.items():
if not value:
destination_participant.push(key)
else:
# 当前处理人类型非全部处理
participant_info = WfService.get_ticket_state_participant_info(destination_state, ticket, data['ticket_data'])
destination_participant_type = participant_info.get('destination_participant_type', 0)
destination_participant = participant_info.get('destination_participant', None)
multi_all_person = participant_info.get('multi_all_person', {})
# 更新工单信息:基础字段及自定义字段, add_relation字段 需要下个处理人是部门、角色等的情况
ticket.state = destination_state
ticket.participant_type = destination_participant_type
ticket.participant = destination_participant
ticket.multi_all_person = multi_all_person
if destination_state.type == State.STATE_TYPE_END:
ticket.act_state = State.TICKET_ACT_STATE_FINISH
elif destination_state.type == State.STATE_TYPE_START:
ticket.act_state = State.TICKET_ACT_STATE_DRAFT
else:
ticket.act_state = State.TICKET_ACT_STATE_ONGOING
if transition.attribute_type == State.TRANSITION_ATTRIBUTE_TYPE_REFUSE:
transition.act_state = State.TICKET_ACT_STATE_BACK
# 只更新必填和可选的字段
for key, value in ticket.state.state_fields.items():
if value in (State.STATE_FIELD_REQUIRED, State.STATE_FIELD_OPTIONAL):
source_ticket_data[key] = ticket_data[key]
ticket.ticket_data = source_ticket_data
ticket.save()
# 更新工单流转记录
TicketFlow.objects.create(ticket=ticket, state=source_state, ticket_data=WfService.get_ticket_all_field_value(ticket),
suggestion=data.get('suggestion',''), participant_type=State.PARTICIPANT_TYPE_PERSONAL,
participant=request.user, transition=transition, create_by=request.user)
return Response(TicketSerializer(instance=ticket).data)
@action(methods=['get'], detail=True, perms_map={'get':'*'})
def flowsteps(self, request, pk=None):