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

This commit is contained in:
shijing 2021-10-19 16:38:48 +08:00
commit 76d1142741
22 changed files with 395 additions and 71 deletions

View File

@ -3,4 +3,5 @@ ENV = 'production'
# base api
VUE_APP_BASE_API = 'http://47.95.0.242:2222/api'
#VUE_APP_BASE_API = 'http://127.0.0.1:8000/api'

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-10-18 05:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('hrm', '0002_auto_20210924_1127'),
]
operations = [
migrations.AddField(
model_name='employee',
name='face_data',
field=models.JSONField(blank=True, null=True, verbose_name='人脸识别数据'),
),
]

View File

@ -28,9 +28,12 @@ class Employee(CommonAModel):
academic = models.CharField('学历', max_length=50, null=True, blank=True)
jobstate = models.IntegerField('在职状态', choices=jobstate_choices, default=1)
job = models.ForeignKey(Position, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='岗位')
face_data = models.JSONField('人脸识别数据', null=True, blank=True)
class Meta:
verbose_name = '员工补充信息'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
return self.name

View File

@ -1,6 +1,6 @@
from apps.system.models import User
from rest_framework.serializers import ModelSerializer
from rest_framework import serializers
from .models import Employee
from apps.system.serializers import UserListSerializer, UserSimpleSerializer
from django.db.models.query import Prefetch
@ -20,3 +20,6 @@ class EmployeeSerializer(ModelSerializer):
# queryset=User.objects.filter(employee_user__isnull=True))
# )
# return queryset
class FaceLoginSerializer(serializers.Serializer):
base64 = serializers.CharField()

View File

@ -1,12 +1,13 @@
from django.db.models import base
from rest_framework import urlpatterns
from apps.hrm.views import EmployeeViewSet
from apps.hrm.views import EmployeeViewSet, FaceLogin
from django.urls import path, include
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register('employee', EmployeeViewSet, basename='employee')
urlpatterns = [
path('facelogin/', FaceLogin.as_view()),
path('', include(router.urls)),
]

View File

@ -1,9 +1,34 @@
from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet, GenericViewSet
from rest_framework.mixins import UpdateModelMixin, RetrieveModelMixin
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
from apps.hrm.models import Employee
from apps.hrm.serializers import EmployeeSerializer
from apps.hrm.serializers import EmployeeSerializer, FaceLoginSerializer
import face_recognition
from django.conf import settings
from django.core.cache import cache
import logging
from rest_framework.generics import CreateAPIView
from rest_framework import status
from rest_framework_simplejwt.tokens import RefreshToken
from apps.system.models import User
logger = logging.getLogger('log')
def load_face_data(username:int, path:str):
"""
将某用户face_encoding加载进缓存
"""
face_datas = cache.get_or_set('face_datas', {}, timeout=None)
photo_path = settings.BASE_DIR + path
picture_of_me = face_recognition.load_image_file(photo_path)
my_face_encoding = face_recognition.face_encodings(picture_of_me)[0]
face_datas[username] = my_face_encoding
cache.set('face_datas', face_datas, timeout=None)
return my_face_encoding
# Create your views here.
class EmployeeViewSet(CreateUpdateModelAMixin, OptimizationMixin, UpdateModelMixin, RetrieveModelMixin, GenericViewSet):
"""
@ -12,4 +37,73 @@ class EmployeeViewSet(CreateUpdateModelAMixin, OptimizationMixin, UpdateModelMix
perms_map = {'get': '*', 'put': 'employee_update'}
queryset = Employee.objects.all()
serializer_class = EmployeeSerializer
ordering = ['-pk']
ordering = ['-pk']
def perform_update(self, serializer):
instance = serializer.save(update_by = self.request.user)
try:
photo_path = settings.BASE_DIR + instance.photo
picture_of_me = face_recognition.load_image_file(photo_path)
my_face_encoding = face_recognition.face_encodings(picture_of_me)[0]
instance.face_data = my_face_encoding.tolist()
instance.save()
except:
logger.error('人脸识别出错')
import uuid
import base64
import os
def tran64(s):
missing_padding = len(s) % 4
if missing_padding != 0:
s = s+'='* (4 - missing_padding)
return s
class FaceLogin(CreateAPIView):
authentication_classes = []
permission_classes = []
serializer_class = FaceLoginSerializer
def create(self, request, *args, **kwargs):
"""
人脸识别登录
"""
# serializer = FaceLoginSerializer(data=request.data)
# serializer.is_valid(raise_exception=True)
filename = str(uuid.uuid4())
filepath = settings.BASE_DIR +'/temp/' + filename +'.png'
with open(filepath, 'wb') as f:
data = tran64(request.data.get('base64').replace(' ', '+'))
f.write(base64.urlsafe_b64decode(data))
# picture_of_me = face_recognition.load_image_file(settings.BASE_DIR +'/temp/me.png')
# my_face_encoding = face_recognition.face_encodings(picture_of_me)[0]
#results = face_recognition.compare_faces([my_face_encoding], unknown_face_encoding, tolerance=0.2)
try:
unknown_picture = face_recognition.load_image_file(filepath)
unknown_face_encoding = face_recognition.face_encodings(unknown_picture)[0]
os.remove(filepath)
except:
return Response('头像解码失败', status=status.HTTP_400_BAD_REQUEST)
# 匹配人脸库
user_faces = Employee.objects.filter(face_data__isnull=False, user__is_active=True).values('user', 'face_data')
user_l = []
face_l = []
for i in user_faces:
user_l.append(i['user'])
face_l.append(i['face_data'])
results = face_recognition.compare_faces(face_l, unknown_face_encoding, tolerance=0.5)
for index, value in enumerate(results):
if value:
# 识别成功
user = User.objects.get(id=user_l[index])
refresh = RefreshToken.for_user(user)
return Response({
'refresh': str(refresh),
'access': str(refresh.access_token),
'username':user.username
})
return Response('未找到对应用户', status=status.HTTP_400_BAD_REQUEST)

View File

@ -0,0 +1,49 @@
# Generated by Django 3.2.6 on 2021-10-18 02:57
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('mtm', '0022_auto_20211014_0944'),
]
operations = [
migrations.AddField(
model_name='recordformfield',
name='high_limit',
field=models.FloatField(blank=True, null=True, verbose_name='上限值'),
),
migrations.AddField(
model_name='recordformfield',
name='high_rule',
field=models.IntegerField(blank=True, choices=[(1, '小于'), (2, '小于等于')], null=True, verbose_name='上限规则'),
),
migrations.AddField(
model_name='recordformfield',
name='low_limit',
field=models.FloatField(blank=True, null=True, verbose_name='下限值'),
),
migrations.AddField(
model_name='recordformfield',
name='low_rule',
field=models.IntegerField(blank=True, choices=[(1, '大于'), (2, '大于等于')], null=True, verbose_name='下限规则'),
),
migrations.AddField(
model_name='recordformfield',
name='need_judge',
field=models.BooleanField(default=False, verbose_name='需要判定'),
),
migrations.AddField(
model_name='recordformfield',
name='rule_expression',
field=models.JSONField(default=list, help_text='判定表达式, 格式为[{"expression":"{value} > 3 and {value}<10"}] 其中{}用于填充工单的字段key,运算时会换算成实际的值符合条件返回true,表达式只支持简单的运算或datetime/time运算.以首次匹配成功的条件为准,所以多个条件不要有冲突', verbose_name='判定表达式'),
),
migrations.AlterField(
model_name='usedstep',
name='step',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='usedstep', to='mtm.step', verbose_name='子工序'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-10-19 01:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mtm', '0023_auto_20211018_1057'),
]
operations = [
migrations.AlterField(
model_name='recordformfield',
name='rule_expression',
field=models.JSONField(default=list, help_text='判定表达式, 格式为[{"expression":"{value} > 3 and {value}<10"}] 其中{}用于填充的字段key,运算时会换算成实际的值符合条件返回true,表达式只支持简单的运算或datetime/time运算.以首次匹配成功的条件为准,所以多个条件不要有冲突', verbose_name='判定表达式'),
),
]

View File

@ -110,6 +110,14 @@ class RecordFormField(CommonAModel):
('selects', '多选下拉'),
('textarea', '文本域'),
)
high_rule_choices = (
(1, '小于'),
(2, '小于等于'),
)
low_rule_choices = (
(1, '大于'),
(2, '大于等于'),
)
form = models.ForeignKey(RecordForm, on_delete=models.CASCADE, verbose_name='关联表格')
field_type = models.CharField('类型', max_length=50, choices=field_type_choices)
field_key = models.CharField('字段标识', max_length=50, help_text='字段类型请尽量特殊,避免与系统中关键字冲突')
@ -119,6 +127,13 @@ class RecordFormField(CommonAModel):
field_choice = models.JSONField('radio、checkbox、select的选项', default=dict, blank=True, null=True,
help_text='radio,checkbox,select,multiselect类型可供选择的选项格式为json如:{"1":"中国", "2":"美国"},注意数字也需要引号')
sort = models.IntegerField('排序号', default=1)
need_judge = models.BooleanField('需要判定', default=False)
high_limit = models.FloatField('上限值', null=True, blank=True)
high_rule = models.IntegerField('上限规则', choices=high_rule_choices, null=True, blank=True)
low_limit = models.FloatField('下限值', null=True, blank=True)
low_rule = models.IntegerField('下限规则', choices=low_rule_choices, null=True, blank=True)
rule_expression = models.JSONField('判定表达式', default=list, help_text='判定表达式, 格式为[{"expression":"{value} > 3 and {value}<10"}] 其中{}用于填充的字段key,运算时会换算成实际的值符合条件返回true,表达式只支持简单的运算或datetime/time运算.以首次匹配成功的条件为准,所以多个条件不要有冲突' )
class Meta:
verbose_name = '记录表格字段'
verbose_name_plural = verbose_name

View File

@ -175,7 +175,7 @@ class RecordFormFieldSerializer(serializers.ModelSerializer):
class RecordFormFieldCreateSerializer(serializers.ModelSerializer):
class Meta:
model = RecordFormField
fields = ['form', 'field_type', 'field_key', 'field_name', 'boolean_field_display', 'field_choice', 'sort']
fields = ['form', 'field_type', 'field_key', 'field_name', 'boolean_field_display', 'field_choice', 'sort', 'need_judge', 'high_limit', 'high_rule', 'low_limit', 'low_rule', 'rule_expression']
def validate(self, data):
if RecordFormField.objects.filter(field_key=data['field_key'], form=data['form'], is_deleted=False).exists():
@ -185,7 +185,7 @@ class RecordFormFieldCreateSerializer(serializers.ModelSerializer):
class RecordFormFieldUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = RecordFormField
fields = ['field_type', 'field_name', 'boolean_field_display', 'field_choice', 'sort']
fields = ['field_type', 'field_name', 'boolean_field_display', 'field_choice', 'sort', 'need_judge', 'high_limit', 'high_rule', 'low_limit', 'low_rule', 'rule_expression']
class RecordFormFieldSimpleSerializer(serializers.ModelSerializer):
class Meta:

View File

@ -0,0 +1,38 @@
# Generated by Django 3.2.6 on 2021-10-19 01:44
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('mtm', '0024_alter_recordformfield_rule_expression'),
('pm', '0004_subproductionplan_steps'),
]
operations = [
migrations.AddField(
model_name='subproductionplan',
name='state',
field=models.IntegerField(default=0, verbose_name='状态'),
),
migrations.CreateModel(
name='SubProductionProgress',
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='删除标记')),
('type', models.IntegerField(default=1, verbose_name='物料应用类型')),
('count', models.IntegerField(verbose_name='应出入数')),
('count_real', models.IntegerField(verbose_name='实际出入数')),
('material', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='关联物料')),
('subproduction_plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='progress_subplan', to='pm.subproductionplan', verbose_name='关联子生产计划')),
],
options={
'abstract': False,
},
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-10-19 01:58
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pm', '0005_auto_20211019_0944'),
]
operations = [
migrations.AlterField(
model_name='subproductionprogress',
name='count_real',
field=models.IntegerField(default=0, verbose_name='实际出入数'),
),
]

View File

@ -31,6 +31,13 @@ class SubProductionPlan(CommonAModel):
"""
子生产计划
"""
state_choices=(
(0, '制定中'),
(1, '已下达'),
(2, '已接收'),
(3, '生产中'),
(4, '已完成')
)
production_plan = models.ForeignKey(ProductionPlan, verbose_name='关联主生产计划', on_delete=models.CASCADE)
subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE)
start_date = models.DateField('计划开工日期')
@ -38,13 +45,21 @@ class SubProductionPlan(CommonAModel):
workshop = models.ForeignKey(Organization, verbose_name='生产车间', on_delete=models.CASCADE)
process = models.ForeignKey(Process, verbose_name='关联大工序', on_delete=models.CASCADE)
steps = models.JSONField('工艺步骤', default=list)
state = models.IntegerField('状态', default=0)
class Meta:
verbose_name = '子生产计划'
verbose_name_plural = verbose_name
# class ProductionProgress(BaseModel):
# """
# 子计划生产进度
# """
# subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.CASCADE)
# material = models.
class SubProductionProgress(BaseModel):
"""
子计划生产进度统计表
"""
type_choices=(
(1, '输入物料'),
(2, '输出物料')
)
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.CASCADE, related_name='progress_subplan')
material = models.ForeignKey(Material, verbose_name='关联物料', on_delete=models.CASCADE)
type = models.IntegerField('物料应用类型', default=1)
count = models.IntegerField('应出入数')
count_real = models.IntegerField('实际出入数', default=0)

View File

@ -1,4 +1,4 @@
from apps.pm.models import ProductionPlan, SubProductionPlan
from apps.pm.models import ProductionPlan, SubProductionPlan, SubProductionProgress
from rest_framework import serializers
from apps.sam.serializers import OrderSerializer
from apps.mtm.serializers import MaterialSimpleSerializer, ProcessSimpleSerializer
@ -38,3 +38,9 @@ class SubProductionPlanUpdateSerializer(serializers.ModelSerializer):
class GenSubPlanSerializer(serializers.Serializer):
pass
class SubProductionProgressSerializer(serializers.ModelSerializer):
material_ = MaterialSimpleSerializer(source='material', read_only=True)
class Meta:
model = SubProductionProgress
fields = '__all__'

View File

@ -2,11 +2,11 @@ from rest_framework import serializers
from rest_framework.views import APIView
from apps.em.models import Equipment
from apps.em.serializers import EquipmentSerializer
from apps.mtm.models import InputMaterial, Step, SubProduction, UsedStep
from apps.mtm.models import InputMaterial, OutputMaterial, Step, SubProduction, UsedStep
from apps.system.mixins import CreateUpdateModelAMixin
from apps.pm.serializers import GenSubPlanSerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer
from apps.pm.serializers import GenSubPlanSerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer, SubProductionProgressSerializer
from rest_framework.mixins import CreateModelMixin, ListModelMixin, UpdateModelMixin
from apps.pm.models import ProductionPlan, SubProductionPlan
from apps.pm.models import ProductionPlan, SubProductionProgress, SubProductionPlan
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from django.shortcuts import render
from apps.sam.models import Order
@ -72,10 +72,14 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel
for i in subps:
steps = Step.objects.filter(usedstep__subproduction=i, usedstep__subproduction__is_deleted=False,
usedstep__is_deleted=False, is_deleted=False).values('id', 'number', 'name', 'usedstep__remark')
SubProductionPlan.objects.create(production_plan=production_plan, subproduction=i,
instance = SubProductionPlan.objects.create(production_plan=production_plan, subproduction=i,
start_date=production_plan.start_date, end_date=production_plan.end_date,
workshop=i.process.workshop, process=i.process, create_by=request.user,
steps = list(steps))
for m in InputMaterial.objects.filter(subproduction=i, is_deleted=False).order_by('sort'):
SubProductionProgress.objects.create(material=m.material, type=1, count=m.count, subproduction_plan=instance)
for m in OutputMaterial.objects.filter(subproduction=i, is_deleted=False).order_by('sort'):
SubProductionProgress.objects.create(material=m.material, type=2, count=m.count, subproduction_plan=instance)
production_plan.is_planed=True
production_plan.save()
return Response()
@ -98,7 +102,26 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo
return SubProductionPlanUpdateSerializer
return SubProductionPlanListSerializer
@action(methods=['get'], detail=True, perms_map={'get':'*'}, serializer_class=SubProductionProgressSerializer)
def progress(self, request, pk=None):
"""
生产进度详情
"""
obj = self.get_object()
serializer = SubProductionProgressSerializer(instance=obj.progress_subplan, many=True)
return Response(serializer.data)
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=serializers.Serializer)
def issue(self, request, pk=None):
"""
下达任务
"""
obj = self.get_object()
if obj.state == 0:
obj.state = 1
obj.save()
return Response()
raise APIException('计划状态有误')
class ResourceViewSet(GenericViewSet):
perms_map = {'*': '*'}

View File

@ -199,5 +199,3 @@ class UserCreateSerializer(serializers.ModelSerializer):
return phone
class FaceLoginSerializer(serializers.Serializer):
base64 = serializers.CharField()

View File

@ -1,5 +1,5 @@
from django.urls import path, include
from .views import FaceLogin, TaskList, UserViewSet, OrganizationViewSet, PermissionViewSet, RoleViewSet, PositionViewSet, TestView, DictTypeViewSet, DictViewSet, PTaskViewSet
from .views import TaskList, UserViewSet, OrganizationViewSet, PermissionViewSet, RoleViewSet, PositionViewSet, TestView, DictTypeViewSet, DictViewSet, PTaskViewSet
from rest_framework import routers
@ -15,6 +15,5 @@ router.register('ptask', PTaskViewSet, basename="ptask")
urlpatterns = [
path('', include(router.urls)),
path('task/', TaskList.as_view()),
path('test/', TestView.as_view()),
path('facelogin/', FaceLogin.as_view())
path('test/', TestView.as_view())
]

View File

@ -21,6 +21,7 @@ from rest_framework.views import APIView
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework.exceptions import ValidationError, ParseError
from apps.hrm.models import Employee
from utils.queryset import get_child_queryset2
from .filters import UserFilter
@ -29,7 +30,7 @@ from .models import (Dict, DictType, File, Organization, Permission, Position,
Role, User)
from .permission import RbacPermission, get_permission_list
from .permission_data import RbacFilterSet
from .serializers import (DictSerializer, DictTypeSerializer, FaceLoginSerializer, FileSerializer,
from .serializers import (DictSerializer, DictTypeSerializer, FileSerializer,
OrganizationSerializer, PermissionSerializer,
PositionSerializer, RoleSerializer, PTaskSerializer,PTaskCreateUpdateSerializer,
UserCreateSerializer, UserListSerializer,
@ -352,43 +353,3 @@ class FileViewSet(CreateModelMixin, DestroyModelMixin, RetrieveModelMixin, ListM
instance.save()
#import face_recognition
import uuid
import base64
import os
def tran64(s):
missing_padding = len(s) % 4
if missing_padding != 0:
s = s+'='* (4 - missing_padding)
return s
class FaceLogin(CreateAPIView):
authentication_classes = []
permission_classes = []
serializer_class = FaceLoginSerializer
def create(self, request, *args, **kwargs):
"""
人脸识别登录
"""
# serializer = FaceLoginSerializer(data=request.data)
# serializer.is_valid(raise_exception=True)
filename = str(uuid.uuid4())
filepath = settings.BASE_DIR +'/temp/' + filename +'.png'
with open(filepath, 'wb') as f:
data = tran64(request.data.get('base64').replace(' ', '+'))
f.write(base64.urlsafe_b64decode(data))
# picture_of_me = face_recognition.load_image_file(settings.BASE_DIR +'/temp/me.png')
# my_face_encoding = face_recognition.face_encodings(picture_of_me)[0]
# unknown_picture = face_recognition.load_image_file(filepath)
# unknown_face_encoding = face_recognition.face_encodings(unknown_picture)[0]
#results = face_recognition.compare_faces([my_face_encoding], unknown_face_encoding, tolerance=0.2)
os.remove(filepath)
# if results[0] == True:
# return Response('这是曹前明')
# else:
# return Response('这不是曹前明')

View File

@ -0,0 +1,36 @@
# Generated by Django 3.2.6 on 2021-10-19 01:58
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('wf', '0013_alter_ticketflow_transition'),
]
operations = [
migrations.CreateModel(
name='WfScript',
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='删除标记')),
('usage', models.IntegerField(choices=[(1, '获取处理人'), (2, '执行操作')], default=1, verbose_name='脚本用途')),
('wait', models.BooleanField(default=True, verbose_name='是否等待执行完成')),
('name', models.CharField(max_length=100, verbose_name='脚本名称')),
('content', models.TextField(verbose_name='脚本内容')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wfscript_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wfscript_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
('workflow', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='wf.workflow', verbose_name='关联工作流')),
],
options={
'abstract': False,
},
),
]

View File

@ -207,4 +207,20 @@ class TicketFlow(BaseModel):
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 = 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):
"""
执行脚本
"""
usage_choices =(
(1, '获取处理人'),
(2, '执行操作'),
)
usage = models.IntegerField('脚本用途', default=1, choices=usage_choices)
wait = models.BooleanField('是否等待执行完成', default=True)
name = models.CharField('脚本名称', max_length=100)
workflow = models.ForeignKey(Workflow, verbose_name='关联工作流', null=True, blank=True, on_delete=models.SET_NULL)
content = models.TextField('脚本内容')

View File

@ -141,8 +141,8 @@ class WfService(object):
multi_all_person_dict = {}
destination_participant_type, destination_participant = state.participant_type, state.participant
if destination_participant_type == State.PARTICIPANT_TYPE_FIELD:
destination_participant = ticket_data.get(destination_participant, None) if destination_participant in ticket_data else Ticket.ticket_data.get(destination_participant, None)
destination_participant = ticket_data.get(destination_participant, 0) if destination_participant in ticket_data \
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))

View File

@ -1,3 +1,4 @@
from django.db.models import query
from apps.system.models import User
from apps.wf.filters import TicketFilterSet
from django.core.exceptions import AppRegistryNotReady
@ -13,7 +14,7 @@ from apps.system.mixins import CreateUpdateCustomMixin, CreateUpdateModelAMixin,
from apps.wf.services import WfService
from rest_framework.exceptions import APIException, PermissionDenied
from rest_framework import status
from django.db.models import Count
# Create your views here.
class WorkflowViewSet(CreateUpdateModelAMixin, ModelViewSet):
perms_map = {'get': '*', 'post': 'workflow_create',
@ -136,7 +137,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
next_state = WfService.get_next_state_by_transition_and_ticket_info(ticket=ticket, transition=transition)
participant_info = WfService.get_ticket_state_participant_info(state=next_state, ticket=ticket, ticket_data=ticket.ticket_data)
destination_participant_type = participant_info.get('destination_participant_type', 0)
destination_participant = participant_info.get('destination_participant', None)
destination_participant = participant_info.get('destination_participant', 0)
multi_all_person = participant_info.get('multi_all_person', {}) # 多人需要全部处理情况
sn = WfService.get_ticket_sn(ticket.workflow) # 流水号
if next_state.type == State.STATE_TYPE_END:
@ -165,6 +166,17 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
participant=ticket.create_by, transition=transition)
return Response(TicketSerializer(instance=ticket).data)
@action(methods=['get'], detail=False, perms_map={'get':'*'})
def duty_agg(self, request, pk=None):
"""
工单待办聚合
"""
ret = {}
queryset = Ticket.objects.filter(participant__contains=request.user.id, is_deleted=False)\
.exclude(act_state__in=[Ticket.TICKET_ACT_STATE_FINISH, Ticket.TICKET_ACT_STATE_CLOSED])
ret['total_count'] = queryset.count()
ret['details'] = list(queryset.annotate(count = Count('workflow')).values('workflow', 'workflow__name', 'count'))
return Response(ret)
@action(methods=['post'], detail=True, perms_map={'post':'*'})
def handle(self, request, pk=None):
@ -197,7 +209,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
if WfService.check_dict_has_all_same_value(multi_all_person):
participant_info = WfService.get_ticket_state_participant_info(destination_state, ticket, data['ticket_data'])
destination_participant_type = participant_info.get('destination_participant_type', 0)
destination_participant = participant_info.get('destination_participant', None)
destination_participant = participant_info.get('destination_participant', 0)
multi_all_person = {}
else:
# 处理人没有没有全部处理完成或者处理动作不一致
@ -211,7 +223,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
# 当前处理人类型非全部处理
participant_info = WfService.get_ticket_state_participant_info(destination_state, ticket, data['ticket_data'])
destination_participant_type = participant_info.get('destination_participant_type', 0)
destination_participant = participant_info.get('destination_participant', None)
destination_participant = participant_info.get('destination_participant', 0)
multi_all_person = participant_info.get('multi_all_person', {})
# 更新工单信息:基础字段及自定义字段, add_relation字段 需要下个处理人是部门、角色等的情况