角色筛选策略以及抄送功能

This commit is contained in:
caoqianming 2021-10-25 08:32:55 +08:00
parent 927f25ae05
commit 2c82f8aa64
7 changed files with 110 additions and 15 deletions

View File

@ -18,7 +18,7 @@ class TicketFilterSet(filters.FilterSet):
elif value == 'worked': elif value == 'worked':
queryset = queryset.filter(ticketflow_ticket__participant=user).exclude(create_by=user).order_by('-update_time').distinct() queryset = queryset.filter(ticketflow_ticket__participant=user).exclude(create_by=user).order_by('-update_time').distinct()
elif value == 'cc': elif value == 'cc':
pass queryset = queryset.filter(ticketflow_ticket__participant_cc__contains=user.id).exclude(create_by=user).order_by('-update_time').distinct()
elif value == 'all': elif value == 'all':
pass pass
else: else:

View File

@ -0,0 +1,53 @@
# Generated by Django 3.2.6 on 2021-10-24 15:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wf', '0015_auto_20211021_1049'),
]
operations = [
migrations.AddField(
model_name='state',
name='participant_cc',
field=models.JSONField(blank=True, default=list, help_text='抄送给(userid列表)', verbose_name='抄送给'),
),
migrations.AddField(
model_name='ticketflow',
name='participant_cc',
field=models.JSONField(blank=True, default=list, help_text='抄送给(userid列表)', verbose_name='抄送给'),
),
migrations.AddField(
model_name='wfscript',
name='func_str',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='函数名'),
),
migrations.AlterField(
model_name='state',
name='participant_type',
field=models.IntegerField(blank=True, choices=[(0, '无处理人'), (1, '个人'), (2, '多人'), (4, '角色'), (6, '脚本'), (7, '工单的字段'), (9, '代码获取')], default=1, help_text='0.无处理人,1.个人,2.多人,3.部门,4.角色,5.变量(支持工单创建人,创建人的leader),6.脚本,7.工单的字段内容(如表单中的"测试负责人",需要为用户名或者逗号隔开的多个用户名),8.父工单的字段内容。 初始状态请选择类型5参与人填create_by', verbose_name='参与者类型'),
),
migrations.AlterField(
model_name='ticket',
name='participant_type',
field=models.IntegerField(choices=[(0, '无处理人'), (1, '个人'), (2, '多人'), (4, '角色'), (6, '脚本'), (7, '工单的字段'), (9, '代码获取')], default=0, help_text='0.无处理人,1.个人,2.多人', verbose_name='当前处理人类型'),
),
migrations.AlterField(
model_name='ticketflow',
name='intervene_type',
field=models.IntegerField(choices=[(0, '正常处理'), (1, '转交'), (2, '加签'), (3, '加签处理完成'), (4, '接单'), (5, '评论'), (6, '删除'), (7, '强制关闭'), (8, '强制修改状态'), (9, 'hook操作'), (10, '撤回'), (11, '抄送')], default=0, help_text='流转类型', verbose_name='干预类型'),
),
migrations.AlterField(
model_name='ticketflow',
name='participant_type',
field=models.IntegerField(choices=[(0, '无处理人'), (1, '个人'), (2, '多人'), (4, '角色'), (6, '脚本'), (7, '工单的字段'), (9, '代码获取')], default=0, help_text='0.无处理人,1.个人,2.多人', verbose_name='处理人类型'),
),
migrations.AlterField(
model_name='wfscript',
name='usage',
field=models.IntegerField(choices=[(1, '获取处理人'), (2, '执行操作')], default=2, verbose_name='脚本用途'),
),
]

View File

@ -85,6 +85,7 @@ class State(CommonAModel):
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不显示, 字典的字典 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.全部处理(要求所有参与人都要处理一遍,才能进入下一步)') distribute_type = models.IntegerField('分配方式', default=1, choices=state_distribute_choices, help_text='1.主动接单(如果当前处理人实际为多人的时候,需要先接单才能处理) 2.直接处理(即使当前处理人实际为多人,也可以直接处理) 3.随机分配(如果实际为多人,则系统会随机分配给其中一个人) 4.全部处理(要求所有参与人都要处理一遍,才能进入下一步)')
filter_policy = models.IntegerField('参与人过滤策略', default=0, choices=state_filter_choices) filter_policy = models.IntegerField('参与人过滤策略', default=0, choices=state_filter_choices)
participant_cc = models.JSONField('抄送给', default=list, blank=True, help_text='抄送给(userid列表)')
class Transition(CommonAModel): class Transition(CommonAModel):
""" """
@ -108,6 +109,7 @@ class Transition(CommonAModel):
TRANSITION_INTERVENE_TYPE_ALTER_STATE = 8 # 强制修改状态操作 TRANSITION_INTERVENE_TYPE_ALTER_STATE = 8 # 强制修改状态操作
TRANSITION_INTERVENE_TYPE_HOOK = 9 # hook操作 TRANSITION_INTERVENE_TYPE_HOOK = 9 # hook操作
TRANSITION_INTERVENE_TYPE_RETREAT = 10 # 撤回 TRANSITION_INTERVENE_TYPE_RETREAT = 10 # 撤回
TRANSITION_INTERVENE_TYPE_CC = 11 # 抄送
intervene_type_choices = ( intervene_type_choices = (
(0, '正常处理'), (0, '正常处理'),
@ -120,7 +122,8 @@ class Transition(CommonAModel):
(TRANSITION_INTERVENE_TYPE_CLOSE, '强制关闭'), (TRANSITION_INTERVENE_TYPE_CLOSE, '强制关闭'),
(TRANSITION_INTERVENE_TYPE_ALTER_STATE, '强制修改状态'), (TRANSITION_INTERVENE_TYPE_ALTER_STATE, '强制修改状态'),
(TRANSITION_INTERVENE_TYPE_HOOK, 'hook操作'), (TRANSITION_INTERVENE_TYPE_HOOK, 'hook操作'),
(TRANSITION_INTERVENE_TYPE_RETREAT, '撤回') (TRANSITION_INTERVENE_TYPE_RETREAT, '撤回'),
(TRANSITION_INTERVENE_TYPE_CC, '抄送')
) )
name = models.CharField('操作', max_length=50) name = models.CharField('操作', max_length=50)
@ -219,6 +222,7 @@ class TicketFlow(BaseModel):
state = models.ForeignKey(State, verbose_name='当前状态', default=0, blank=True, on_delete=models.CASCADE) 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格式') ticket_data = models.JSONField('工单数据', default=dict, blank=True, help_text='可以用于记录当前表单数据json格式')
intervene_type = models.IntegerField('干预类型', default=0, help_text='流转类型', choices=Transition.intervene_type_choices) intervene_type = models.IntegerField('干预类型', default=0, help_text='流转类型', choices=Transition.intervene_type_choices)
participant_cc = models.JSONField('抄送给', default=list, blank=True, help_text='抄送给(userid列表)')
class WfScript(CommonAModel): class WfScript(CommonAModel):
""" """
@ -228,7 +232,7 @@ class WfScript(CommonAModel):
(1, '获取处理人'), (1, '获取处理人'),
(2, '执行操作'), (2, '执行操作'),
) )
usage = models.IntegerField('脚本用途', default=1, choices=usage_choices) usage = models.IntegerField('脚本用途', default=2, choices=usage_choices)
wait = models.BooleanField('是否等待执行完成', default=True) wait = models.BooleanField('是否等待执行完成', default=True)
name = models.CharField('脚本名称', max_length=100) name = models.CharField('脚本名称', max_length=100)
workflow = models.ForeignKey(Workflow, verbose_name='关联工作流', null=True, blank=True, on_delete=models.SET_NULL) workflow = models.ForeignKey(Workflow, verbose_name='关联工作流', null=True, blank=True, on_delete=models.SET_NULL)

View File

@ -8,6 +8,7 @@ from django.utils import timezone
from datetime import timedelta from datetime import timedelta
import random import random
from .scripts import GetParticipants from .scripts import GetParticipants
from utils.queryset import get_parent_queryset
class WfService(object): class WfService(object):
@staticmethod @staticmethod
@ -159,11 +160,14 @@ class WfService(object):
user_queryset = User.objects.filter(roles__in=destination_participant) user_queryset = User.objects.filter(roles__in=destination_participant)
# 如果选择了角色, 需要走过滤策略 # 如果选择了角色, 需要走过滤策略
if ticket.filter_policy == 1: if ticket.filter_policy == 1:
user_queryset = user_queryset.filter(dept=ticket.belong_dept) depts = get_parent_queryset(ticket.belong_dept)
user_queryset = user_queryset.filter(dept__in=depts)
elif ticket.filter_policy == 2: elif ticket.filter_policy == 2:
user_queryset = user_queryset.filter(dept=ticket.create_by.dept) depts = get_parent_queryset(ticket.create_by.dept)
user_queryset = user_queryset.filter(dept__in=depts)
elif ticket.filter_policy == 3: elif ticket.filter_policy == 3:
user_queryset = user_queryset.filter(dept=request.user.dept) depts = get_parent_queryset(request.user.dept)
user_queryset = user_queryset.filter(dept__in=depts)
destination_participant = list(user_queryset.values_list('id', flat=True)) destination_participant = list(user_queryset.values_list('id', flat=True))
if type(destination_participant) == list: if type(destination_participant) == list:
destination_participant_type = State.PARTICIPANT_TYPE_MULTI destination_participant_type = State.PARTICIPANT_TYPE_MULTI

View File

@ -18,6 +18,7 @@ from rest_framework import status
from django.db.models import Count from django.db.models import Count
from .scripts import GetParticipants from .scripts import GetParticipants
# Create your views here. # Create your views here.
class FromCodeListView(APIView): class FromCodeListView(APIView):
def get(self, request, format=None): def get(self, request, format=None):
@ -144,6 +145,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
if key not in ticket_data or not ticket_data[key]: if key not in ticket_data or not ticket_data[key]:
raise APIException('字段{}必填'.format(key)) raise APIException('字段{}必填'.format(key))
ticket = serializer.save(state=start_state, create_by=request.user, act_state=Ticket.TICKET_ACT_STATE_DRAFT, belong_dept=request.user.dept) # 先创建出来 ticket = serializer.save(state=start_state, create_by=request.user, act_state=Ticket.TICKET_ACT_STATE_DRAFT, belong_dept=request.user.dept) # 先创建出来
next_state = WfService.get_next_state_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) 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_type = participant_info.get('destination_participant_type', 0)
@ -174,6 +176,16 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
TicketFlow.objects.create(ticket=ticket, state=start_state, ticket_data=WfService.get_ticket_all_field_value(ticket), 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, suggestion=rdata.get('suggestion',''), participant_type=State.PARTICIPANT_TYPE_PERSONAL,
participant=ticket.create_by, transition=transition) participant=ticket.create_by, transition=transition)
# 开始状态需要抄送
if start_state.participant_cc:
TicketFlow.objects.create(ticket=ticket, state=ticket.start_state,
participant_type=0, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_CC,
participant=None, participant_cc=start_state.participant_cc)
# 目标状态需要抄送
if next_state.participant_cc:
TicketFlow.objects.create(ticket=ticket, state=ticket.next_state,
participant_type=0, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_CC,
participant=None, participant_cc=next_state.participant_cc)
return Response(TicketSerializer(instance=ticket).data) return Response(TicketSerializer(instance=ticket).data)
@action(methods=['get'], detail=False, perms_map={'get':'*'}) @action(methods=['get'], detail=False, perms_map={'get':'*'})
@ -228,7 +240,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
destination_participant = [] destination_participant = []
for key, value in multi_all_person.items(): for key, value in multi_all_person.items():
if not value: if not value:
destination_participant.push(key) destination_participant.append(key)
else: else:
# 当前处理人类型非全部处理 # 当前处理人类型非全部处理
participant_info = WfService.get_ticket_state_participant_info(destination_state, ticket, data['ticket_data']) participant_info = WfService.get_ticket_state_participant_info(destination_state, ticket, data['ticket_data'])
@ -262,6 +274,11 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
TicketFlow.objects.create(ticket=ticket, state=source_state, ticket_data=WfService.get_ticket_all_field_value(ticket), 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, suggestion=data.get('suggestion',''), participant_type=State.PARTICIPANT_TYPE_PERSONAL,
participant=request.user, transition=transition) participant=request.user, transition=transition)
# 目标状态需要抄送
if destination_state.participant_cc:
TicketFlow.objects.create(ticket=ticket, state=ticket.destination_state,
participant_type=0, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_CC,
participant=None, participant_cc=destination_state.participant_cc)
return Response(TicketSerializer(instance=ticket).data) return Response(TicketSerializer(instance=ticket).data)

View File

@ -5,11 +5,11 @@ from django.db.models.query import QuerySet
from apps.system.models import CommonAModel, CommonBModel, Organization, User, Dict, File from apps.system.models import CommonAModel, CommonBModel, Organization, User, Dict, File
from utils.model import SoftModel, BaseModel from utils.model import SoftModel, BaseModel
from simple_history.models import HistoricalRecords from simple_history.models import HistoricalRecords
from apps.mtm.models import Material, Step from apps.mtm.models import Material, Step, RecordForm
class Good(CommonAModel): class Product(CommonAModel):
""" """
物品 产品(所有生产过程中出现过的)
""" """
act_state_choices=( act_state_choices=(
(0, '待执行'), (0, '待执行'),
@ -20,20 +20,27 @@ class Good(CommonAModel):
m_state = models.ForeignKey(Material, verbose_name='所属物料状态', on_delete=models.CASCADE) m_state = models.ForeignKey(Material, verbose_name='所属物料状态', on_delete=models.CASCADE)
p_state = models.ForeignKey(Step, verbose_name='所在步骤', on_delete=models.CASCADE, null=True, blank=True) p_state = models.ForeignKey(Step, verbose_name='所在步骤', on_delete=models.CASCADE, null=True, blank=True)
act_state = models.IntegerField('进行状态', default=0) act_state = models.IntegerField('进行状态', default=0)
remark = models.CharField('备注', max_length=200, null=True, blank=True)
class ProductForm(CommonAModel):
"""
记录表格
"""
record_form = models.ForeignKey(RecordForm, verbose_name='所用表格', on_delete=models.CASCADE)
data = models.JSONField('记录的数据', default=dict, blank=True)
class GoodFlow(BaseModel): class ProductFlow(BaseModel):
""" """
物品流转日志 品流转日志
""" """
pass product = models.ForeignKey(Product, verbose_name='产品', on_delete=models.CASCADE)
class Vendor(CommonAModel): class Vendor(CommonAModel):
""" """
供应商信息 供应商信息
""" """
name = models.CharField('供应商名称', max_length=50, unique=True) name = models.CharField('供应商名称', max_length=50, unique=True)
contact = models.CharField('联系人', max_length=20) contact = models.CharField('联系人', max_length=20)
contact_phone = models.CharField('联系电话', max_length=11, unique=True) contact_phone = models.CharField('联系电话', max_length=11, unique=True)

View File

@ -57,4 +57,14 @@ def get_child_queryset2(obj, hasParent=True):
while child_queryset: while child_queryset:
queryset = queryset | child_queryset queryset = queryset | child_queryset
child_queryset = cls.objects.filter(parent__in=child_queryset) child_queryset = cls.objects.filter(parent__in=child_queryset)
return queryset return queryset
def get_parent_queryset(obj, hasSelf=True):
cls = type(obj)
ids = []
if hasSelf:
ids.append(obj.id)
while obj.parent:
obj = obj.parent
ids.append(obj.id)
return cls.objects.filter(id__in=ids)