Merge branch 'develop' of https://e.coding.net/ctcdevteam/hberp/hberp into develop

This commit is contained in:
shijing 2021-10-22 16:25:37 +08:00
commit d400f53905
14 changed files with 166 additions and 26 deletions

View File

@ -61,9 +61,9 @@ class FIFO(CommonAModel):
operator = models.ForeignKey(User, verbose_name='操作人', on_delete=models.CASCADE) operator = models.ForeignKey(User, verbose_name='操作人', on_delete=models.CASCADE)
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.DO_NOTHING, null=True, blank=True) subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.DO_NOTHING, null=True, blank=True)
class FIFODetail(CommonAModel): # class FIFODetail(CommonAModel):
""" # """
领料详细记录 # 领料详细记录
""" # """

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-10-21 08:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mtm', '0024_alter_recordformfield_rule_expression'),
]
operations = [
migrations.AddField(
model_name='outputmaterial',
name='is_main',
field=models.BooleanField(default=True, verbose_name='是否主产出'),
),
]

View File

@ -177,6 +177,7 @@ class OutputMaterial(CommonAModel):
输出物料 输出物料
""" """
material = models.ForeignKey(Material, verbose_name='输出物料', on_delete=models.CASCADE, related_name='outputmaterial') material = models.ForeignKey(Material, verbose_name='输出物料', on_delete=models.CASCADE, related_name='outputmaterial')
is_main = models.BooleanField('是否主产出', default=True) # 以该产品完成度计算进度
count = models.FloatField('产出量', default=1) count = models.FloatField('产出量', default=1)
subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE) subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE)
sort = models.IntegerField('排序号', default=1) sort = models.IntegerField('排序号', default=1)

View File

@ -98,9 +98,11 @@ class InputMaterialUpdateSerializer(serializers.ModelSerializer):
class OutputMaterialSerializer(serializers.ModelSerializer): class OutputMaterialSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = OutputMaterial model = OutputMaterial
fields = ['count', 'sort', 'material', 'subproduction'] fields = ['count', 'sort', 'material', 'subproduction', 'is_main']
def create(self, validated_data): def create(self, validated_data):
if OutputMaterial.objects.filter(subproduction=validated_data['subproduction'], is_deleted=False, is_main=True).exists():
raise ValidationError('主产出只能有1个')
if OutputMaterial.objects.filter(material=validated_data['material'], subproduction=validated_data['subproduction'], is_deleted=False).exists(): if OutputMaterial.objects.filter(material=validated_data['material'], subproduction=validated_data['subproduction'], is_deleted=False).exists():
raise ValidationError('该物料已存在') raise ValidationError('该物料已存在')
return super().create(validated_data) return super().create(validated_data)
@ -108,7 +110,7 @@ class OutputMaterialSerializer(serializers.ModelSerializer):
class OutputMaterialUpdateSerializer(serializers.ModelSerializer): class OutputMaterialUpdateSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = OutputMaterial model = OutputMaterial
fields = ['count', 'sort'] fields = ['count', 'sort', 'is_main']
class UsedStepCreateSerializer(serializers.ModelSerializer): class UsedStepCreateSerializer(serializers.ModelSerializer):
""" """

View File

@ -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]) queryset = queryset.filter(participant__contains=user.id).exclude(act_state__in=[Ticket.TICKET_ACT_STATE_FINISH, Ticket.TICKET_ACT_STATE_CLOSED])
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':
pass
elif value == 'all': elif value == 'all':
pass pass
else: else:

View File

@ -0,0 +1,40 @@
# Generated by Django 3.2.6 on 2021-10-21 02:49
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('system', '0003_auto_20210812_0909'),
('wf', '0014_wfscript'),
]
operations = [
migrations.AddField(
model_name='state',
name='filter_policy',
field=models.IntegerField(choices=[(0, ''), (1, '和工单同属一及上级部门'), (2, '和创建人同属一及上级部门'), (3, '和上步处理人同属一及上级部门')], default=0, verbose_name='参与人过滤策略'),
),
migrations.AddField(
model_name='ticket',
name='belong_dept',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='ticket_belong_dept', to='system.organization', verbose_name='所属部门'),
),
migrations.AlterField(
model_name='state',
name='participant_type',
field=models.IntegerField(blank=True, choices=[(0, '无处理人'), (1, '个人'), (2, '多人'), (3, '部门'), (4, '角色'), (5, '变量'), (6, '脚本'), (7, '工单的字段'), (8, '父工单的字段'), (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, '多人'), (3, '部门'), (4, '角色'), (5, '变量'), (6, '脚本'), (7, '工单的字段'), (8, '父工单的字段'), (9, '代码获取')], default=0, help_text='0.无处理人,1.个人,2.多人', verbose_name='当前处理人类型'),
),
migrations.AlterField(
model_name='ticketflow',
name='participant_type',
field=models.IntegerField(choices=[(0, '无处理人'), (1, '个人'), (2, '多人'), (3, '部门'), (4, '角色'), (5, '变量'), (6, '脚本'), (7, '工单的字段'), (8, '父工单的字段'), (9, '代码获取')], default=0, help_text='0.无处理人,1.个人,2.多人', verbose_name='处理人类型'),
),
]

View File

@ -1,3 +1,4 @@
from random import choice
from django.db import models from django.db import models
from django.db.models.base import Model from django.db.models.base import Model
import django.utils.timezone as timezone import django.utils.timezone as timezone
@ -39,16 +40,18 @@ class State(CommonAModel):
PARTICIPANT_TYPE_ROBOT = 6 PARTICIPANT_TYPE_ROBOT = 6
PARTICIPANT_TYPE_FIELD = 7 PARTICIPANT_TYPE_FIELD = 7
PARTICIPANT_TYPE_PARENT_FIELD = 8 PARTICIPANT_TYPE_PARENT_FIELD = 8
PARTICIPANT_TYPE_FORMCODE = 9
state_participanttype_choices = ( state_participanttype_choices = (
(0, '无处理人'), (0, '无处理人'),
(PARTICIPANT_TYPE_PERSONAL, '个人'), (PARTICIPANT_TYPE_PERSONAL, '个人'),
(PARTICIPANT_TYPE_MULTI, '多人'), (PARTICIPANT_TYPE_MULTI, '多人'),
(PARTICIPANT_TYPE_DEPT, '部门'), # (PARTICIPANT_TYPE_DEPT, '部门'),
(PARTICIPANT_TYPE_ROLE, '角色'), (PARTICIPANT_TYPE_ROLE, '角色'),
(PARTICIPANT_TYPE_VARIABLE, '变量'), # (PARTICIPANT_TYPE_VARIABLE, '变量'),
(PARTICIPANT_TYPE_ROBOT, '脚本'), (PARTICIPANT_TYPE_ROBOT, '脚本'),
(PARTICIPANT_TYPE_FIELD, '工单的字段'), (PARTICIPANT_TYPE_FIELD, '工单的字段'),
(PARTICIPANT_TYPE_PARENT_FIELD, '父工单的字段') # (PARTICIPANT_TYPE_PARENT_FIELD, '父工单的字段'),
(PARTICIPANT_TYPE_FORMCODE, '代码获取')
) )
STATE_DISTRIBUTE_TYPE_ACTIVE = 1 # 主动接单 STATE_DISTRIBUTE_TYPE_ACTIVE = 1 # 主动接单
STATE_DISTRIBUTE_TYPE_DIRECT = 2 # 直接处理(当前为多人的情况,都可以处理,而不需要先接单) STATE_DISTRIBUTE_TYPE_DIRECT = 2 # 直接处理(当前为多人的情况,都可以处理,而不需要先接单)
@ -64,6 +67,13 @@ class State(CommonAModel):
STATE_FIELD_READONLY= 1 # 字段只读 STATE_FIELD_READONLY= 1 # 字段只读
STATE_FIELD_REQUIRED = 2 # 字段必填 STATE_FIELD_REQUIRED = 2 # 字段必填
STATE_FIELD_OPTIONAL = 3 # 字段可选 STATE_FIELD_OPTIONAL = 3 # 字段可选
state_filter_choices=(
(0, ''),
(1, '和工单同属一及上级部门'),
(2, '和创建人同属一及上级部门'),
(3, '和上步处理人同属一及上级部门'),
)
name = models.CharField('名称', max_length=50) name = models.CharField('名称', max_length=50)
workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='所属工作流') workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='所属工作流')
is_hidden = models.BooleanField('是否隐藏', default=False, help_text='设置为True时,获取工单步骤api中不显示此状态(当前处于此状态时除外)') is_hidden = models.BooleanField('是否隐藏', default=False, help_text='设置为True时,获取工单步骤api中不显示此状态(当前处于此状态时除外)')
@ -74,6 +84,7 @@ class State(CommonAModel):
participant = models.JSONField('参与者', default=list, blank=True, help_text='可以为空(无处理人的情况,如结束状态)、userid、userid列表\部门id\角色id\变量(create_by,create_by_tl)\脚本记录的id等包含子工作流的需要设置处理人为loonrobot') 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不显示, 字典的字典 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)
class Transition(CommonAModel): class Transition(CommonAModel):
""" """
@ -155,7 +166,7 @@ class CustomField(CommonAModel):
help_text='radio,checkbox,select,multiselect类型可供选择的选项格式为json如:{"1":"中国", "2":"美国"},注意数字也需要引号') help_text='radio,checkbox,select,multiselect类型可供选择的选项格式为json如:{"1":"中国", "2":"美国"},注意数字也需要引号')
label = models.JSONField('标签', blank=True, default=dict, help_text='自定义标签json格式调用方可根据标签自行处理特殊场景逻辑loonflow只保存文本内容') label = models.JSONField('标签', blank=True, default=dict, help_text='自定义标签json格式调用方可根据标签自行处理特殊场景逻辑loonflow只保存文本内容')
class Ticket(CommonAModel): class Ticket(CommonBModel):
""" """
工单 工单
""" """
@ -177,9 +188,9 @@ class Ticket(CommonAModel):
category_choices =( category_choices =(
('all', '全部'), ('all', '全部'),
('owner', '我创建的'), ('owner', '我创建的'),
('duty', ''), ('duty', ''),
('worked', '我处理的'), ('worked', '我处理的'),
('relation', '抄送我的') ('cc', '抄送我的')
) )
title = models.CharField('标题', max_length=500, blank=True, default='', help_text="工单标题") title = models.CharField('标题', max_length=500, blank=True, default='', help_text="工单标题")
workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='关联工作流') workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='关联工作流')
@ -209,7 +220,6 @@ class TicketFlow(BaseModel):
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)
class WfScript(CommonAModel): class WfScript(CommonAModel):
""" """
执行脚本 执行脚本
@ -222,5 +232,6 @@ class WfScript(CommonAModel):
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)
func_str = models.CharField('函数名', max_length=50, null=True, blank=True)
content = models.TextField('脚本内容') content = models.TextField('脚本内容')

View File

@ -0,0 +1,14 @@
class GetParticipants:
all_funcs = [
{'func':'get_create_by', 'name':'获取工单创建人'}
]
# def all_funcs(self):
# # return list(filter(lambda x: x.startswith('get_') and callable(getattr(self, x)), dir(self)))
# return [(func, getattr(self, func).__doc__) for func in dir(self) if callable(getattr(self, func)) and func.startswith('get_')]
def get_create_by(self, state:dict={}, ticket:dict={}, ticket_data:dict={}, request={}):
"""工单创建人"""
participant = ticket.create_by.id
return participant

View File

@ -7,6 +7,7 @@ from rest_framework.exceptions import APIException
from django.utils import timezone from django.utils import timezone
from datetime import timedelta from datetime import timedelta
import random import random
from .scripts import GetParticipants
class WfService(object): class WfService(object):
@staticmethod @staticmethod
@ -104,7 +105,7 @@ class WfService(object):
@classmethod @classmethod
def get_next_state_by_transition_and_ticket_info(cls, ticket:Ticket, transition: Transition, ticket_data:dict={})->object: def get_next_state_by_transition_and_ticket_info(cls, ticket:Ticket, transition: Transition, ticket_data:dict={}, request:dict={})->object:
""" """
获取下个节点状态 获取下个节点状态
""" """
@ -119,12 +120,12 @@ class WfService(object):
for i in transition.condition_expression: for i in transition.condition_expression:
expression = i['expression'].format(**ticket_all_value) expression = i['expression'].format(**ticket_all_value)
import datetime, time # 用于支持条件表达式中对时间的操作 import datetime, time # 用于支持条件表达式中对时间的操作
if eval(expression): if eval(expression, {"__builtins__":None}, {'datetime':datetime, 'time':time}):
destination_state = State.objects.get(pk=i['target_state']) destination_state = State.objects.get(pk=i['target_state'])
return destination_state return destination_state
@classmethod @classmethod
def get_ticket_state_participant_info(cls, state:State, ticket:Ticket, ticket_data:dict={}): def get_ticket_state_participant_info(cls, state:State, ticket:Ticket, ticket_data:dict={}, request={}):
""" """
获取工单目标状态实际的处理人, 处理人类型 获取工单目标状态实际的处理人, 处理人类型
""" """
@ -147,15 +148,29 @@ class WfService(object):
if destination_participant_type == State.PARTICIPANT_TYPE_FIELD: if destination_participant_type == State.PARTICIPANT_TYPE_FIELD:
destination_participant = ticket_data.get(destination_participant, 0) if destination_participant in ticket_data \ destination_participant = ticket_data.get(destination_participant, 0) if destination_participant in ticket_data \
else Ticket.ticket_data.get(destination_participant, 0) else Ticket.ticket_data.get(destination_participant, 0)
elif destination_participant_type == State.PARTICIPANT_TYPE_DEPT:#单部门
destination_participant = list(User.objects.filter(dept=destination_participant).values_list('id', flat=True))
elif destination_participant_type == State.PARTICIPANT_TYPE_ROLE:#单角色 elif destination_participant_type == State.PARTICIPANT_TYPE_FORMCODE:#代码获取
destination_participant = list(User.objects.filter(roles=destination_participant).values_list('id', flat=True)) destination_participant = getattr(GetParticipants, destination_participant)(state=state, ticket=ticket, ticket_data=ticket_data, request=request)
elif destination_participant_type == State.PARTICIPANT_TYPE_DEPT:#部门
destination_participant = list(User.objects.filter(dept__in=destination_participant).values_list('id', flat=True))
elif destination_participant_type == State.PARTICIPANT_TYPE_ROLE:#角色
user_queryset = User.objects.filter(roles__in=destination_participant)
# 如果选择了角色, 需要走过滤策略
if ticket.filter_policy == 1:
user_queryset = user_queryset.filter(dept=ticket.belong_dept)
elif ticket.filter_policy == 2:
user_queryset = user_queryset.filter(dept=ticket.create_by.dept)
elif ticket.filter_policy == 3:
user_queryset = user_queryset.filter(dept=request.user.dept)
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
destination_participant = list(set(destination_participant)) destination_participant = list(set(destination_participant))
if len(destination_participant) == 1: # 如果只有一个人
destination_participant_type = State.PARTICIPANT_TYPE_PERSONAL
destination_participant = destination_participant[0]
else: else:
destination_participant_type = State.PARTICIPANT_TYPE_PERSONAL destination_participant_type = State.PARTICIPANT_TYPE_PERSONAL
if destination_participant_type == State.PARTICIPANT_TYPE_MULTI: if destination_participant_type == State.PARTICIPANT_TYPE_MULTI:

View File

@ -1,6 +1,6 @@
from django.db.models import base from django.db.models import base
from rest_framework import urlpatterns from rest_framework import urlpatterns
from apps.wf.views import CustomFieldViewSet, StateViewSet, TicketFlowViewSet, TicketViewSet, TransitionViewSet, WorkflowViewSet from apps.wf.views import CustomFieldViewSet, FromCodeListView, StateViewSet, TicketFlowViewSet, TicketViewSet, TransitionViewSet, WorkflowViewSet
from django.urls import path, include from django.urls import path, include
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
@ -12,6 +12,7 @@ router.register('customfield', CustomFieldViewSet, basename='wf_customfield')
router.register('ticket', TicketViewSet, basename='wf_ticket') router.register('ticket', TicketViewSet, basename='wf_ticket')
router.register('ticketflow', TicketFlowViewSet, basename='wf_ticketflow') router.register('ticketflow', TicketFlowViewSet, basename='wf_ticketflow')
urlpatterns = [ urlpatterns = [
path('participant_from_code', FromCodeListView.as_view()),
path('', include(router.urls)), path('', include(router.urls)),
] ]

View File

@ -1,4 +1,5 @@
from django.db.models import query from django.db.models import query
from rest_framework.views import APIView
from apps.system.models import User from apps.system.models import User
from apps.wf.filters import TicketFilterSet from apps.wf.filters import TicketFilterSet
from django.core.exceptions import AppRegistryNotReady from django.core.exceptions import AppRegistryNotReady
@ -15,7 +16,16 @@ from apps.wf.services import WfService
from rest_framework.exceptions import APIException, PermissionDenied from rest_framework.exceptions import APIException, PermissionDenied
from rest_framework import status from rest_framework import status
from django.db.models import Count from django.db.models import Count
from .scripts import GetParticipants
# Create your views here. # Create your views here.
class FromCodeListView(APIView):
def get(self, request, format=None):
"""
获取处理人代码列表
"""
return Response(GetParticipants.all_funcs)
class WorkflowViewSet(CreateUpdateModelAMixin, ModelViewSet): class WorkflowViewSet(CreateUpdateModelAMixin, ModelViewSet):
perms_map = {'get': '*', 'post': 'workflow_create', perms_map = {'get': '*', 'post': 'workflow_create',
'put': 'workflow_update', 'delete': 'workflow_delete'} 'put': 'workflow_update', 'delete': 'workflow_delete'}
@ -133,7 +143,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
if value == State.STATE_FIELD_REQUIRED: if value == State.STATE_FIELD_REQUIRED:
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) # 先创建出来 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)
@ -201,7 +211,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
if value == State.STATE_FIELD_REQUIRED: if value == State.STATE_FIELD_REQUIRED:
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))
destination_state = WfService.get_next_state_by_transition_and_ticket_info(ticket, transition, ticket_data) destination_state = WfService.get_next_state_by_transition_and_ticket_info(ticket, transition, ticket_data, request)
multi_all_person = ticket.multi_all_person multi_all_person = ticket.multi_all_person
if multi_all_person: if multi_all_person:
multi_all_person[request.user.id] =dict(transition=transition.id) multi_all_person[request.user.id] =dict(transition=transition.id)

View File

@ -5,6 +5,28 @@ 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
class Good(CommonAModel):
"""
物品
"""
act_state_choices=(
(0, '待执行'),
(1, '进行中'),
(2, '已完成')
)
number = models.CharField('物品编号', primary_key=True, null=True, blank=True, max_length=50)
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)
act_state = models.IntegerField('进行状态', default=0)
class GoodFlow(BaseModel):
"""
物品流转日志
"""
pass
class Vendor(CommonAModel): class Vendor(CommonAModel):

View File

@ -10,3 +10,4 @@ drf-yasg==1.20.0
psutil==5.8.0 psutil==5.8.0
pillow==8.3.1 pillow==8.3.1
opencv-python==4.5.3.56 opencv-python==4.5.3.56
django-celery-results==2.2.0

View File

@ -41,6 +41,7 @@ INSTALLED_APPS = [
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'corsheaders', 'corsheaders',
'django_celery_beat', 'django_celery_beat',
'django_celery_results',
'drf_yasg', 'drf_yasg',
'rest_framework', 'rest_framework',
"django_filters", "django_filters",
@ -55,7 +56,8 @@ INSTALLED_APPS = [
'apps.inm', 'apps.inm',
'apps.sam', 'apps.sam',
'apps.qm', 'apps.qm',
'apps.pm' 'apps.pm',
# 'apps.wpm'
] ]
MIDDLEWARE = [ MIDDLEWARE = [
@ -202,6 +204,7 @@ CELERY_BROKER_URL = "redis://redis:6379/0" # 任务存储
CELERYD_MAX_TASKS_PER_CHILD = 100 # 每个worker最多执行300个任务就会被销毁可防止内存泄露 CELERYD_MAX_TASKS_PER_CHILD = 100 # 每个worker最多执行300个任务就会被销毁可防止内存泄露
CELERY_TIMEZONE = 'Asia/Shanghai' # 设置时区 CELERY_TIMEZONE = 'Asia/Shanghai' # 设置时区
CELERY_ENABLE_UTC = True # 启动时区设置 CELERY_ENABLE_UTC = True # 启动时区设置
CELERY_RESULT_BACKEND = 'django-db'
# swagger配置 # swagger配置
SWAGGER_SETTINGS = { SWAGGER_SETTINGS = {