From 207ef134947544a68cd1de1ffd51f5944d579aff Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 29 Sep 2021 10:17:42 +0800 Subject: [PATCH 01/32] =?UTF-8?q?=E8=BF=94=E5=9B=9Eticket=20data=E5=88=97?= =?UTF-8?q?=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wf/models.py | 2 +- hb_server/apps/wf/serializers.py | 37 ++++++++++++++++++++++++++++++++ hb_server/apps/wf/services.py | 2 ++ hb_server/apps/wf/views.py | 6 +++++- 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/hb_server/apps/wf/models.py b/hb_server/apps/wf/models.py index c4cfc29..a665d04 100644 --- a/hb_server/apps/wf/models.py +++ b/hb_server/apps/wf/models.py @@ -72,7 +72,7 @@ class State(CommonAModel): enable_retreat = models.BooleanField('允许撤回', default=False, help_text='开启后允许工单创建人在此状态直接撤回工单到初始状态') participant_type = models.IntegerField('参与者类型', choices=type2_choices, default=1, blank=True, help_text='0.无处理人,1.个人,2.多人,3.部门,4.角色,5.变量(支持工单创建人,创建人的leader),6.脚本,7.工单的字段内容(如表单中的"测试负责人",需要为用户名或者逗号隔开的多个用户名),8.父工单的字段内容。 初始状态请选择类型5,参与人填create_by') participant = models.JSONField('参与者', default=list, blank=True, help_text='可以为空(无处理人的情况,如结束状态)、userid、userid列表\部门id\角色id\变量(create_by,create_by_tl)\脚本记录的id等,包含子工作流的需要设置处理人为loonrobot') - state_fields = models.JSONField('表单字段', default=dict, help_text='json格式字典存储,包括读写属性1:只读,2:必填,3:可选. 示例:{"created_at":1,"title":2, "sn":1}, 内置特殊字段participant_info.participant_name:当前处理人信息(部门名称、角色名称),state.state_name:当前状态的状态名,workflow.workflow_name:工作流名称') # json格式存储,包括读写属性1:只读,2:必填,3:可选,4:不显示, 字典的字典 + state_fields = models.JSONField('表单字段', default=dict, help_text='json格式字典存储,包括读写属性1:只读,2:必填,3:可选. 示例:{"create_time":1,"title":2, "sn":1}, 内置特殊字段participant_info.participant_name:当前处理人信息(部门名称、角色名称),state.state_name:当前状态的状态名,workflow.workflow_name:工作流名称') # json格式存储,包括读写属性1:只读,2:必填,3:可选,4:不显示, 字典的字典 distribute_type = models.IntegerField('分配方式', default=1, choices=state_distribute_choices, help_text='1.主动接单(如果当前处理人实际为多人的时候,需要先接单才能处理) 2.直接处理(即使当前处理人实际为多人,也可以直接处理) 3.随机分配(如果实际为多人,则系统会随机分配给其中一个人) 4.全部处理(要求所有参与人都要处理一遍,才能进入下一步)') class Transition(CommonAModel): diff --git a/hb_server/apps/wf/serializers.py b/hb_server/apps/wf/serializers.py index e674c31..5199aad 100644 --- a/hb_server/apps/wf/serializers.py +++ b/hb_server/apps/wf/serializers.py @@ -71,6 +71,43 @@ class TicketSerializer(serializers.ModelSerializer): queryset = queryset.select_related('workflow','state') return queryset +class TicketListSerializer(serializers.ModelSerializer): + workflow_ = WorkflowSimpleSerializer(source='workflow', read_only=True) + state_ = StateSimpleSerializer(source='state', read_only=True) + + class Meta: + model = Ticket + fields = ['id', 'title', 'sn', 'workflow', 'workflow_', 'state', 'state_', 'act_state'] + + @staticmethod + def setup_eager_loading(queryset): + queryset = queryset.select_related('workflow','state') + return queryset + +class TicketDetailSerializer(serializers.ModelSerializer): + workflow_ = WorkflowSimpleSerializer(source='workflow', read_only=True) + state_ = StateSimpleSerializer(source='state', read_only=True) + ticket_data_ = serializers.SerializerMethodField() + class Meta: + model = Ticket + fields = '__all__' + + @staticmethod + def setup_eager_loading(queryset): + queryset = queryset.select_related('workflow','state') + return queryset + + def get_ticket_data_(self, obj): + ticket_data = obj.ticket_data + state_fields = obj.state.state_fields + all_fields = CustomField.objects.filter(workflow=obj.workflow).order_by('sort') + all_fields_l = CustomFieldSerializer(instance=all_fields, many=True).data + for i in all_fields_l: + key = i['field_key'] + i['field_state'] = state_fields.get(key, 1) + i['field_value'] = ticket_data.get(key, None) + return all_fields_l + class TicketFlowSerializer(serializers.ModelSerializer): participant_ = UserSimpleSerializer(source='participant', read_only=True) state_ = StateSimpleSerializer(source='state', read_only=True) diff --git a/hb_server/apps/wf/services.py b/hb_server/apps/wf/services.py index bee8799..dd206ee 100644 --- a/hb_server/apps/wf/services.py +++ b/hb_server/apps/wf/services.py @@ -1,3 +1,4 @@ +from apps.wf.serializers import CustomFieldSerializer from apps.wf.serializers import TicketSerializer, TicketSimpleSerializer from typing import Tuple from apps.system.models import User @@ -212,3 +213,4 @@ class WfService(object): return field_info_dict + diff --git a/hb_server/apps/wf/views.py b/hb_server/apps/wf/views.py index 9df0518..0eec47a 100644 --- a/hb_server/apps/wf/views.py +++ b/hb_server/apps/wf/views.py @@ -2,7 +2,7 @@ from django.core.exceptions import AppRegistryNotReady from rest_framework.response import Response from rest_framework import serializers from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin -from apps.wf.serializers import CustomFieldSerializer, StateSerializer, TicketCreateSerializer, TicketFlowSerializer, TicketHandleSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer +from apps.wf.serializers import CustomFieldSerializer, StateSerializer, TicketCreateSerializer, TicketFlowSerializer, TicketHandleSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer, TicketListSerializer, TicketDetailSerializer from django.shortcuts import get_object_or_404, render from rest_framework.viewsets import GenericViewSet, ModelViewSet from rest_framework.decorators import action, api_view @@ -101,6 +101,10 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin return TicketCreateSerializer elif self.action == 'handle': return TicketHandleSerializer + elif self.action == 'list': + return TicketListSerializer + elif self.action == 'retrieve': + return TicketDetailSerializer return super().get_serializer_class() def create(self, request, *args, **kwargs): From 89d5eb06861e530a35baf29b534006f6e5f775b2 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 29 Sep 2021 11:40:31 +0800 Subject: [PATCH 02/32] inventory serializer --- hb_server/apps/inm/views.py | 11 ++++------- hb_server/apps/wf/serializers.py | 2 +- hb_server/apps/wf/views.py | 14 ++++++++++++++ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/hb_server/apps/inm/views.py b/hb_server/apps/inm/views.py index 4c3b8c4..e8e8754 100644 --- a/hb_server/apps/inm/views.py +++ b/hb_server/apps/inm/views.py @@ -1,5 +1,6 @@ from django.shortcuts import render -from rest_framework.viewsets import ModelViewSet +from rest_framework.mixins import ListModelMixin +from rest_framework.viewsets import GenericViewSet, ModelViewSet from apps.inm.models import WareHouse,Inventory from apps.inm.serializers import WareHouseSerializer, WareHouseCreateUpdateSerializer,InventorySerializer,InventoryCreateUpdateSerializer @@ -23,7 +24,8 @@ class WarehouseViewSet(CreateUpdateModelAMixin, ModelViewSet): if self.action in ['create', 'update']: return WareHouseCreateUpdateSerializer return WareHouseSerializer -class InventoryViewSet(CreateUpdateModelAMixin, ModelViewSet): + +class InventoryViewSet(ListModelMixin, GenericViewSet): """ 物料基本信息-增删改查 """ @@ -33,8 +35,3 @@ class InventoryViewSet(CreateUpdateModelAMixin, ModelViewSet): filterset_fields = [] ordering_fields = ['create_time'] ordering = ['-create_time'] - - def get_serializer_class(self): - if self.action in ['create', 'update']: - return InventoryCreateUpdateSerializer - return InventorySerializer diff --git a/hb_server/apps/wf/serializers.py b/hb_server/apps/wf/serializers.py index 5199aad..84c7862 100644 --- a/hb_server/apps/wf/serializers.py +++ b/hb_server/apps/wf/serializers.py @@ -77,7 +77,7 @@ class TicketListSerializer(serializers.ModelSerializer): class Meta: model = Ticket - fields = ['id', 'title', 'sn', 'workflow', 'workflow_', 'state', 'state_', 'act_state'] + fields = ['id', 'title', 'sn', 'workflow', 'workflow_', 'state', 'state_', 'act_state', 'distribute_type'] @staticmethod def setup_eager_loading(queryset): diff --git a/hb_server/apps/wf/views.py b/hb_server/apps/wf/views.py index 0eec47a..efe8077 100644 --- a/hb_server/apps/wf/views.py +++ b/hb_server/apps/wf/views.py @@ -107,6 +107,20 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin return TicketDetailSerializer return super().get_serializer_class() + def list(self, request, *args, **kwargs): + category = request.query_params.get('category', None) + if category and category in ['all', 'owner', 'duty', 'worked', 'relation']: + user = request.user + if category == 'owner': + self.queryset = Ticket.objects.filter(create_by=user) + elif category == 'duty': + self.queryset = Ticket.objects.filter(participant__contains=user.id).exclude(act_state__in=[Ticket.TICKET_ACT_STATE_FINISH, Ticket.TICKET_ACT_STATE_CLOSED]) + + else: + raise APIException('查询分类错误') + return super().list(request, *args, **kwargs) + + def create(self, request, *args, **kwargs): """ 新建工单 From cc8c409f190b062fb9df7bbc91edef4ebd6bfc5c Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 30 Sep 2021 09:32:58 +0800 Subject: [PATCH 03/32] =?UTF-8?q?=E6=8C=87=E5=AE=9A=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E5=88=86=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wf/filters.py | 22 ++++++++++++++++++++++ hb_server/apps/wf/models.py | 11 +++++++++-- hb_server/apps/wf/serializers.py | 4 ++-- hb_server/apps/wf/views.py | 20 ++++++-------------- 4 files changed, 39 insertions(+), 18 deletions(-) create mode 100644 hb_server/apps/wf/filters.py diff --git a/hb_server/apps/wf/filters.py b/hb_server/apps/wf/filters.py new file mode 100644 index 0000000..0fbde88 --- /dev/null +++ b/hb_server/apps/wf/filters.py @@ -0,0 +1,22 @@ +from django_filters import rest_framework as filters +from .models import Ticket +class TicketFilterSet(filters.FilterSet): + start_create = filters.DateFilter(field_name="create_time", lookup_expr='gte') + end_create = filters.DateFilter(field_name="create_time", lookup_expr='lte') + category = filters.ChoiceFilter(choices = Ticket.category_choices, method='filter_category') + + class Meta: + model = Ticket + fields = ['workflow', 'state', 'act_state', 'start_create', 'end_create', 'category'] + + def filter_category(self, queryset, name, value): + user=self.request.user + if value == 'owner': + queryset = queryset.filter(create_by=user) + elif value == 'duty': + queryset = queryset.filter(participant__contains=user.id).exclude(act_state__in=[Ticket.TICKET_ACT_STATE_FINISH, Ticket.TICKET_ACT_STATE_CLOSED]) + elif value == 'worked': + queryset = queryset.filter(ticketflow_ticket__participant=user).exclude(create_by=user) + else: + queryset = queryset.none() + return queryset \ No newline at end of file diff --git a/hb_server/apps/wf/models.py b/hb_server/apps/wf/models.py index a665d04..8328153 100644 --- a/hb_server/apps/wf/models.py +++ b/hb_server/apps/wf/models.py @@ -149,6 +149,13 @@ class Ticket(CommonAModel): (TICKET_ACT_STATE_FINISH, '已完成'), (TICKET_ACT_STATE_CLOSED, '已关闭') ) + category_choices =( + ('all', '全部'), + ('owner', '我创建的'), + ('duty', '代办'), + ('worked', '我处理的'), + ('relation', '抄送我的') + ) title = models.CharField('标题', max_length=500, blank=True, default='', help_text="工单标题") workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='关联工作流') sn = models.CharField('流水号', max_length=25, help_text="工单的流水号") @@ -168,10 +175,10 @@ class TicketFlow(BaseModel): """ 工单流转日志 """ - ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, verbose_name='关联工单') + ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, verbose_name='关联工单', related_name='ticketflow_ticket') transition = models.ForeignKey(Transition, verbose_name='流转id', help_text='与worklow.Transition关联, 为0时表示认为干预的操作', on_delete=models.CASCADE, null=True, blank=True) suggestion = models.CharField('处理意见', max_length=10000, default='', blank=True) participant_type = models.IntegerField('处理人类型', default=0, help_text='0.无处理人,1.个人,2.多人', choices=State.type2_choices) - participant = models.ForeignKey(User, verbose_name='处理人', on_delete=models.SET_NULL, null=True, blank=True) + participant = models.ForeignKey(User, verbose_name='处理人', on_delete=models.SET_NULL, null=True, blank=True, related_name='ticketflow_participant') state = models.ForeignKey(State, verbose_name='当前状态', default=0, blank=True, on_delete=models.CASCADE) ticket_data = models.JSONField('工单数据', default=dict, blank=True, help_text='可以用于记录当前表单数据,json格式') \ No newline at end of file diff --git a/hb_server/apps/wf/serializers.py b/hb_server/apps/wf/serializers.py index 84c7862..8df3742 100644 --- a/hb_server/apps/wf/serializers.py +++ b/hb_server/apps/wf/serializers.py @@ -23,7 +23,7 @@ class WorkflowSimpleSerializer(serializers.ModelSerializer): class StateSimpleSerializer(serializers.ModelSerializer): class Meta: model = State - fields = ['id', 'name', 'type'] + fields = ['id', 'name', 'type', 'distribute_type'] class TransitionSerializer(serializers.ModelSerializer): source_state_ = StateSimpleSerializer(source='source_state', read_only=True) @@ -77,7 +77,7 @@ class TicketListSerializer(serializers.ModelSerializer): class Meta: model = Ticket - fields = ['id', 'title', 'sn', 'workflow', 'workflow_', 'state', 'state_', 'act_state', 'distribute_type'] + fields = ['id', 'title', 'sn', 'workflow', 'workflow_', 'state', 'state_', 'act_state'] @staticmethod def setup_eager_loading(queryset): diff --git a/hb_server/apps/wf/views.py b/hb_server/apps/wf/views.py index efe8077..4a00884 100644 --- a/hb_server/apps/wf/views.py +++ b/hb_server/apps/wf/views.py @@ -1,3 +1,4 @@ +from apps.wf.filters import TicketFilterSet from django.core.exceptions import AppRegistryNotReady from rest_framework.response import Response from rest_framework import serializers @@ -93,7 +94,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin queryset = Ticket.objects.all() serializer_class = TicketSerializer search_fields = ['title'] - filterset_fields = ['workflow', 'state'] + filterset_class = TicketFilterSet ordering = ['-create_time'] def get_serializer_class(self): @@ -106,20 +107,11 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin elif self.action == 'retrieve': return TicketDetailSerializer return super().get_serializer_class() - - def list(self, request, *args, **kwargs): - category = request.query_params.get('category', None) - if category and category in ['all', 'owner', 'duty', 'worked', 'relation']: - user = request.user - if category == 'owner': - self.queryset = Ticket.objects.filter(create_by=user) - elif category == 'duty': - self.queryset = Ticket.objects.filter(participant__contains=user.id).exclude(act_state__in=[Ticket.TICKET_ACT_STATE_FINISH, Ticket.TICKET_ACT_STATE_CLOSED]) - - else: - raise APIException('查询分类错误') - return super().list(request, *args, **kwargs) + def get_queryset(self): + if self.action=='list' and (not self.request.query_params.get('category', None)): + raise APIException('请指定查询分类') + return super().get_queryset() def create(self, request, *args, **kwargs): """ From 8a43ad7ee751c9deab222fc4baaf200dde47d368 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 30 Sep 2021 09:57:06 +0800 Subject: [PATCH 04/32] =?UTF-8?q?ticket=20=E6=9F=A5=E8=AF=A2=E5=88=86?= =?UTF-8?q?=E7=B1=BBall?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wf/filters.py | 2 ++ .../wf/migrations/0011_auto_20210930_0954.py | 31 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 hb_server/apps/wf/migrations/0011_auto_20210930_0954.py diff --git a/hb_server/apps/wf/filters.py b/hb_server/apps/wf/filters.py index 0fbde88..7e2990c 100644 --- a/hb_server/apps/wf/filters.py +++ b/hb_server/apps/wf/filters.py @@ -17,6 +17,8 @@ class TicketFilterSet(filters.FilterSet): queryset = queryset.filter(participant__contains=user.id).exclude(act_state__in=[Ticket.TICKET_ACT_STATE_FINISH, Ticket.TICKET_ACT_STATE_CLOSED]) elif value == 'worked': queryset = queryset.filter(ticketflow_ticket__participant=user).exclude(create_by=user) + elif value == 'all': + pass else: queryset = queryset.none() return queryset \ No newline at end of file diff --git a/hb_server/apps/wf/migrations/0011_auto_20210930_0954.py b/hb_server/apps/wf/migrations/0011_auto_20210930_0954.py new file mode 100644 index 0000000..fedb874 --- /dev/null +++ b/hb_server/apps/wf/migrations/0011_auto_20210930_0954.py @@ -0,0 +1,31 @@ +# Generated by Django 3.2.6 on 2021-09-30 01:54 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('wf', '0010_alter_ticketflow_transition'), + ] + + operations = [ + migrations.AlterField( + model_name='state', + name='state_fields', + field=models.JSONField(default=dict, help_text='json格式字典存储,包括读写属性1:只读,2:必填,3:可选. 示例:{"create_time":1,"title":2, "sn":1}, 内置特殊字段participant_info.participant_name:当前处理人信息(部门名称、角色名称),state.state_name:当前状态的状态名,workflow.workflow_name:工作流名称', verbose_name='表单字段'), + ), + migrations.AlterField( + model_name='ticketflow', + name='participant', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='ticketflow_participant', to=settings.AUTH_USER_MODEL, verbose_name='处理人'), + ), + migrations.AlterField( + model_name='ticketflow', + name='ticket', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ticketflow_ticket', to='wf.ticket', verbose_name='关联工单'), + ), + ] From bb53fbe220d302ac98d7436d2a95eb5201155505 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 30 Sep 2021 10:03:15 +0800 Subject: [PATCH 05/32] =?UTF-8?q?=E5=88=9B=E5=BB=BA=E5=92=8C=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wf/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hb_server/apps/wf/serializers.py b/hb_server/apps/wf/serializers.py index 8df3742..4d6b770 100644 --- a/hb_server/apps/wf/serializers.py +++ b/hb_server/apps/wf/serializers.py @@ -77,7 +77,7 @@ class TicketListSerializer(serializers.ModelSerializer): class Meta: model = Ticket - fields = ['id', 'title', 'sn', 'workflow', 'workflow_', 'state', 'state_', 'act_state'] + fields = ['id', 'title', 'sn', 'workflow', 'workflow_', 'state', 'state_', 'act_state', 'create_time', 'update_time'] @staticmethod def setup_eager_loading(queryset): From 050d4e5bda391296ad2ae6eb139d96592c0fc28e Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 30 Sep 2021 16:09:39 +0800 Subject: [PATCH 06/32] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=9D=A1=E4=BB=B6?= =?UTF-8?q?=E6=B5=81=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wf/serializers.py | 2 +- hb_server/apps/wf/services.py | 10 ++++++++-- hb_server/apps/wf/views.py | 6 +++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/hb_server/apps/wf/serializers.py b/hb_server/apps/wf/serializers.py index 4d6b770..81512ed 100644 --- a/hb_server/apps/wf/serializers.py +++ b/hb_server/apps/wf/serializers.py @@ -77,7 +77,7 @@ class TicketListSerializer(serializers.ModelSerializer): class Meta: model = Ticket - fields = ['id', 'title', 'sn', 'workflow', 'workflow_', 'state', 'state_', 'act_state', 'create_time', 'update_time'] + fields = ['id', 'title', 'sn', 'workflow', 'workflow_', 'state', 'state_', 'act_state', 'create_time', 'update_time', 'participant_type'] @staticmethod def setup_eager_loading(queryset): diff --git a/hb_server/apps/wf/services.py b/hb_server/apps/wf/services.py index dd206ee..a3fc2b8 100644 --- a/hb_server/apps/wf/services.py +++ b/hb_server/apps/wf/services.py @@ -91,7 +91,7 @@ class WfService(object): @classmethod - def get_next_state_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, ticket_data:dict={})->object: """ 获取下个节点状态 """ @@ -103,8 +103,14 @@ class WfService(object): # raise APIException('流转错误') source_state = ticket.state destination_state = transition.destination_state + ticket_all_value = cls.get_ticket_all_field_value(ticket) + ticket_all_value.update(**ticket_data) if transition.condition_expression: - pass + for i in transition.condition_expression: + expression = i['expression'].format(**ticket_all_value) + import datetime, time # 用于支持条件表达式中对时间的操作 + if eval(expression): + destination_state = State.objects.get(i['expression'].get('target')) return destination_state @classmethod diff --git a/hb_server/apps/wf/views.py b/hb_server/apps/wf/views.py index 4a00884..cd33e99 100644 --- a/hb_server/apps/wf/views.py +++ b/hb_server/apps/wf/views.py @@ -128,7 +128,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin if value == State.STATE_FIELD_REQUIRED: if key not in ticket_data or not ticket_data[key]: raise APIException('字段{}必填'.format(key)) - ticket = serializer.save(state=start_state, create_by=request.user) # 先创建出来 + ticket = serializer.save(state=start_state, create_by=request.user, act_state=Ticket.TICKET_ACT_STATE_DRAFT) # 先创建出来 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) @@ -162,7 +162,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin return Response(TicketSerializer(instance=ticket).data) - @action(methods=['post'], detail=True, perms_map={'get':'*'}) + @action(methods=['post'], detail=True, perms_map={'post':'*'}) def handle(self, request, pk=None): """ 处理工单 @@ -185,7 +185,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin 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) + destination_state = WfService.get_next_state_by_transition_and_ticket_info(ticket, transition, ticket_data) multi_all_person = ticket.multi_all_person if multi_all_person: multi_all_person[request.user.id] =dict(transition=transition.id) From 016f3166418e5b73fa848f9d7f53bb6ab91c363b Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 30 Sep 2021 16:22:11 +0800 Subject: [PATCH 07/32] =?UTF-8?q?=E6=9D=A1=E4=BB=B6=E6=B5=81=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wf/services.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/hb_server/apps/wf/services.py b/hb_server/apps/wf/services.py index a3fc2b8..28e7a08 100644 --- a/hb_server/apps/wf/services.py +++ b/hb_server/apps/wf/services.py @@ -95,12 +95,6 @@ class WfService(object): """ 获取下个节点状态 """ - # if ticket: # 如果是新建工单 - # source_state = ticket.state - # else: - # source_state = cls.get_workflow_start_state(workflow) - # if transition.source_state != source_state: - # raise APIException('流转错误') source_state = ticket.state destination_state = transition.destination_state ticket_all_value = cls.get_ticket_all_field_value(ticket) @@ -110,7 +104,7 @@ class WfService(object): expression = i['expression'].format(**ticket_all_value) import datetime, time # 用于支持条件表达式中对时间的操作 if eval(expression): - destination_state = State.objects.get(i['expression'].get('target')) + destination_state = State.objects.get(i['expression'].get('target_state')) return destination_state @classmethod From 33db917a06cec2a679f7472823440b889736d9f8 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 30 Sep 2021 16:40:00 +0800 Subject: [PATCH 08/32] =?UTF-8?q?=E6=8E=A5=E5=8D=95bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wf/services.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hb_server/apps/wf/services.py b/hb_server/apps/wf/services.py index 28e7a08..2f91c58 100644 --- a/hb_server/apps/wf/services.py +++ b/hb_server/apps/wf/services.py @@ -164,21 +164,21 @@ class WfService(object): state = ticket.state if participant_type == State.PARTICIPANT_TYPE_PERSONAL: if user.id != participant: - return dict(permission=False, msg="非当前处理人") + return dict(permission=False, msg="非当前处理人", need_accept=False) elif participant_type in [State.PARTICIPANT_TYPE_MULTI, State.PARTICIPANT_TYPE_DEPT, State.PARTICIPANT_TYPE_ROLE]: if user.id not in participant: - return dict(permission=False, msg="非当前处理人") + return dict(permission=False, msg="非当前处理人", need_accept=False) current_participant_count = len(participant) if current_participant_count == 1: - if [user.id] != participant: + if [user.id] != participant or user.id != participant: return dict(permission=False, msg="非当前处理人") elif current_participant_count >1 and state.distribute_type == State.STATE_DISTRIBUTE_TYPE_ACTIVE: if user.id not in participant: - return dict(permission=False, msg="非当前处理人") + return dict(permission=False, msg="非当前处理人", need_accept=False) return dict(permission=False, msg="需要先接单再处理", need_accept=True) if ticket.in_add_node: - return dict(permission=False, msg="工单当前处于加签中,请加签完成后操作") - return dict(permission=True, msg="") + return dict(permission=False, msg="工单当前处于加签中,请加签完成后操作", need_accept=False) + return dict(permission=True, msg="", need_accept=False) @classmethod def check_dict_has_all_same_value(cls, dict_obj: object)->tuple: From 36924b43adb893f148e12a999fc3242bbf79044c Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 30 Sep 2021 16:54:23 +0800 Subject: [PATCH 09/32] =?UTF-8?q?=E6=8E=A5=E5=8D=95=E4=B9=8B=E5=90=8E?= =?UTF-8?q?=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wf/services.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hb_server/apps/wf/services.py b/hb_server/apps/wf/services.py index 2f91c58..7fd7026 100644 --- a/hb_server/apps/wf/services.py +++ b/hb_server/apps/wf/services.py @@ -170,8 +170,10 @@ class WfService(object): return dict(permission=False, msg="非当前处理人", need_accept=False) current_participant_count = len(participant) if current_participant_count == 1: - if [user.id] != participant or user.id != participant: - return dict(permission=False, msg="非当前处理人") + if [user.id] == participant or user.id == participant: + pass + else: + return dict(permission=False, msg="非当前处理人", need_accept=False) elif current_participant_count >1 and state.distribute_type == State.STATE_DISTRIBUTE_TYPE_ACTIVE: if user.id not in participant: return dict(permission=False, msg="非当前处理人", need_accept=False) From 35ab5f1c791963aa9f970a6a8d730fd4732daf56 Mon Sep 17 00:00:00 2001 From: shijing Date: Fri, 8 Oct 2021 08:39:16 +0800 Subject: [PATCH 10/32] workflowRefresh --- hb_client/src/api/workflow.js | 8 + hb_client/src/views/workflow/state.vue | 61 +++- hb_client/src/views/workflow/ticket.vue | 435 ++++++++++++++++-------- 3 files changed, 363 insertions(+), 141 deletions(-) diff --git a/hb_client/src/api/workflow.js b/hb_client/src/api/workflow.js index 5fd2b05..0b11d69 100644 --- a/hb_client/src/api/workflow.js +++ b/hb_client/src/api/workflow.js @@ -154,6 +154,14 @@ export function createTicket(data) { data }) } +//接单 +export function ticketAccpet(id,data) { + return request({ + url: `/wf/ticket/${id}/accpet/`, + method: 'post', + data + }) +} //工单详情 export function getTicketDetail(id) { return request({ diff --git a/hb_client/src/views/workflow/state.vue b/hb_client/src/views/workflow/state.vue index 7895dca..6c12777 100644 --- a/hb_client/src/views/workflow/state.vue +++ b/hb_client/src/views/workflow/state.vue @@ -148,6 +148,28 @@ + + 添加修改 + + + + + + + + + + + + + + + + + + + +
取消 @@ -157,7 +179,7 @@
+ + \ No newline at end of file + diff --git a/hb_client/src/views/workflow/index.vue b/hb_client/src/views/workflow/index.vue index c92c762..40d6889 100644 --- a/hb_client/src/views/workflow/index.vue +++ b/hb_client/src/views/workflow/index.vue @@ -50,13 +50,13 @@ {{ !!(scope.row.view_permission_check)?'是':'否' }} - + @@ -87,6 +91,21 @@ @pagination="getList" /> +
+
+
工作流流程图
+ + +

工作流名称 :{{watchedName}}

+
+ +

创建时间 :{{watchedCreateTime}}

+
+
+ + +
+
@@ -139,12 +158,14 @@ + + diff --git a/hb_client/src/views/workflow/ticket.vue b/hb_client/src/views/workflow/ticket.vue index 77078da..dd781ae 100644 --- a/hb_client/src/views/workflow/ticket.vue +++ b/hb_client/src/views/workflow/ticket.vue @@ -62,7 +62,7 @@
@@ -106,8 +106,6 @@ @@ -152,7 +150,7 @@ @@ -195,9 +193,8 @@ @@ -212,13 +209,19 @@
工单流程图
+ + +

工单名称 :{{watchedName}}

+
+ +

创建时间 :{{watchedCreateTime}}

+
+
- - - +
@@ -307,7 +310,7 @@ - - + + diff --git a/hb_client/src/views/workflow/transitions.vue b/hb_client/src/views/workflow/transitions.vue index a11521a..341fa2b 100644 --- a/hb_client/src/views/workflow/transitions.vue +++ b/hb_client/src/views/workflow/transitions.vue @@ -97,10 +97,14 @@ - + - import {getWfStateList, getWfTransitionList, createWfTransition,updateWfTransition,deleteWfTransition } from "@/api/workflow"; import checkPermission from "@/utils/permission"; - +import vueJsonEditor from 'vue-json-editor' import { genTree } from "@/utils" const defaultwftransition = { name: "", }; export default { - components: { }, + components: { vueJsonEditor }, name: "TST", props: ["ID"], data() { @@ -211,13 +215,11 @@ export default { this.wftransition = Object.assign({}, scope.row); // copy obj this.dialogType = "edit"; this.dialogVisible = true; - this.$nextTick(() => { - this.$refs["Form"].clearValidate(); - }); + // this.wftransition.condition_expression = JSON.stringify(scope.row.condition_expression) + // this.$nextTick(() => { + // this.$refs["Form"].clearValidate(); + // }); }, - - - async confirm(form) { this.$refs[form].validate((valid) => { if (valid) { @@ -245,8 +247,6 @@ export default { } }); }, - - handleDelete(scope) { this.$confirm("确认删除?", "警告", { confirmButtonText: "确认", @@ -262,9 +262,6 @@ export default { console.error(err); }); }, - - - }, }; From 1e7ebd672aa72fb2e9257d0de674016fe2ca87ca Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 12 Oct 2021 10:45:14 +0800 Subject: [PATCH 19/32] =?UTF-8?q?=E4=BA=A7=E5=93=81=E7=94=9F=E4=BA=A7?= =?UTF-8?q?=E5=88=86=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/mtm/migrations/0001_initial.py | 18 --- .../mtm/migrations/0019_auto_20211012_0901.py | 137 ++++++++++++++++++ hb_server/apps/mtm/models.py | 35 +++-- hb_server/apps/mtm/serializers.py | 28 ++-- hb_server/apps/mtm/urls.py | 4 +- hb_server/apps/mtm/views.py | 45 ++---- 6 files changed, 183 insertions(+), 84 deletions(-) create mode 100644 hb_server/apps/mtm/migrations/0019_auto_20211012_0901.py diff --git a/hb_server/apps/mtm/migrations/0001_initial.py b/hb_server/apps/mtm/migrations/0001_initial.py index d44f801..654ed11 100644 --- a/hb_server/apps/mtm/migrations/0001_initial.py +++ b/hb_server/apps/mtm/migrations/0001_initial.py @@ -94,24 +94,6 @@ class Migration(migrations.Migration): 'verbose_name_plural': '操作记录条目', }, ), - migrations.CreateModel( - name='ProductProcess', - fields=[ - ('id', models.BigAutoField(auto_created=True, 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='删除标记')), - ('sort', models.IntegerField(default=1, verbose_name='排序号')), - ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='productprocess_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), - ('process', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.process', verbose_name='工序')), - ('product', 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='productprocess_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), - ], - options={ - 'verbose_name': '产品生产工序', - 'verbose_name_plural': '产品生产工序', - }, - ), migrations.CreateModel( name='OutputMaterial', fields=[ diff --git a/hb_server/apps/mtm/migrations/0019_auto_20211012_0901.py b/hb_server/apps/mtm/migrations/0019_auto_20211012_0901.py new file mode 100644 index 0000000..9cc24ed --- /dev/null +++ b/hb_server/apps/mtm/migrations/0019_auto_20211012_0901.py @@ -0,0 +1,137 @@ +# Generated by Django 3.2.6 on 2021-10-12 01:01 + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('mtm', '0018_material_count'), + ] + + operations = [ + migrations.CreateModel( + name='SubProduction', + fields=[ + ('id', models.BigAutoField(auto_created=True, 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(blank=True, max_length=50, null=True, verbose_name='命名')), + ('sort', models.IntegerField(default=1, verbose_name='排序号')), + ], + options={ + 'verbose_name': '产品生产工序', + 'verbose_name_plural': '产品生产工序', + }, + ), + migrations.RemoveField( + model_name='inputmaterial', + name='create_by', + ), + migrations.RemoveField( + model_name='inputmaterial', + name='process', + ), + migrations.RemoveField( + model_name='inputmaterial', + name='product', + ), + migrations.RemoveField( + model_name='inputmaterial', + name='update_by', + ), + migrations.RemoveField( + model_name='material', + name='processes', + ), + migrations.RemoveField( + model_name='outputmaterial', + name='create_by', + ), + migrations.RemoveField( + model_name='outputmaterial', + name='process', + ), + migrations.RemoveField( + model_name='outputmaterial', + name='product', + ), + migrations.RemoveField( + model_name='outputmaterial', + name='update_by', + ), + migrations.RemoveField( + model_name='techdoc', + name='create_by', + ), + migrations.RemoveField( + model_name='techdoc', + name='process', + ), + migrations.RemoveField( + model_name='techdoc', + name='product', + ), + migrations.RemoveField( + model_name='techdoc', + name='update_by', + ), + migrations.RemoveField( + model_name='usedstep', + name='create_by', + ), + migrations.RemoveField( + model_name='usedstep', + name='process', + ), + migrations.RemoveField( + model_name='usedstep', + name='product', + ), + migrations.RemoveField( + model_name='usedstep', + name='update_by', + ), + migrations.AddField( + model_name='usedstep', + name='remark', + field=models.TextField(blank=True, null=True, verbose_name='生产备注'), + ), + migrations.AlterField( + model_name='step', + name='process', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='step_process', to='mtm.process', verbose_name='所属工序'), + ), + migrations.AddField( + model_name='subproduction', + name='product', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='产品'), + ), + migrations.AddField( + model_name='inputmaterial', + name='subproduction', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='mtm.subproduction', verbose_name='关联生产分解'), + preserve_default=False, + ), + migrations.AddField( + model_name='outputmaterial', + name='subproduction', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='mtm.subproduction', verbose_name='关联生产分解'), + preserve_default=False, + ), + migrations.AddField( + model_name='techdoc', + name='subproduction', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='mtm.subproduction', verbose_name='关联生产分解'), + preserve_default=False, + ), + migrations.AddField( + model_name='usedstep', + name='subproduction', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='mtm.subproduction', verbose_name='关联生产分解'), + preserve_default=False, + ), + ] diff --git a/hb_server/apps/mtm/models.py b/hb_server/apps/mtm/models.py index 36f2c04..1037796 100644 --- a/hb_server/apps/mtm/models.py +++ b/hb_server/apps/mtm/models.py @@ -29,7 +29,6 @@ class Material(CommonAModel): specification = models.CharField('型号', max_length=100, null=True, blank=True) type = models.CharField('物料类型', choices= type_choices, max_length=20, default=1) sort_str = models.CharField('排序字符', max_length=100, null=True, blank=True) - processes = models.JSONField('工艺流程', default=list, blank=True, null=True) unit = models.CharField('基准计量单位', choices=unit_choices, default='块', max_length=10) count = models.IntegerField('物料总数', default=0) class Meta: @@ -60,7 +59,7 @@ class Step(CommonAModel): """ 工序步骤 """ - process = models.ForeignKey(Process, on_delete=models.CASCADE, verbose_name='所属工序') + process = models.ForeignKey(Process, on_delete=models.CASCADE, verbose_name='所属工序', related_name='step_process') name = models.CharField('工序步骤名称', max_length=100) number = models.CharField('步骤编号', max_length=100, null=True, blank=True) instruction_content = models.TextField('相应操作指导', null=True, blank=True) @@ -127,26 +126,28 @@ class RecordFormField(CommonAModel): def __str__(self): return self.field_key + '-' + self.field_name -class ProductProcess(CommonAModel): + + +class SubProduction(BaseModel): """ - 产品生产工艺集 + 产品生产分解 """ + name = models.CharField('命名', max_length=50, null=True, blank=True) product = models.ForeignKey(Material, verbose_name='产品', on_delete=models.CASCADE) - process = models.ForeignKey(Process, verbose_name='工序', on_delete=models.CASCADE) sort = models.IntegerField('排序号', default=1) class Meta: verbose_name = '产品生产工序' verbose_name_plural = verbose_name -class InputMaterial(CommonAModel): + +class InputMaterial(BaseModel): """ 输入物料 """ material = models.ForeignKey(Material, verbose_name='输入物料', on_delete=models.CASCADE, related_name='inputmaterial') count = models.FloatField('消耗量', default=1) - product = models.ForeignKey(Material, verbose_name='关联产品', on_delete=models.CASCADE, related_name='inputmaterial_product') - process = models.ForeignKey(Process, verbose_name='关联工序', on_delete=models.CASCADE) + subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE) sort = models.IntegerField('排序号', default=1) class Meta: @@ -155,41 +156,39 @@ class InputMaterial(CommonAModel): -class OutputMaterial(CommonAModel): +class OutputMaterial(BaseModel): """ 输出物料 """ material = models.ForeignKey(Material, verbose_name='输出物料', on_delete=models.CASCADE, related_name='outputmaterial') count = models.FloatField('产出量', default=1) - product = models.ForeignKey(Material, verbose_name='关联产品', on_delete=models.CASCADE, related_name='outputmaterial_product') - process = models.ForeignKey(Process, verbose_name='关联工序', on_delete=models.CASCADE) + subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE) sort = models.IntegerField('排序号', default=1) class Meta: verbose_name = '输出物料' verbose_name_plural = verbose_name -class UsedStep(CommonAModel): +class UsedStep(BaseModel): """ - 产品生产子工序 + 涉及的生产子工序 """ step = models.ForeignKey(Step, verbose_name='子工序', on_delete=models.CASCADE, related_name='usedsteps') - product = models.ForeignKey(Material, verbose_name='关联产品', on_delete=models.CASCADE) - process = models.ForeignKey(Process, verbose_name='关联工序', on_delete=models.CASCADE) + remark = models.TextField('生产备注', null=True, blank=True) + subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE) class Meta: verbose_name = '产品生产子工序' verbose_name_plural = verbose_name -class TechDoc(CommonAModel): +class TechDoc(BaseModel): """ 技术文件 """ name = models.CharField('名称', max_length=50) file = models.ForeignKey(File, verbose_name='技术文件', on_delete=models.CASCADE) - product = models.ForeignKey(Material, verbose_name='关联产品', on_delete=models.CASCADE) - process = models.ForeignKey(Process, verbose_name='关联工序', on_delete=models.CASCADE) + subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE) content = models.TextField('内容', null=True, blank=True) class Meta: diff --git a/hb_server/apps/mtm/serializers.py b/hb_server/apps/mtm/serializers.py index 6a1dd98..e6ab035 100644 --- a/hb_server/apps/mtm/serializers.py +++ b/hb_server/apps/mtm/serializers.py @@ -1,7 +1,7 @@ from apps.em.serializers import EquipmentSimpleSerializer from rest_framework import serializers from rest_framework.exceptions import ParseError, ValidationError -from .models import InputMaterial, Material, OutputMaterial, Process, ProductProcess, RecordForm, RecordFormField, Step, TechDoc, UsedStep +from .models import InputMaterial, Material, OutputMaterial, Process, RecordForm, RecordFormField, Step, TechDoc, UsedStep, SubProduction from apps.system.serializers import FileSimpleSerializer, OrganizationSimpleSerializer @@ -18,7 +18,8 @@ class MaterialDetailSerializer(serializers.ModelSerializer): fields = '__all__' def get_processes_(self, obj): - objs = Process.objects.filter(id__in=obj.processes).order_by('number') + steps = UsedStep.objects.filter(subproducation__product=obj) + objs = Process.objects.filter(step_process__in=steps).order_by('number') return ProcessSimpleSerializer(instance=objs, many=True).data @@ -60,18 +61,11 @@ class StepDetailSerializer(serializers.ModelSerializer): queryset = queryset.prefetch_related('equipments') return queryset -class ProductProcessListSerializer(serializers.ModelSerializer): - process_ = ProcessSimpleSerializer(source='process', read_only=True) - product_ = MaterialSimpleSerializer(source='product', read_only=True) +class SubProductionSerializer(serializers.ModelSerializer): class Meta: - model = ProductProcess + model = SubProduction fields = '__all__' -class ProductProcessUpdateSerializer(serializers.ModelSerializer): - class Meta: - model = ProductProcess - fields = ['sort'] - class InputMaterialListSerializer(serializers.ModelSerializer): material_ = MaterialSimpleSerializer(source='material', read_only=True) class Meta: @@ -88,10 +82,10 @@ class OutputMaterialListSerializer(serializers.ModelSerializer): class InputMaterialSerializer(serializers.ModelSerializer): class Meta: model = InputMaterial - fields = ['count', 'sort', 'material', 'product', 'process'] + fields = ['count', 'sort', 'material', 'subproduction'] def create(self, validated_data): - if InputMaterial.objects.filter(material=validated_data['material'], product=validated_data['product'], process=validated_data['process'], is_deleted=False).exists(): + if InputMaterial.objects.filter(material=validated_data['material'], subproduction=validated_data['subproduction'], is_deleted=False).exists(): raise ValidationError('该物料已存在') return super().create(validated_data) @@ -103,10 +97,10 @@ class InputMaterialUpdateSerializer(serializers.ModelSerializer): class OutputMaterialSerializer(serializers.ModelSerializer): class Meta: model = OutputMaterial - fields = ['count', 'sort', 'material', 'product', 'process'] + fields = ['count', 'sort', 'material', 'subproduction'] def create(self, validated_data): - if OutputMaterial.objects.filter(material=validated_data['material'], product=validated_data['product'], process=validated_data['process'], is_deleted=False).exists(): + if OutputMaterial.objects.filter(material=validated_data['material'], subproduction=validated_data['subproduction'], is_deleted=False).exists(): raise ValidationError('该物料已存在') return super().create(validated_data) @@ -121,7 +115,7 @@ class UsedStepCreateSerializer(serializers.ModelSerializer): """ class Meta: model = UsedStep - fields = ['step', 'product', 'process'] + fields = ['step', 'subproduction'] class UsedStepListSerializer(serializers.ModelSerializer): """ @@ -198,7 +192,7 @@ class TechDocListSerializer(serializers.ModelSerializer): class TechDocCreateSerializer(serializers.ModelSerializer): class Meta: model = TechDoc - fields = ['file', 'product', 'process', 'name', 'content'] + fields = ['file', 'subproduction', 'name', 'content'] class TechDocUpdateSerializer(serializers.ModelSerializer): class Meta: diff --git a/hb_server/apps/mtm/urls.py b/hb_server/apps/mtm/urls.py index 7048bc5..5c18f0c 100644 --- a/hb_server/apps/mtm/urls.py +++ b/hb_server/apps/mtm/urls.py @@ -1,14 +1,14 @@ from django.db.models import base from rest_framework import urlpatterns -from apps.mtm.views import InputMaterialViewSet, MaterialViewSet, OutputMaterialViewSet, ProcessViewSet, RecordFormFieldViewSet, RecordFormViewSet, StepViewSet, TechDocViewSet, UsedStepViewSet +from apps.mtm.views import InputMaterialViewSet, MaterialViewSet, OutputMaterialViewSet, ProcessViewSet, RecordFormFieldViewSet, RecordFormViewSet, StepViewSet, SubProductionViewSet, TechDocViewSet, UsedStepViewSet from django.urls import path, include from rest_framework.routers import DefaultRouter router = DefaultRouter() router.register('material', MaterialViewSet, basename='material') router.register('process', ProcessViewSet, basename='process') -# router.register('productprocess', ProductProcessViewSet, basename='productprocess') router.register('step', StepViewSet, basename='step') +router.register('subproducation', SubProductionViewSet, basename='subproducation') router.register('inputmaterial', InputMaterialViewSet, basename='inputmaterial') router.register('outputmaterial', OutputMaterialViewSet, basename='outputmaterial') router.register('usedstep', UsedStepViewSet, basename='usedstep') diff --git a/hb_server/apps/mtm/views.py b/hb_server/apps/mtm/views.py index 974bdba..96ecf06 100644 --- a/hb_server/apps/mtm/views.py +++ b/hb_server/apps/mtm/views.py @@ -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, RecordForm, RecordFormField, Step, TechDoc, 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, TechDocCreateSerializer, TechDocListSerializer, TechDocUpdateSerializer, UsedStepCreateSerializer, UsedStepListSerializer +from apps.mtm.models import InputMaterial, Material, OutputMaterial, Process, RecordForm, RecordFormField, Step, TechDoc, UsedStep, SubProduction +from apps.mtm.serializers import InputMaterialListSerializer, InputMaterialSerializer, InputMaterialUpdateSerializer, MaterialDetailSerializer, MaterialSerializer, MaterialSimpleSerializer, OutputMaterialListSerializer, OutputMaterialSerializer, OutputMaterialUpdateSerializer, ProcessSerializer, RecordFormCreateSerializer, RecordFormFieldCreateSerializer, RecordFormFieldSerializer, RecordFormFieldUpdateSerializer, RecordFormSerializer, RecordFormUpdateSerializer, StepDetailSerializer, StepSerializer, SubProductionSerializer, TechDocCreateSerializer, TechDocListSerializer, TechDocUpdateSerializer, UsedStepCreateSerializer, UsedStepListSerializer from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin from rest_framework.decorators import action from rest_framework.response import Response @@ -30,15 +30,6 @@ class MaterialViewSet(PageOrNot, CreateUpdateModelAMixin, ModelViewSet): return MaterialDetailSerializer return MaterialSerializer - # @action(methods=['get'], detail=True, perms_map={'get':'*'}, pagination_class=None, serializer_class=MaterialSimpleSerializer) - # def processes(self, request, pk=None): - # """ - # 产品生产工艺流程 - # """ - # material = self.get_object() - # serializer = self.serializer_class(instance=Process.objects.filter(id__in=material.processes), many=True) - # return Response(serializer.data) - class ProcessViewSet(PageOrNot, CreateUpdateModelAMixin, ModelViewSet): """ @@ -78,20 +69,16 @@ class StepViewSet(OptimizationMixin, CreateUpdateModelAMixin, CreateModelMixin, return StepDetailSerializer return StepSerializer -# class ProductProcessViewSet(PageOrNot, CreateModelMixin, UpdateModelMixin, ListModelMixin, DestroyModelMixin, GenericViewSet): -# """ -# 产品生产工艺流程增删改查 -# """ -# perms_map={'*':'*'} -# queryset = ProductProcess.objects.select_related('process', 'product').all() -# filterset_fields = ['process', 'product'] -# serializer_class = ProductProcessListSerializer -# ordering = ['sort'] - -# def get_serializer_class(self): -# if self.action == 'update': -# return ProductProcessUpdateSerializer -# return super().get_serializer_class() +class SubProductionViewSet(PageOrNot, CreateModelMixin, UpdateModelMixin, ListModelMixin, DestroyModelMixin, GenericViewSet): + """ + 产品生产分解增删改查 + """ + perms_map={'*':'*'} + queryset = SubProduction.objects.all() + filterset_fields = ['product'] + search_fields = ['name'] + serializer_class = SubProductionSerializer + ordering = ['sort'] class InputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet): """ @@ -100,7 +87,7 @@ class InputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet): perms_map = {'*':'*'} queryset = InputMaterial.objects.select_related('material').all() serializer_class = InputMaterialSerializer - filterset_fields = ['process', 'product'] + filterset_fields = ['subproduction'] ordering = ['sort', '-create_time'] def get_serializer_class(self): @@ -117,7 +104,7 @@ class OutputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet): perms_map = {'*':'*'} queryset = OutputMaterial.objects.select_related('material').all() serializer_class = OutputMaterialSerializer - filterset_fields = ['process', 'product'] + filterset_fields = ['subproduction'] ordering = ['sort', '-create_time'] def get_serializer_class(self): @@ -133,7 +120,7 @@ class UsedStepViewSet(OptimizationMixin, CreateUpdateModelAMixin, CreateModelMix """ perms_map = {'*':'*'} queryset = UsedStep.objects.all() - filterset_fields = ['process', 'product', 'step'] + filterset_fields = ['subproduction', 'step'] ordering = ['step__sort', '-step__create_time'] def get_serializer_class(self): @@ -189,7 +176,7 @@ class TechDocViewSet(OptimizationMixin, CreateUpdateModelAMixin, ModelViewSet): """ perms_map = {'*':'*'} queryset = TechDoc.objects.select_related('file').all() - filterset_fields = ['process', 'product'] + filterset_fields = ['subproduction'] search_fields = ['name'] ordering = ['-id'] From 9b198bce6c8f05185e50ff2056576b4661f48728 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 12 Oct 2021 10:49:39 +0800 Subject: [PATCH 20/32] =?UTF-8?q?=E7=89=A9=E6=96=99=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/mtm/serializers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/hb_server/apps/mtm/serializers.py b/hb_server/apps/mtm/serializers.py index e6ab035..3e34690 100644 --- a/hb_server/apps/mtm/serializers.py +++ b/hb_server/apps/mtm/serializers.py @@ -6,7 +6,6 @@ from apps.system.serializers import FileSimpleSerializer, OrganizationSimpleSeri class MaterialSerializer(serializers.ModelSerializer): - processes = serializers.ListField(child=serializers.IntegerField(min_value=1)) class Meta: model = Material fields = '__all__' From 38e7cf2ee2656b0f440c85b86ebcc85cfd994316 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 12 Oct 2021 11:32:13 +0800 Subject: [PATCH 21/32] =?UTF-8?q?=E6=9F=A5=E7=9C=8B=E4=BA=A7=E5=93=81?= =?UTF-8?q?=E8=AF=A6=E6=83=85bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/mtm/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hb_server/apps/mtm/serializers.py b/hb_server/apps/mtm/serializers.py index 3e34690..081dc49 100644 --- a/hb_server/apps/mtm/serializers.py +++ b/hb_server/apps/mtm/serializers.py @@ -17,7 +17,7 @@ class MaterialDetailSerializer(serializers.ModelSerializer): fields = '__all__' def get_processes_(self, obj): - steps = UsedStep.objects.filter(subproducation__product=obj) + steps = UsedStep.objects.filter(subproduction__product=obj) objs = Process.objects.filter(step_process__in=steps).order_by('number') return ProcessSimpleSerializer(instance=objs, many=True).data From bbf5b2cbc9ee2bfbc401fa1de9254c6031cd4653 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 12 Oct 2021 13:00:49 +0800 Subject: [PATCH 22/32] =?UTF-8?q?=E6=9F=A5=E7=9C=8B=E4=BA=A7=E5=93=81?= =?UTF-8?q?=E5=A4=A7=E5=B7=A5=E5=BA=8Fbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/mtm/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hb_server/apps/mtm/serializers.py b/hb_server/apps/mtm/serializers.py index 081dc49..d512b57 100644 --- a/hb_server/apps/mtm/serializers.py +++ b/hb_server/apps/mtm/serializers.py @@ -17,8 +17,8 @@ class MaterialDetailSerializer(serializers.ModelSerializer): fields = '__all__' def get_processes_(self, obj): - steps = UsedStep.objects.filter(subproduction__product=obj) - objs = Process.objects.filter(step_process__in=steps).order_by('number') + steps = UsedStep.objects.filter(subproduction__product=obj).values_list('step', flat=True) + objs = Process.objects.filter(step_process__id__in=steps).order_by('number') return ProcessSimpleSerializer(instance=objs, many=True).data From 98a07bb27afd167b8f4c4e0e498d7812b2e3af88 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 12 Oct 2021 15:59:10 +0800 Subject: [PATCH 23/32] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=B7=A5=E5=8D=95?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E6=8E=A5=E5=8F=A3=E7=8A=B6=E6=80=81=E5=85=81?= =?UTF-8?q?=E8=AE=B8=E6=92=A4=E5=9B=9E=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wf/serializers.py | 2 +- hb_server/apps/wf/views.py | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/hb_server/apps/wf/serializers.py b/hb_server/apps/wf/serializers.py index 697a96a..addf700 100644 --- a/hb_server/apps/wf/serializers.py +++ b/hb_server/apps/wf/serializers.py @@ -23,7 +23,7 @@ class WorkflowSimpleSerializer(serializers.ModelSerializer): class StateSimpleSerializer(serializers.ModelSerializer): class Meta: model = State - fields = ['id', 'name', 'type', 'distribute_type'] + fields = ['id', 'name', 'type', 'distribute_type', 'enable_retreat'] class TransitionSerializer(serializers.ModelSerializer): source_state_ = StateSimpleSerializer(source='source_state', read_only=True) diff --git a/hb_server/apps/wf/views.py b/hb_server/apps/wf/views.py index 187d70f..461ccb8 100644 --- a/hb_server/apps/wf/views.py +++ b/hb_server/apps/wf/views.py @@ -292,7 +292,21 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin """ 撤回工单,允许创建人在指定状态撤回工单至初始状态,状态设置中开启允许撤回 """ - pass + ticket = self.get_object() + if ticket.create_by != request.user: + raise APIException('非创建人不可撤单') + if not ticket.state.enable_retreat: + raise APIException('该状态不可撤单') + start_state = WfService.get_workflow_start_state(ticket.workflow) + ticket.state = start_state + ticket.participant_type = State.PARTICIPANT_TYPE_PERSONAL + ticket.participant = request.user + ticket.act_state = Ticket.TICKET_ACT_STATE_RETREAT + ticket.save() + # 更新流转记录 + TicketFlow.objects.create(ticket=ticket, state=ticket.state, ticket_data=WfService.get_ticket_all_field_value(ticket), + suggestion='撤单', participant_type=State.PARTICIPANT_TYPE_PERSONAL, + participant=request.user, transition=None) class TicketFlowViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet): """ From 39e69ce170ca5d1dcb88cf21f5743d1942f43db2 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 12 Oct 2021 16:01:37 +0800 Subject: [PATCH 24/32] =?UTF-8?q?=E5=B7=A5=E5=8D=95=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0create=5Fby=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wf/models.py | 3 ++- hb_server/apps/wf/serializers.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/hb_server/apps/wf/models.py b/hb_server/apps/wf/models.py index 8328153..84ce41d 100644 --- a/hb_server/apps/wf/models.py +++ b/hb_server/apps/wf/models.py @@ -181,4 +181,5 @@ class TicketFlow(BaseModel): participant_type = models.IntegerField('处理人类型', default=0, help_text='0.无处理人,1.个人,2.多人', choices=State.type2_choices) participant = models.ForeignKey(User, verbose_name='处理人', on_delete=models.SET_NULL, null=True, blank=True, related_name='ticketflow_participant') state = models.ForeignKey(State, verbose_name='当前状态', default=0, blank=True, on_delete=models.CASCADE) - ticket_data = models.JSONField('工单数据', default=dict, blank=True, help_text='可以用于记录当前表单数据,json格式') \ No newline at end of file + ticket_data = models.JSONField('工单数据', default=dict, blank=True, help_text='可以用于记录当前表单数据,json格式') + # intervene_type_id = models.IntegerField('干预类型', default=0, help_text='见service.constant_service中定义') \ No newline at end of file diff --git a/hb_server/apps/wf/serializers.py b/hb_server/apps/wf/serializers.py index addf700..9843157 100644 --- a/hb_server/apps/wf/serializers.py +++ b/hb_server/apps/wf/serializers.py @@ -77,7 +77,7 @@ class TicketListSerializer(serializers.ModelSerializer): class Meta: model = Ticket - fields = ['id', 'title', 'sn', 'workflow', 'workflow_', 'state', 'state_', 'act_state', 'create_time', 'update_time', 'participant_type'] + fields = ['id', 'title', 'sn', 'workflow', 'workflow_', 'state', 'state_', 'act_state', 'create_time', 'update_time', 'participant_type', 'create_by'] @staticmethod def setup_eager_loading(queryset): From 252687be47d723c3757c28b0df8934c37b8045f3 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 12 Oct 2021 16:29:15 +0800 Subject: [PATCH 25/32] =?UTF-8?q?=E6=92=A4=E5=8D=95=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../0012_ticketflow_intervene_type.py | 18 ++++++++++ hb_server/apps/wf/models.py | 35 ++++++++++++++++--- hb_server/apps/wf/views.py | 5 +-- 3 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 hb_server/apps/wf/migrations/0012_ticketflow_intervene_type.py diff --git a/hb_server/apps/wf/migrations/0012_ticketflow_intervene_type.py b/hb_server/apps/wf/migrations/0012_ticketflow_intervene_type.py new file mode 100644 index 0000000..c6c9534 --- /dev/null +++ b/hb_server/apps/wf/migrations/0012_ticketflow_intervene_type.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.6 on 2021-10-12 08:23 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wf', '0011_auto_20210930_0954'), + ] + + operations = [ + migrations.AddField( + model_name='ticketflow', + name='intervene_type', + field=models.IntegerField(choices=[(0, '正常处理'), (1, '转交'), (2, '加签'), (3, '加签处理完成'), (4, '接单'), (5, '评论'), (6, '删除'), (7, '强制关闭'), (8, '强制修改状态'), (9, 'hook操作'), (10, '撤回')], default=0, help_text='流转类型', verbose_name='干预类型'), + ), + ] diff --git a/hb_server/apps/wf/models.py b/hb_server/apps/wf/models.py index 84ce41d..4878086 100644 --- a/hb_server/apps/wf/models.py +++ b/hb_server/apps/wf/models.py @@ -39,7 +39,7 @@ class State(CommonAModel): PARTICIPANT_TYPE_ROBOT = 6 PARTICIPANT_TYPE_FIELD = 7 PARTICIPANT_TYPE_PARENT_FIELD = 8 - type2_choices = ( + state_participanttype_choices = ( (0, '无处理人'), (PARTICIPANT_TYPE_PERSONAL, '个人'), (PARTICIPANT_TYPE_MULTI, '多人'), @@ -70,7 +70,7 @@ class State(CommonAModel): sort = models.IntegerField('状态顺序', default=0, help_text='用于工单步骤接口时,step上状态的顺序(因为存在网状情况,所以需要人为设定顺序),值越小越靠前') type = models.IntegerField('状态类型', default=0, choices=type_choices, help_text='0.普通类型 1.初始状态(用于新建工单时,获取对应的字段必填及transition信息) 2.结束状态(此状态下的工单不得再处理,即没有对应的transition)') enable_retreat = models.BooleanField('允许撤回', default=False, help_text='开启后允许工单创建人在此状态直接撤回工单到初始状态') - participant_type = models.IntegerField('参与者类型', choices=type2_choices, default=1, blank=True, help_text='0.无处理人,1.个人,2.多人,3.部门,4.角色,5.变量(支持工单创建人,创建人的leader),6.脚本,7.工单的字段内容(如表单中的"测试负责人",需要为用户名或者逗号隔开的多个用户名),8.父工单的字段内容。 初始状态请选择类型5,参与人填create_by') + participant_type = models.IntegerField('参与者类型', choices=state_participanttype_choices, default=1, blank=True, help_text='0.无处理人,1.个人,2.多人,3.部门,4.角色,5.变量(支持工单创建人,创建人的leader),6.脚本,7.工单的字段内容(如表单中的"测试负责人",需要为用户名或者逗号隔开的多个用户名),8.父工单的字段内容。 初始状态请选择类型5,参与人填create_by') participant = models.JSONField('参与者', default=list, blank=True, help_text='可以为空(无处理人的情况,如结束状态)、userid、userid列表\部门id\角色id\变量(create_by,create_by_tl)\脚本记录的id等,包含子工作流的需要设置处理人为loonrobot') state_fields = models.JSONField('表单字段', default=dict, help_text='json格式字典存储,包括读写属性1:只读,2:必填,3:可选. 示例:{"create_time":1,"title":2, "sn":1}, 内置特殊字段participant_info.participant_name:当前处理人信息(部门名称、角色名称),state.state_name:当前状态的状态名,workflow.workflow_name:工作流名称') # json格式存储,包括读写属性1:只读,2:必填,3:可选,4:不显示, 字典的字典 distribute_type = models.IntegerField('分配方式', default=1, choices=state_distribute_choices, help_text='1.主动接单(如果当前处理人实际为多人的时候,需要先接单才能处理) 2.直接处理(即使当前处理人实际为多人,也可以直接处理) 3.随机分配(如果实际为多人,则系统会随机分配给其中一个人) 4.全部处理(要求所有参与人都要处理一遍,才能进入下一步)') @@ -87,6 +87,31 @@ class Transition(CommonAModel): (2, '拒绝'), (3, '其他') ) + TRANSITION_INTERVENE_TYPE_DELIVER = 1 # 转交操作 + TRANSITION_INTERVENE_TYPE_ADD_NODE = 2 # 加签操作 + TRANSITION_INTERVENE_TYPE_ADD_NODE_END = 3 # 加签处理完成 + TRANSITION_INTERVENE_TYPE_ACCEPT = 4 # 接单操作 + TRANSITION_INTERVENE_TYPE_COMMENT = 5 # 评论操作 + TRANSITION_INTERVENE_TYPE_DELETE = 6 # 删除操作 + TRANSITION_INTERVENE_TYPE_CLOSE = 7 # 强制关闭操作 + TRANSITION_INTERVENE_TYPE_ALTER_STATE = 8 # 强制修改状态操作 + TRANSITION_INTERVENE_TYPE_HOOK = 9 # hook操作 + TRANSITION_INTERVENE_TYPE_RETREAT = 10 # 撤回 + + intervene_type_choices = ( + (0, '正常处理'), + (TRANSITION_INTERVENE_TYPE_DELIVER, '转交'), + (TRANSITION_INTERVENE_TYPE_ADD_NODE, '加签'), + (TRANSITION_INTERVENE_TYPE_ADD_NODE_END, '加签处理完成'), + (TRANSITION_INTERVENE_TYPE_ACCEPT, '接单'), + (TRANSITION_INTERVENE_TYPE_COMMENT, '评论'), + (TRANSITION_INTERVENE_TYPE_DELETE, '删除'), + (TRANSITION_INTERVENE_TYPE_CLOSE, '强制关闭'), + (TRANSITION_INTERVENE_TYPE_ALTER_STATE, '强制修改状态'), + (TRANSITION_INTERVENE_TYPE_HOOK, 'hook操作'), + (TRANSITION_INTERVENE_TYPE_RETREAT, '撤回') + ) + name = models.CharField('操作', max_length=50) workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='所属工作流') timer = models.IntegerField('定时器(单位秒)', default=0, help_text='单位秒。处于源状态X秒后如果状态都没有过变化则自动流转到目标状态。设置时间有效') @@ -166,7 +191,7 @@ class Ticket(CommonAModel): in_add_node = models.BooleanField('加签状态中', default=False, help_text='是否处于加签状态下') add_node_man = models.ForeignKey(User, verbose_name='加签人', on_delete=models.SET_NULL, null=True, blank=True, help_text='加签操作的人,工单当前处理人处理完成后会回到该处理人,当处于加签状态下才有效') - participant_type = models.IntegerField('当前处理人类型', default=0, help_text='0.无处理人,1.个人,2.多人', choices=State.type2_choices) + participant_type = models.IntegerField('当前处理人类型', default=0, help_text='0.无处理人,1.个人,2.多人', choices=State.state_participanttype_choices) participant = models.JSONField('当前处理人', default=list, blank=True, help_text='可以为空(无处理人的情况,如结束状态)、userid、userid列表') act_state = models.IntegerField('进行状态', default=1, help_text='当前工单的进行状态', choices=act_state_choices) multi_all_person = models.JSONField('全部处理的结果', default=dict, blank=True, help_text='需要当前状态处理人全部处理时实际的处理结果,json格式') @@ -178,8 +203,8 @@ class TicketFlow(BaseModel): ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, verbose_name='关联工单', related_name='ticketflow_ticket') transition = models.ForeignKey(Transition, verbose_name='流转id', help_text='与worklow.Transition关联, 为0时表示认为干预的操作', on_delete=models.CASCADE, null=True, blank=True) suggestion = models.CharField('处理意见', max_length=10000, default='', blank=True) - participant_type = models.IntegerField('处理人类型', default=0, help_text='0.无处理人,1.个人,2.多人', choices=State.type2_choices) + participant_type = models.IntegerField('处理人类型', default=0, help_text='0.无处理人,1.个人,2.多人', choices=State.state_participanttype_choices) participant = models.ForeignKey(User, verbose_name='处理人', on_delete=models.SET_NULL, null=True, blank=True, related_name='ticketflow_participant') state = models.ForeignKey(State, verbose_name='当前状态', default=0, blank=True, on_delete=models.CASCADE) ticket_data = models.JSONField('工单数据', default=dict, blank=True, help_text='可以用于记录当前表单数据,json格式') - # intervene_type_id = models.IntegerField('干预类型', default=0, help_text='见service.constant_service中定义') \ No newline at end of file + intervene_type = models.IntegerField('干预类型', default=0, help_text='流转类型', choices=Transition.intervene_type_choices) \ No newline at end of file diff --git a/hb_server/apps/wf/views.py b/hb_server/apps/wf/views.py index 461ccb8..9668189 100644 --- a/hb_server/apps/wf/views.py +++ b/hb_server/apps/wf/views.py @@ -281,7 +281,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin # 接单日志 # 更新工单流转记录 TicketFlow.objects.create(ticket=ticket, state=ticket.state, ticket_data=WfService.get_ticket_all_field_value(ticket), - suggestion='接单处理', participant_type=State.PARTICIPANT_TYPE_PERSONAL, + suggestion='接单', participant_type=State.PARTICIPANT_TYPE_PERSONAL, intervene_type=Transition.TRANSITION_ATTRIBUTE_TYPE_ACCEPT, participant=request.user, transition=None) return Response() else: @@ -305,8 +305,9 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin ticket.save() # 更新流转记录 TicketFlow.objects.create(ticket=ticket, state=ticket.state, ticket_data=WfService.get_ticket_all_field_value(ticket), - suggestion='撤单', participant_type=State.PARTICIPANT_TYPE_PERSONAL, + suggestion='撤回', participant_type=State.PARTICIPANT_TYPE_PERSONAL, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_RETREAT, participant=request.user, transition=None) + return Response() class TicketFlowViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet): """ From a535c1dc503ecf9b4fbfcd4ac31f4432c2b5ef6d Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 12 Oct 2021 16:42:11 +0800 Subject: [PATCH 26/32] =?UTF-8?q?=E6=8E=A5=E5=8D=95=E6=93=8D=E4=BD=9Cbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wf/views.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hb_server/apps/wf/views.py b/hb_server/apps/wf/views.py index 9668189..f041c1a 100644 --- a/hb_server/apps/wf/views.py +++ b/hb_server/apps/wf/views.py @@ -281,7 +281,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin # 接单日志 # 更新工单流转记录 TicketFlow.objects.create(ticket=ticket, state=ticket.state, ticket_data=WfService.get_ticket_all_field_value(ticket), - suggestion='接单', participant_type=State.PARTICIPANT_TYPE_PERSONAL, intervene_type=Transition.TRANSITION_ATTRIBUTE_TYPE_ACCEPT, + suggestion='', participant_type=State.PARTICIPANT_TYPE_PERSONAL, intervene_type=Transition.TRANSITION_ATTRIBUTE_TYPE_ACCEPT, participant=request.user, transition=None) return Response() else: @@ -300,12 +300,12 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin start_state = WfService.get_workflow_start_state(ticket.workflow) ticket.state = start_state ticket.participant_type = State.PARTICIPANT_TYPE_PERSONAL - ticket.participant = request.user + ticket.participant = request.user.id ticket.act_state = Ticket.TICKET_ACT_STATE_RETREAT ticket.save() # 更新流转记录 TicketFlow.objects.create(ticket=ticket, state=ticket.state, ticket_data=WfService.get_ticket_all_field_value(ticket), - suggestion='撤回', participant_type=State.PARTICIPANT_TYPE_PERSONAL, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_RETREAT, + suggestion='', participant_type=State.PARTICIPANT_TYPE_PERSONAL, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_RETREAT, participant=request.user, transition=None) return Response() From 4bab6de73dfe872c7650e5e327c530afc7ca7d5e Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 12 Oct 2021 16:58:32 +0800 Subject: [PATCH 27/32] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=A7=E5=93=81?= =?UTF-8?q?=E5=88=86=E8=A7=A3=E8=A1=A8=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mtm/migrations/0020_auto_20211012_1657.py | 26 +++++++++++++++++++ hb_server/apps/mtm/models.py | 2 +- hb_server/apps/mtm/views.py | 8 +++--- 3 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 hb_server/apps/mtm/migrations/0020_auto_20211012_1657.py diff --git a/hb_server/apps/mtm/migrations/0020_auto_20211012_1657.py b/hb_server/apps/mtm/migrations/0020_auto_20211012_1657.py new file mode 100644 index 0000000..2d80438 --- /dev/null +++ b/hb_server/apps/mtm/migrations/0020_auto_20211012_1657.py @@ -0,0 +1,26 @@ +# Generated by Django 3.2.6 on 2021-10-12 08:57 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('mtm', '0019_auto_20211012_0901'), + ] + + operations = [ + migrations.AddField( + model_name='subproduction', + name='create_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='subproduction_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'), + ), + migrations.AddField( + model_name='subproduction', + name='update_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='subproduction_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'), + ), + ] diff --git a/hb_server/apps/mtm/models.py b/hb_server/apps/mtm/models.py index 1037796..4574713 100644 --- a/hb_server/apps/mtm/models.py +++ b/hb_server/apps/mtm/models.py @@ -128,7 +128,7 @@ class RecordFormField(CommonAModel): -class SubProduction(BaseModel): +class SubProduction(CommonAModel): """ 产品生产分解 """ diff --git a/hb_server/apps/mtm/views.py b/hb_server/apps/mtm/views.py index 96ecf06..7c63a27 100644 --- a/hb_server/apps/mtm/views.py +++ b/hb_server/apps/mtm/views.py @@ -69,7 +69,7 @@ class StepViewSet(OptimizationMixin, CreateUpdateModelAMixin, CreateModelMixin, return StepDetailSerializer return StepSerializer -class SubProductionViewSet(PageOrNot, CreateModelMixin, UpdateModelMixin, ListModelMixin, DestroyModelMixin, GenericViewSet): +class SubProductionViewSet(CreateUpdateModelAMixin, ModelViewSet): """ 产品生产分解增删改查 """ @@ -80,7 +80,7 @@ class SubProductionViewSet(PageOrNot, CreateModelMixin, UpdateModelMixin, ListMo serializer_class = SubProductionSerializer ordering = ['sort'] -class InputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet): +class InputMaterialViewSet(ModelViewSet): """ 输入物料-增删改查 """ @@ -97,7 +97,7 @@ class InputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet): return InputMaterialUpdateSerializer return InputMaterialSerializer -class OutputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet): +class OutputMaterialViewSet(ModelViewSet): """ 输出物料-增删改查 """ @@ -114,7 +114,7 @@ class OutputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet): return OutputMaterialUpdateSerializer return OutputMaterialSerializer -class UsedStepViewSet(OptimizationMixin, CreateUpdateModelAMixin, CreateModelMixin, DestroyModelMixin, ListModelMixin, GenericViewSet): +class UsedStepViewSet(OptimizationMixin, CreateModelMixin, DestroyModelMixin, ListModelMixin, GenericViewSet): """ 产品生产子工序表 """ From 2eabbf891e68a10cf1c899f92d9c02991cd5cb58 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 12 Oct 2021 17:04:07 +0800 Subject: [PATCH 28/32] =?UTF-8?q?=E5=AD=90=E5=B7=A5=E5=BA=8F=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E5=AE=8C=E6=95=B4list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/mtm/views.py | 4 ++-- hb_server/apps/wf/views.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hb_server/apps/mtm/views.py b/hb_server/apps/mtm/views.py index 7c63a27..a8a85d9 100644 --- a/hb_server/apps/mtm/views.py +++ b/hb_server/apps/mtm/views.py @@ -53,11 +53,11 @@ class ProcessViewSet(PageOrNot, CreateUpdateModelAMixin, ModelViewSet): serializer = self.serializer_class(instance=Step.objects.prefetch_related('equipments').filter(process=process, is_deleted=False), many=True) return Response(serializer.data) -class StepViewSet(OptimizationMixin, CreateUpdateModelAMixin, CreateModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin, GenericViewSet): +class StepViewSet(OptimizationMixin, CreateUpdateModelAMixin, ModelViewSet): """ 子工序-增删改查 """ - perms_map = {'*':'process_update'} + perms_map = {'*':'*'} queryset = Step.objects.all() serializer_class = StepSerializer search_fields = ['name', 'number'] diff --git a/hb_server/apps/wf/views.py b/hb_server/apps/wf/views.py index f041c1a..22eb977 100644 --- a/hb_server/apps/wf/views.py +++ b/hb_server/apps/wf/views.py @@ -294,9 +294,9 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin """ ticket = self.get_object() if ticket.create_by != request.user: - raise APIException('非创建人不可撤单') + raise APIException('非创建人不可撤回') if not ticket.state.enable_retreat: - raise APIException('该状态不可撤单') + raise APIException('该状态不可撤回') start_state = WfService.get_workflow_start_state(ticket.workflow) ticket.state = start_state ticket.participant_type = State.PARTICIPANT_TYPE_PERSONAL From 84a78f0470e9b83648fbf332851f51f69f531096 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 13 Oct 2021 08:49:48 +0800 Subject: [PATCH 29/32] =?UTF-8?q?=E6=92=A4=E5=9B=9E=E5=8E=9F=E5=9B=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wf/serializers.py | 5 ++++- hb_server/apps/wf/views.py | 19 +++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/hb_server/apps/wf/serializers.py b/hb_server/apps/wf/serializers.py index 9843157..9075341 100644 --- a/hb_server/apps/wf/serializers.py +++ b/hb_server/apps/wf/serializers.py @@ -126,4 +126,7 @@ class TicketFlowSimpleSerializer(serializers.ModelSerializer): class TicketHandleSerializer(serializers.Serializer): transition = serializers.IntegerField(label="流转id") ticket_data = serializers.JSONField(label="表单数据json") - suggestion = serializers.CharField(label="处理意见", required = False) \ No newline at end of file + suggestion = serializers.CharField(label="处理意见", required = False) + +class TicketRetreatSerializer(serializers.Serializer): + suggestion = serializers.CharField(label="撤回原因", required = False) \ No newline at end of file diff --git a/hb_server/apps/wf/views.py b/hb_server/apps/wf/views.py index 22eb977..a1aab22 100644 --- a/hb_server/apps/wf/views.py +++ b/hb_server/apps/wf/views.py @@ -3,7 +3,7 @@ from django.core.exceptions import AppRegistryNotReady from rest_framework.response import Response from rest_framework import serializers from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin -from apps.wf.serializers import CustomFieldSerializer, StateSerializer, TicketCreateSerializer, TicketFlowSerializer, TicketFlowSimpleSerializer, TicketHandleSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer, TicketListSerializer, TicketDetailSerializer +from apps.wf.serializers import CustomFieldSerializer, StateSerializer, TicketCreateSerializer, TicketFlowSerializer, TicketFlowSimpleSerializer, TicketHandleSerializer, TicketRetreatSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer, TicketListSerializer, TicketDetailSerializer from django.shortcuts import get_object_or_404, render from rest_framework.viewsets import GenericViewSet, ModelViewSet from rest_framework.decorators import action, api_view @@ -102,6 +102,8 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin return TicketCreateSerializer elif self.action == 'handle': return TicketHandleSerializer + elif self.action == 'retreat': + return TicketRetreatSerializer elif self.action == 'list': return TicketListSerializer elif self.action == 'retrieve': @@ -304,10 +306,23 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin ticket.act_state = Ticket.TICKET_ACT_STATE_RETREAT ticket.save() # 更新流转记录 + suggestion = request.data.get('suggestion', '') # 撤回原因 TicketFlow.objects.create(ticket=ticket, state=ticket.state, ticket_data=WfService.get_ticket_all_field_value(ticket), - suggestion='', participant_type=State.PARTICIPANT_TYPE_PERSONAL, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_RETREAT, + suggestion=suggestion, participant_type=State.PARTICIPANT_TYPE_PERSONAL, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_RETREAT, participant=request.user, transition=None) return Response() + + @action(methods=['post'], detail=True, perms_map={'post':'*'}) + def add_node(self, request, pk=None): + """ + 加签 + """ + + def close(self, request, pk=None): + """ + 关闭工单(超级管理员或者创建人在初始状态) + """ + class TicketFlowViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet): """ From 52df9ddec52f3a9c95408455fd6ad5ad2265e87b Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 13 Oct 2021 08:58:41 +0800 Subject: [PATCH 30/32] =?UTF-8?q?=E7=94=9F=E4=BA=A7=E5=88=86=E8=A7=A3?= =?UTF-8?q?=E8=A1=A8=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mtm/migrations/0021_auto_20211013_0856.py | 56 +++++++++++++++++++ hb_server/apps/mtm/models.py | 8 +-- hb_server/apps/mtm/views.py | 4 +- 3 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 hb_server/apps/mtm/migrations/0021_auto_20211013_0856.py diff --git a/hb_server/apps/mtm/migrations/0021_auto_20211013_0856.py b/hb_server/apps/mtm/migrations/0021_auto_20211013_0856.py new file mode 100644 index 0000000..21d8787 --- /dev/null +++ b/hb_server/apps/mtm/migrations/0021_auto_20211013_0856.py @@ -0,0 +1,56 @@ +# Generated by Django 3.2.6 on 2021-10-13 00:56 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('mtm', '0020_auto_20211012_1657'), + ] + + operations = [ + migrations.AddField( + model_name='inputmaterial', + name='create_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='inputmaterial_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'), + ), + migrations.AddField( + model_name='inputmaterial', + name='update_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='inputmaterial_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'), + ), + migrations.AddField( + model_name='outputmaterial', + name='create_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='outputmaterial_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'), + ), + migrations.AddField( + model_name='outputmaterial', + name='update_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='outputmaterial_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'), + ), + migrations.AddField( + model_name='techdoc', + name='create_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='techdoc_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'), + ), + migrations.AddField( + model_name='techdoc', + name='update_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='techdoc_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'), + ), + migrations.AddField( + model_name='usedstep', + name='create_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='usedstep_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'), + ), + migrations.AddField( + model_name='usedstep', + name='update_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='usedstep_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'), + ), + ] diff --git a/hb_server/apps/mtm/models.py b/hb_server/apps/mtm/models.py index 4574713..0a0c582 100644 --- a/hb_server/apps/mtm/models.py +++ b/hb_server/apps/mtm/models.py @@ -141,7 +141,7 @@ class SubProduction(CommonAModel): verbose_name_plural = verbose_name -class InputMaterial(BaseModel): +class InputMaterial(CommonAModel): """ 输入物料 """ @@ -156,7 +156,7 @@ class InputMaterial(BaseModel): -class OutputMaterial(BaseModel): +class OutputMaterial(CommonAModel): """ 输出物料 """ @@ -169,7 +169,7 @@ class OutputMaterial(BaseModel): verbose_name = '输出物料' verbose_name_plural = verbose_name -class UsedStep(BaseModel): +class UsedStep(CommonAModel): """ 涉及的生产子工序 """ @@ -182,7 +182,7 @@ class UsedStep(BaseModel): verbose_name_plural = verbose_name -class TechDoc(BaseModel): +class TechDoc(CommonAModel): """ 技术文件 """ diff --git a/hb_server/apps/mtm/views.py b/hb_server/apps/mtm/views.py index a8a85d9..c18d60f 100644 --- a/hb_server/apps/mtm/views.py +++ b/hb_server/apps/mtm/views.py @@ -80,7 +80,7 @@ class SubProductionViewSet(CreateUpdateModelAMixin, ModelViewSet): serializer_class = SubProductionSerializer ordering = ['sort'] -class InputMaterialViewSet(ModelViewSet): +class InputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet): """ 输入物料-增删改查 """ @@ -97,7 +97,7 @@ class InputMaterialViewSet(ModelViewSet): return InputMaterialUpdateSerializer return InputMaterialSerializer -class OutputMaterialViewSet(ModelViewSet): +class OutputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet): """ 输出物料-增删改查 """ From a30cab1c94b29aa06ad5030ddfdfe86da5f45ef9 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 13 Oct 2021 09:04:52 +0800 Subject: [PATCH 31/32] =?UTF-8?q?=E5=B7=A5=E8=89=BA=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E7=89=A9=E6=96=99detail=E6=8E=A5=E5=8F=A3=E5=90=88=E5=B9=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/mtm/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hb_server/apps/mtm/serializers.py b/hb_server/apps/mtm/serializers.py index d512b57..04f3e3d 100644 --- a/hb_server/apps/mtm/serializers.py +++ b/hb_server/apps/mtm/serializers.py @@ -18,7 +18,7 @@ class MaterialDetailSerializer(serializers.ModelSerializer): def get_processes_(self, obj): steps = UsedStep.objects.filter(subproduction__product=obj).values_list('step', flat=True) - objs = Process.objects.filter(step_process__id__in=steps).order_by('number') + objs = Process.objects.filter(step_process__id__in=steps).distinct().order_by('number') return ProcessSimpleSerializer(instance=objs, many=True).data From 3f959c6e2d9463ad5c430fc35a5da319c8d55b5a Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 13 Oct 2021 09:23:03 +0800 Subject: [PATCH 32/32] =?UTF-8?q?mtm=20usedstep=20remark=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/mtm/models.py | 4 ++-- hb_server/apps/mtm/serializers.py | 10 +++++++++- hb_server/apps/mtm/views.py | 6 ++++-- hb_server/apps/pm/views.py | 6 +++++- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/hb_server/apps/mtm/models.py b/hb_server/apps/mtm/models.py index 0a0c582..82b39c7 100644 --- a/hb_server/apps/mtm/models.py +++ b/hb_server/apps/mtm/models.py @@ -16,9 +16,9 @@ class Material(CommonAModel): (1, '成品'), (2, '半成品'), (3, '主要原料'), - (4,'辅助原料') , + (4, '辅助原料') , (5, '加工工具'), - (6,'辅助工具') + (6, '辅助工具') ) unit_choices =( ('块', '块'), diff --git a/hb_server/apps/mtm/serializers.py b/hb_server/apps/mtm/serializers.py index 04f3e3d..7cd8fe5 100644 --- a/hb_server/apps/mtm/serializers.py +++ b/hb_server/apps/mtm/serializers.py @@ -114,7 +114,15 @@ class UsedStepCreateSerializer(serializers.ModelSerializer): """ class Meta: model = UsedStep - fields = ['step', 'subproduction'] + fields = ['step', 'subproduction', 'remark'] + +class UsedStepUpdateSerializer(serializers.ModelSerializer): + """ + 产品生产子工序编辑 + """ + class Meta: + model = UsedStep + fields = ['remark'] class UsedStepListSerializer(serializers.ModelSerializer): """ diff --git a/hb_server/apps/mtm/views.py b/hb_server/apps/mtm/views.py index c18d60f..0e1c44f 100644 --- a/hb_server/apps/mtm/views.py +++ b/hb_server/apps/mtm/views.py @@ -3,7 +3,7 @@ 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, RecordForm, RecordFormField, Step, TechDoc, UsedStep, SubProduction -from apps.mtm.serializers import InputMaterialListSerializer, InputMaterialSerializer, InputMaterialUpdateSerializer, MaterialDetailSerializer, MaterialSerializer, MaterialSimpleSerializer, OutputMaterialListSerializer, OutputMaterialSerializer, OutputMaterialUpdateSerializer, ProcessSerializer, RecordFormCreateSerializer, RecordFormFieldCreateSerializer, RecordFormFieldSerializer, RecordFormFieldUpdateSerializer, RecordFormSerializer, RecordFormUpdateSerializer, StepDetailSerializer, StepSerializer, SubProductionSerializer, TechDocCreateSerializer, TechDocListSerializer, TechDocUpdateSerializer, UsedStepCreateSerializer, UsedStepListSerializer +from apps.mtm.serializers import InputMaterialListSerializer, InputMaterialSerializer, InputMaterialUpdateSerializer, MaterialDetailSerializer, MaterialSerializer, MaterialSimpleSerializer, OutputMaterialListSerializer, OutputMaterialSerializer, OutputMaterialUpdateSerializer, ProcessSerializer, RecordFormCreateSerializer, RecordFormFieldCreateSerializer, RecordFormFieldSerializer, RecordFormFieldUpdateSerializer, RecordFormSerializer, RecordFormUpdateSerializer, StepDetailSerializer, StepSerializer, SubProductionSerializer, TechDocCreateSerializer, TechDocListSerializer, TechDocUpdateSerializer, UsedStepCreateSerializer, UsedStepListSerializer, UsedStepUpdateSerializer from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin from rest_framework.decorators import action from rest_framework.response import Response @@ -114,7 +114,7 @@ class OutputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet): return OutputMaterialUpdateSerializer return OutputMaterialSerializer -class UsedStepViewSet(OptimizationMixin, CreateModelMixin, DestroyModelMixin, ListModelMixin, GenericViewSet): +class UsedStepViewSet(OptimizationMixin, CreateModelMixin, DestroyModelMixin, ListModelMixin, UpdateModelMixin, GenericViewSet): """ 产品生产子工序表 """ @@ -126,6 +126,8 @@ class UsedStepViewSet(OptimizationMixin, CreateModelMixin, DestroyModelMixin, Li def get_serializer_class(self): if self.action =='create': return UsedStepCreateSerializer + elif self.action == 'update': + return UsedStepUpdateSerializer return UsedStepListSerializer class RecordFormViewSet(OptimizationMixin, CreateUpdateModelAMixin, ModelViewSet): diff --git a/hb_server/apps/pm/views.py b/hb_server/apps/pm/views.py index 25509dc..a0d28fa 100644 --- a/hb_server/apps/pm/views.py +++ b/hb_server/apps/pm/views.py @@ -1,3 +1,4 @@ +from rest_framework.views import APIView from apps.system.mixins import CreateUpdateModelAMixin from apps.pm.serializers import ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer from rest_framework.mixins import CreateModelMixin, ListModelMixin @@ -49,4 +50,7 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel raise APIException('排产数量错误') instance = serializer.save(create_by=request.user, product=order.product) updateOrderPlanedCount(instance.order) - return Response() \ No newline at end of file + return Response() + +class ResourceCalculate(APIView): + pass \ No newline at end of file