Merge branch 'develop' of https://e.coding.net/ctcdevteam/hberp/hberp into develop
This commit is contained in:
commit
3772a9b715
|
@ -59,7 +59,6 @@
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
@change="keyChange($index,item.field_key)"
|
@change="keyChange($index,item.field_key)"
|
||||||
/>
|
/>
|
||||||
<!--</el-date-picker>-->
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
v-else-if="item.field_type === 'datetime'"
|
v-else-if="item.field_type === 'datetime'"
|
||||||
|
@ -73,7 +72,6 @@
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
@change="keyChange($index,item.field_key)"
|
@change="keyChange($index,item.field_key)"
|
||||||
/>
|
/>
|
||||||
<!--</el-date-picker>-->
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
v-else-if="item.field_type === 'select'"
|
v-else-if="item.field_type === 'select'"
|
||||||
|
@ -91,7 +89,6 @@
|
||||||
:label="item1"
|
:label="item1"
|
||||||
:value="item1"
|
:value="item1"
|
||||||
/>
|
/>
|
||||||
<!--</el-option>-->
|
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
|
@ -137,20 +134,15 @@
|
||||||
<div class="canvasBtn" @click="error1">标记</div>
|
<div class="canvasBtn" @click="error1">标记</div>
|
||||||
<div class="canvasBtn" @click="word1()">文字</div>
|
<div class="canvasBtn" @click="word1()">文字</div>
|
||||||
<div class="canvasBtn" @click="back()">回退</div>
|
<div class="canvasBtn" @click="back()">回退</div>
|
||||||
<!--<div class="canvasBtn" @click="saveTu()">保存</div>-->
|
<div class="canvasBtn" @click="saveTu()">保存</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="res"></div>
|
<div id="res"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-form-item label="是否合格">
|
<el-form-item label="是否合格">
|
||||||
<!--<el-radio-group v-model="is_testok">
|
|
||||||
<el-radio :label="testokTrue">检查合格</el-radio>
|
|
||||||
<el-radio :label="testokFalse">检查不合格</el-radio>
|
|
||||||
</el-radio-group>-->
|
|
||||||
<el-radio v-model="is_testok" :label="testokTrue">检查合格</el-radio>
|
<el-radio v-model="is_testok" :label="testokTrue">检查合格</el-radio>
|
||||||
<el-radio v-model="is_testok" :label="testokFalse">检查不合格</el-radio>
|
<el-radio v-model="is_testok" :label="testokFalse">检查不合格</el-radio>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
@ -160,7 +152,6 @@
|
||||||
<el-button type="warning" @click="submitfield('2')">提交</el-button>
|
<el-button type="warning" @click="submitfield('2')">提交</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -202,6 +193,11 @@
|
||||||
formData.forEach(item => {
|
formData.forEach(item => {
|
||||||
let obj = new Object();
|
let obj = new Object();
|
||||||
obj = item;
|
obj = item;
|
||||||
|
if(item.field_type === 'draw'){
|
||||||
|
obj.is_testok = true;
|
||||||
|
}else{
|
||||||
|
obj.is_testok = null;
|
||||||
|
}
|
||||||
that.formData.push(obj)
|
that.formData.push(obj)
|
||||||
});
|
});
|
||||||
that.formData=[...formData];
|
that.formData=[...formData];
|
||||||
|
@ -270,10 +266,8 @@
|
||||||
},
|
},
|
||||||
methods:{
|
methods:{
|
||||||
filterBlock(parent,rule,index,field_key){
|
filterBlock(parent,rule,index,field_key){
|
||||||
debugger;
|
|
||||||
let that = this;
|
let that = this;
|
||||||
if(parent!==''&&parent!==null&&parent!==undefined){
|
if(parent!==''&&parent!==null&&parent!==undefined){
|
||||||
debugger;
|
|
||||||
if(rule!==''&&rule!==null&&rule!==undefined){
|
if(rule!==''&&rule!==null&&rule!==undefined){
|
||||||
let reg = /\{(.+?)\}/g;
|
let reg = /\{(.+?)\}/g;
|
||||||
//let str = rule.replace(temp,'').replace('$','');//=='有'
|
//let str = rule.replace(temp,'').replace('$','');//=='有'
|
||||||
|
@ -515,7 +509,6 @@
|
||||||
if(v != '' && v != ' '){
|
if(v != '' && v != ' '){
|
||||||
this.imgData=ctx3.getImageData(0,0,canvas3.width,canvas3.height);
|
this.imgData=ctx3.getImageData(0,0,canvas3.width,canvas3.height);
|
||||||
let img = ctx3.getImageData(0,0,canvas3.width,canvas3.height);
|
let img = ctx3.getImageData(0,0,canvas3.width,canvas3.height);
|
||||||
// debugger;
|
|
||||||
preDrawAry.push(img);
|
preDrawAry.push(img);
|
||||||
ctx3.moveTo(oldX,oldY);
|
ctx3.moveTo(oldX,oldY);
|
||||||
ctx3.fillStyle="#e42343";
|
ctx3.fillStyle="#e42343";
|
||||||
|
@ -586,10 +579,7 @@
|
||||||
let formData = new FormData();
|
let formData = new FormData();
|
||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
upFile(formData).then((res) => {
|
upFile(formData).then((res) => {
|
||||||
debugger;
|
that.imgUrl=res.data.path;
|
||||||
console.log(res);
|
|
||||||
that.imgUrl=res.data.field;
|
|
||||||
// console.log(res);
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
//base64ToFile
|
//base64ToFile
|
||||||
|
@ -632,7 +622,6 @@
|
||||||
//替换变量
|
//替换变量
|
||||||
a = a.replace(ky, 'yyy');
|
a = a.replace(ky, 'yyy');
|
||||||
let yyy = "'"+that.checkForm[ky]+"'";
|
let yyy = "'"+that.checkForm[ky]+"'";
|
||||||
// debugger;
|
|
||||||
if(eval(eval( a))){
|
if(eval(eval( a))){
|
||||||
str += 'true';
|
str += 'true';
|
||||||
}else{
|
}else{
|
||||||
|
@ -684,8 +673,8 @@
|
||||||
upFile(formData).then((res) => {
|
upFile(formData).then((res) => {
|
||||||
if(res){
|
if(res){
|
||||||
let key = drawArr[0].field_key;
|
let key = drawArr[0].field_key;
|
||||||
that.imgUrl=res.data.file;
|
that.imgUrl=res.data.path;
|
||||||
that.checkForm[key] = res.data.file;
|
that.checkForm[key] = res.data.path;
|
||||||
that.fieldData(isSubmit);
|
that.fieldData(isSubmit);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -698,14 +687,12 @@
|
||||||
that.field = []; //检查项目
|
that.field = []; //检查项目
|
||||||
let submit = isSubmit=='1'?false:true;
|
let submit = isSubmit=='1'?false:true;
|
||||||
that.formData.forEach((item) => {
|
that.formData.forEach((item) => {
|
||||||
if(!item.is_hidden){
|
|
||||||
that.field.push({
|
that.field.push({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
field_value: that.checkForm[item.field_key],
|
field_value: that.checkForm[item.field_key],
|
||||||
is_testok: item.is_testok,//单项检查结果
|
is_testok: item.is_testok,//单项检查结果
|
||||||
is_hidden: item.is_hidden
|
is_hidden: item.is_hidden
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
that.testrecord.record_data = that.field;//检查项列表
|
that.testrecord.record_data = that.field;//检查项列表
|
||||||
that.testrecord.is_testok = that.is_testok;//检查表检查结果
|
that.testrecord.is_testok = that.is_testok;//检查表检查结果
|
||||||
|
|
|
@ -677,10 +677,7 @@
|
||||||
let formData = new FormData();
|
let formData = new FormData();
|
||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
upFile(formData).then((res) => {
|
upFile(formData).then((res) => {
|
||||||
debugger;
|
that.imgUrl=res.data.path;
|
||||||
console.log(res);
|
|
||||||
that.imgUrl=res.data.field;
|
|
||||||
// console.log(res);
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
//base64ToFile
|
//base64ToFile
|
||||||
|
@ -775,7 +772,7 @@
|
||||||
upFile(formData).then((res) => {
|
upFile(formData).then((res) => {
|
||||||
if(res){
|
if(res){
|
||||||
let key = drawArr[0].field_key;
|
let key = drawArr[0].field_key;
|
||||||
that.imgUrl=res.data.file;
|
that.imgUrl=res.data.path;
|
||||||
that.checkForm[key] = that.imgUrl;
|
that.checkForm[key] = that.imgUrl;
|
||||||
that.fieldData();
|
that.fieldData();
|
||||||
}
|
}
|
||||||
|
@ -789,14 +786,12 @@
|
||||||
that.field = []; //检查项目
|
that.field = []; //检查项目
|
||||||
let submit = isSubmit=='1'?false:true;
|
let submit = isSubmit=='1'?false:true;
|
||||||
that.formData.forEach((item) => {
|
that.formData.forEach((item) => {
|
||||||
if(!item.is_hidden){
|
|
||||||
that.field.push({
|
that.field.push({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
field_value: that.checkForm[item.field_key],
|
field_value: that.checkForm[item.field_key],
|
||||||
is_testok: item.is_testok,//单项检查结果
|
is_testok: item.is_testok,//单项检查结果
|
||||||
is_hidden: item.is_hidden
|
is_hidden: item.is_hidden
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
// that.testrecord.form = that.formID;//检查表
|
// that.testrecord.form = that.formID;//检查表
|
||||||
that.testrecord.record_data = that.field;//检查项列表
|
that.testrecord.record_data = that.field;//检查项列表
|
||||||
|
@ -804,7 +799,6 @@
|
||||||
// that.testrecord.wproduct = that.wproductId;//半成品ID
|
// that.testrecord.wproduct = that.wproductId;//半成品ID
|
||||||
// that.testrecord.is_submited = true;
|
// that.testrecord.is_submited = true;
|
||||||
that.testrecord.id = that.recordId;
|
that.testrecord.id = that.recordId;
|
||||||
debugger;
|
|
||||||
if(submit){//提交
|
if(submit){//提交
|
||||||
this.$emit('recordSubmit',that.testrecord);
|
this.$emit('recordSubmit',that.testrecord);
|
||||||
}else {//保存
|
}else {//保存
|
||||||
|
|
|
@ -392,9 +392,14 @@
|
||||||
let that = this;
|
let that = this;
|
||||||
getPlanGantt({}).then(res=>{
|
getPlanGantt({}).then(res=>{
|
||||||
if(res.code===200){
|
if(res.code===200){
|
||||||
// debugger;
|
let arr =[],list=[];
|
||||||
let arr =[];
|
let li = res.data.results;
|
||||||
let list = res.data.results;
|
li.forEach((item,index)=>{
|
||||||
|
if(index===1){
|
||||||
|
list.push(item)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// let list = res.data.results[1];
|
||||||
list.forEach(item => {
|
list.forEach(item => {
|
||||||
if (!item.children || item.children.length < 1) {
|
if (!item.children || item.children.length < 1) {
|
||||||
let startTime = new Date(item.start_date).getTime();
|
let startTime = new Date(item.start_date).getTime();
|
||||||
|
|
|
@ -769,7 +769,7 @@
|
||||||
//点击检验:如果有一个直接进入,如果有多个表再进行选择
|
//点击检验:如果有一个直接进入,如果有多个表再进行选择
|
||||||
handleInspection(scope,index) {
|
handleInspection(scope,index) {
|
||||||
//调该物料对应的检查表
|
//调该物料对应的检查表
|
||||||
debugger;
|
// debugger;
|
||||||
let that = this;
|
let that = this;
|
||||||
this.innerIndex = index;
|
this.innerIndex = index;
|
||||||
// this.outerVisible = true;
|
// this.outerVisible = true;
|
||||||
|
@ -819,7 +819,7 @@
|
||||||
},
|
},
|
||||||
//点击记录里的检验
|
//点击记录里的检验
|
||||||
handleInspectionRecord(scope){
|
handleInspectionRecord(scope){
|
||||||
debugger;
|
// debugger;
|
||||||
let that =this;
|
let that =this;
|
||||||
that.recordVisible = false;
|
that.recordVisible = false;
|
||||||
that.recordId = scope.row.id;
|
that.recordId = scope.row.id;
|
||||||
|
@ -838,7 +838,7 @@
|
||||||
that.hasPicture = true;
|
that.hasPicture = true;
|
||||||
}
|
}
|
||||||
getTestRecordItem(scope.row.id).then((res) => {
|
getTestRecordItem(scope.row.id).then((res) => {
|
||||||
debugger;
|
// debugger;
|
||||||
let arr = [];
|
let arr = [];
|
||||||
let fieldList = res.data.record_data;
|
let fieldList = res.data.record_data;
|
||||||
for(let i=0;i<that.fieldList.length;i++){
|
for(let i=0;i<that.fieldList.length;i++){
|
||||||
|
@ -899,11 +899,11 @@
|
||||||
},
|
},
|
||||||
//半产品复检
|
//半产品复检
|
||||||
handleReview() {
|
handleReview() {
|
||||||
debugger;
|
// debugger;
|
||||||
let that = this;
|
let that = this;
|
||||||
testInit({ wproduct: this.wproduct,form: that.recordform}).then((response) => {
|
testInit({ wproduct: this.wproduct,form: that.recordform}).then((response) => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
debugger;
|
// debugger;
|
||||||
that.hasPicture = false;
|
that.hasPicture = false;
|
||||||
that.recordId = response.data.id;
|
that.recordId = response.data.id;
|
||||||
that.formName = response.data.form_.name;
|
that.formName = response.data.form_.name;
|
||||||
|
@ -926,7 +926,7 @@
|
||||||
|
|
||||||
//根据选择的表,渲染检查项目
|
//根据选择的表,渲染检查项目
|
||||||
submitrecordform(index) {
|
submitrecordform(index) {
|
||||||
debugger;
|
// debugger;
|
||||||
let that = this;
|
let that = this;
|
||||||
this.outerVisible = false;
|
this.outerVisible = false;
|
||||||
if (that.recordform != "") {
|
if (that.recordform != "") {
|
||||||
|
@ -952,7 +952,7 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}else if(index==='2'){//复检
|
}else if(index==='2'){//复检
|
||||||
debugger;
|
// debugger;
|
||||||
that.handleReview();
|
that.handleReview();
|
||||||
}
|
}
|
||||||
} else this.$message.error("请选择检查表!");
|
} else this.$message.error("请选择检查表!");
|
||||||
|
@ -1011,14 +1011,13 @@
|
||||||
},
|
},
|
||||||
//保存检查项目
|
//保存检查项目
|
||||||
recordSave(value) {
|
recordSave(value) {
|
||||||
debugger;
|
|
||||||
let that = this;
|
let that = this;
|
||||||
let id = value.id;
|
let id = value.id;
|
||||||
let params = {};
|
let params = {};
|
||||||
params.record_data = value.record_data;
|
params.record_data = value.record_data;
|
||||||
params.is_testok = value.is_testok;
|
params.is_testok = value.is_testok;
|
||||||
putTestRecordItem(id,params).then((res) => {
|
putTestRecordItem(id,params).then((res) => {
|
||||||
debugger;
|
// debugger;
|
||||||
if (res.code >= 200) {
|
if (res.code >= 200) {
|
||||||
that.recordVisible = false;
|
that.recordVisible = false;
|
||||||
that.limitedReview = false;
|
that.limitedReview = false;
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
# Generated by Django 3.2.9 on 2021-12-21 01:23
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('wf', '0018_workflow_key'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='ticket',
|
||||||
|
name='script_run_last_result',
|
||||||
|
field=models.BooleanField(default=True, verbose_name='脚本最后一次执行结果'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='ticketflow',
|
||||||
|
name='participant_str',
|
||||||
|
field=models.CharField(blank=True, help_text='非人工处理的处理人相关信息', max_length=200, null=True, verbose_name='处理人'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='state',
|
||||||
|
name='state_fields',
|
||||||
|
field=models.JSONField(default=dict, help_text='json格式字典存储,包括读写属性1:只读,2:必填,3:可选, 4:隐藏 示例:{"create_time":1,"title":2, "sn":1}, 内置特殊字段participant_info.participant_name:当前处理人信息(部门名称、角色名称),state.state_name:当前状态的状态名,workflow.workflow_name:工作流名称', verbose_name='表单字段'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='ticket',
|
||||||
|
name='title',
|
||||||
|
field=models.CharField(blank=True, help_text='工单标题', max_length=500, null=True, 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='workflow',
|
||||||
|
name='description',
|
||||||
|
field=models.CharField(blank=True, max_length=200, null=True, verbose_name='描述'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -206,7 +206,7 @@ class Ticket(CommonBModel):
|
||||||
ticket_data = models.JSONField('工单数据', default=dict, help_text='工单自定义字段内容')
|
ticket_data = models.JSONField('工单数据', default=dict, help_text='工单自定义字段内容')
|
||||||
in_add_node = models.BooleanField('加签状态中', default=False, help_text='是否处于加签状态下')
|
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='加签操作的人,工单当前处理人处理完成后会回到该处理人,当处于加签状态下才有效')
|
add_node_man = models.ForeignKey(User, verbose_name='加签人', on_delete=models.SET_NULL, null=True, blank=True, help_text='加签操作的人,工单当前处理人处理完成后会回到该处理人,当处于加签状态下才有效')
|
||||||
|
script_run_last_result = models.BooleanField('脚本最后一次执行结果', default=True)
|
||||||
participant_type = models.IntegerField('当前处理人类型', default=0, help_text='0.无处理人,1.个人,2.多人', choices=State.state_participanttype_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列表')
|
participant = models.JSONField('当前处理人', default=list, blank=True, help_text='可以为空(无处理人的情况,如结束状态)、userid、userid列表')
|
||||||
act_state = models.IntegerField('进行状态', default=1, help_text='当前工单的进行状态', choices=act_state_choices)
|
act_state = models.IntegerField('进行状态', default=1, help_text='当前工单的进行状态', choices=act_state_choices)
|
||||||
|
@ -228,8 +228,9 @@ class TicketFlow(BaseModel):
|
||||||
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, verbose_name='关联工单', related_name='ticketflow_ticket')
|
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关联, 为空时表示认为干预的操作', on_delete=models.CASCADE, null=True, blank=True)
|
transition = models.ForeignKey(Transition, verbose_name='流转id', help_text='与worklow.Transition关联, 为空时表示认为干预的操作', on_delete=models.CASCADE, null=True, blank=True)
|
||||||
suggestion = models.CharField('处理意见', max_length=10000, default='', blank=True)
|
suggestion = models.CharField('处理意见', max_length=10000, default='', blank=True)
|
||||||
participant_type = models.IntegerField('处理人类型', default=0, help_text='0.无处理人,1.个人,2.多人', choices=State.state_participanttype_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')
|
participant = models.ForeignKey(User, verbose_name='处理人', on_delete=models.SET_NULL, null=True, blank=True, related_name='ticketflow_participant')
|
||||||
|
participant_str = models.CharField('处理人', max_length=200, null=True, blank=True, help_text='非人工处理的处理人相关信息')
|
||||||
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)
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
from apps.system.models import User
|
||||||
|
from apps.wf.models import State, Ticket, TicketFlow, Transition
|
||||||
|
from apps.wpm.models import WProduct
|
||||||
|
|
||||||
|
|
||||||
class GetParticipants:
|
class GetParticipants:
|
||||||
|
|
||||||
all_funcs = [
|
all_funcs = [
|
||||||
|
@ -9,7 +14,7 @@ class GetParticipants:
|
||||||
# return [(func, getattr(self, func).__doc__) for func in dir(self) if callable(getattr(self, func)) and func.startswith('get_')]
|
# return [(func, getattr(self, func).__doc__) for func in dir(self) if callable(getattr(self, func)) and func.startswith('get_')]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_create_by(cls, state:dict={}, ticket:dict={}, ticket_data:dict={}, request={}):
|
def get_create_by(cls, state:dict={}, ticket:dict={}, new_ticket_data:dict={}, handler:User={}):
|
||||||
"""工单创建人"""
|
"""工单创建人"""
|
||||||
participant = ticket.create_by.id
|
participant = ticket.create_by.id
|
||||||
return participant
|
return participant
|
||||||
|
@ -18,12 +23,51 @@ class HandleScripts:
|
||||||
all_funcs = [
|
all_funcs = [
|
||||||
{'func': 'handle_wproduct', 'name':'处理不合格品'}
|
{'func': 'handle_wproduct', 'name':'处理不合格品'}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def handle_wproduct(cls, ticket:dict={}):
|
def to_next(cls, ticket:Ticket, by_timer:bool=False, by_task:bool=False, by_hook:bool=False, script_str:str=''):
|
||||||
|
# 获取信息
|
||||||
|
transition_obj = Transition.objects.filter(source_state=ticket.state, is_deleted=False).first()
|
||||||
|
|
||||||
|
TicketFlow.objects.create(ticket=ticket, state=ticket.state,
|
||||||
|
participant_type=State.PARTICIPANT_TYPE_ROBOT,
|
||||||
|
participant_str='func:{}'.format(script_str),
|
||||||
|
transition=transition_obj)
|
||||||
|
from .services import WfService
|
||||||
|
|
||||||
|
# 自动执行流转
|
||||||
|
WfService.handle_ticket(ticket=ticket, transition=transition_obj, new_ticket_data=ticket.ticket_data, by_task=True)
|
||||||
|
|
||||||
|
return ticket
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def handle_wproduct(cls, ticket:Ticket):
|
||||||
"""处理不合格品"""
|
"""处理不合格品"""
|
||||||
|
# 任务处理
|
||||||
|
|
||||||
ticket_data = ticket.ticket_data
|
ticket_data = ticket.ticket_data
|
||||||
wp = ticket.wt_ticket
|
wt = ticket.wt_ticket
|
||||||
wp = ticket_data
|
wp = wt.wproduct
|
||||||
if 'shenli2' in ticket_data and ticket_data['shenli2']:
|
if 'shenli2' in ticket_data and ticket_data['shenli2']:
|
||||||
|
wt.decision = ticket_data['shenli2']
|
||||||
if ticket_data['shenli2'] in ['返工', '返修']:
|
if ticket_data['shenli2'] in ['返工', '返修']:
|
||||||
pass
|
pass
|
||||||
|
elif ticket_data['shenli2'] in ['让步接收']:
|
||||||
|
wp.act_state = WProduct.WPR_ACT_STATE_OK
|
||||||
|
elif 'shenli1' in ticket_data and ticket_data['shenli1']:
|
||||||
|
wp.decision = ticket_data['shenli1']
|
||||||
|
if ticket_data['shenli1'] in ['返工', '返修']:
|
||||||
|
pass
|
||||||
|
elif ticket_data['shenli1'] in ['让步接收']:
|
||||||
|
wp.act_state = WProduct.WPR_ACT_STATE_OK
|
||||||
|
wt.save()
|
||||||
|
wp.save()
|
||||||
|
|
||||||
|
# 调用自动流转
|
||||||
|
ticket = cls.to_next(ticket=ticket, by_task=True, script_str= 'handle_wproduct')
|
||||||
|
if ticket.act_state == Ticket.TICKET_ACT_STATE_FINISH:
|
||||||
|
# 如果工单完成
|
||||||
|
wp.ticket = None
|
||||||
|
wp.save()
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from apps.system.models import User
|
||||||
from apps.system.serializers import UserSimpleSerializer
|
from apps.system.serializers import UserSimpleSerializer
|
||||||
import rest_framework
|
import rest_framework
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
@ -55,7 +56,6 @@ class TicketCreateSerializer(serializers.ModelSerializer):
|
||||||
fields=['title','workflow', 'ticket_data', 'transition']
|
fields=['title','workflow', 'ticket_data', 'transition']
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
validated_data.pop('transition')
|
|
||||||
return super().create(validated_data)
|
return super().create(validated_data)
|
||||||
|
|
||||||
class TicketSerializer(serializers.ModelSerializer):
|
class TicketSerializer(serializers.ModelSerializer):
|
||||||
|
@ -124,9 +124,9 @@ class TicketFlowSimpleSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class TicketHandleSerializer(serializers.Serializer):
|
class TicketHandleSerializer(serializers.Serializer):
|
||||||
transition = serializers.IntegerField(label="流转id")
|
transition = serializers.PrimaryKeyRelatedField(queryset=Transition.objects.all(), label="流转id")
|
||||||
ticket_data = serializers.JSONField(label="表单数据json")
|
ticket_data = serializers.JSONField(label="表单数据json")
|
||||||
suggestion = serializers.CharField(label="处理意见", required = False)
|
suggestion = serializers.CharField(label="处理意见", required = False, allow_blank=True)
|
||||||
|
|
||||||
class TicketRetreatSerializer(serializers.Serializer):
|
class TicketRetreatSerializer(serializers.Serializer):
|
||||||
suggestion = serializers.CharField(label="撤回原因", required = False)
|
suggestion = serializers.CharField(label="撤回原因", required = False)
|
||||||
|
@ -136,10 +136,10 @@ class TicketCloseSerializer(serializers.Serializer):
|
||||||
|
|
||||||
class TicketAddNodeSerializer(serializers.Serializer):
|
class TicketAddNodeSerializer(serializers.Serializer):
|
||||||
suggestion = serializers.CharField(label="加签说明", required = False)
|
suggestion = serializers.CharField(label="加签说明", required = False)
|
||||||
toadd_user = serializers.IntegerField(label='发送给谁去加签')
|
toadd_user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), label='发送给谁去加签')
|
||||||
|
|
||||||
class TicketAddNodeEndSerializer(serializers.Serializer):
|
class TicketAddNodeEndSerializer(serializers.Serializer):
|
||||||
suggestion = serializers.CharField(label="加签意见", required = False)
|
suggestion = serializers.CharField(label="加签意见", required = False)
|
||||||
|
|
||||||
class TicketDestorySerializer(serializers.Serializer):
|
class TicketDestorySerializer(serializers.Serializer):
|
||||||
ids = serializers.ListField(child=serializers.IntegerField(), label='工单ID列表')
|
ids = serializers.ListField(child=serializers.PrimaryKeyRelatedField(queryset=Ticket.objects.all()), label='工单ID列表')
|
|
@ -2,12 +2,12 @@ from apps.wf.serializers import CustomFieldSerializer
|
||||||
from apps.wf.serializers import TicketSerializer, TicketSimpleSerializer
|
from apps.wf.serializers import TicketSerializer, TicketSimpleSerializer
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
from apps.system.models import User
|
from apps.system.models import User
|
||||||
from apps.wf.models import CustomField, State, Ticket, Transition, Workflow
|
from apps.wf.models import CustomField, State, Ticket, TicketFlow, Transition, Workflow
|
||||||
from rest_framework.exceptions import APIException
|
from rest_framework.exceptions import APIException, PermissionDenied
|
||||||
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
|
from .scripts import GetParticipants, HandleScripts
|
||||||
from utils.queryset import get_parent_queryset
|
from utils.queryset import get_parent_queryset
|
||||||
|
|
||||||
class WfService(object):
|
class WfService(object):
|
||||||
|
@ -113,14 +113,14 @@ class WfService(object):
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_next_state_by_transition_and_ticket_info(cls, ticket:Ticket, transition: Transition, ticket_data:dict={}, request:dict={})->object:
|
def get_next_state_by_transition_and_ticket_info(cls, ticket:Ticket, transition: Transition, new_ticket_data:dict={})->object:
|
||||||
"""
|
"""
|
||||||
获取下个节点状态
|
获取下个节点状态
|
||||||
"""
|
"""
|
||||||
source_state = ticket.state
|
source_state = ticket.state
|
||||||
destination_state = transition.destination_state
|
destination_state = transition.destination_state
|
||||||
ticket_all_value = cls.get_ticket_all_field_value(ticket)
|
ticket_all_value = cls.get_ticket_all_field_value(ticket)
|
||||||
ticket_all_value.update(**ticket_data)
|
ticket_all_value.update(**new_ticket_data)
|
||||||
for key, value in ticket_all_value.items():
|
for key, value in ticket_all_value.items():
|
||||||
if isinstance(ticket_all_value[key], str):
|
if isinstance(ticket_all_value[key], str):
|
||||||
ticket_all_value[key] = "'" + ticket_all_value[key] + "'"
|
ticket_all_value[key] = "'" + ticket_all_value[key] + "'"
|
||||||
|
@ -133,7 +133,7 @@ class WfService(object):
|
||||||
return destination_state
|
return destination_state
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_ticket_state_participant_info(cls, state:State, ticket:Ticket, ticket_data:dict={}, request={}):
|
def get_ticket_state_participant_info(cls, state:State, ticket:Ticket, new_ticket_data:dict={}, handler:User=None):
|
||||||
"""
|
"""
|
||||||
获取工单目标状态实际的处理人, 处理人类型
|
获取工单目标状态实际的处理人, 处理人类型
|
||||||
"""
|
"""
|
||||||
|
@ -154,11 +154,12 @@ class WfService(object):
|
||||||
multi_all_person_dict = {}
|
multi_all_person_dict = {}
|
||||||
destination_participant_type, destination_participant = state.participant_type, state.participant
|
destination_participant_type, destination_participant = state.participant_type, state.participant
|
||||||
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 = new_ticket_data.get(destination_participant, 0) if destination_participant in new_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_FORMCODE:#代码获取
|
elif destination_participant_type == State.PARTICIPANT_TYPE_FORMCODE:#代码获取
|
||||||
destination_participant = getattr(GetParticipants, destination_participant)(state=state, ticket=ticket, ticket_data=ticket_data, request=request)
|
destination_participant = getattr(GetParticipants, destination_participant)(
|
||||||
|
state=state, ticket=ticket, new_ticket_data=new_ticket_data, hander=handler)
|
||||||
|
|
||||||
elif destination_participant_type == State.PARTICIPANT_TYPE_DEPT:#部门
|
elif destination_participant_type == State.PARTICIPANT_TYPE_DEPT:#部门
|
||||||
destination_participant = list(User.objects.filter(dept__in=destination_participant).values_list('id', flat=True))
|
destination_participant = list(User.objects.filter(dept__in=destination_participant).values_list('id', flat=True))
|
||||||
|
@ -173,7 +174,7 @@ class WfService(object):
|
||||||
depts = get_parent_queryset(ticket.create_by.dept)
|
depts = get_parent_queryset(ticket.create_by.dept)
|
||||||
user_queryset = user_queryset.filter(dept__in=depts)
|
user_queryset = user_queryset.filter(dept__in=depts)
|
||||||
elif state.filter_policy == 3:
|
elif state.filter_policy == 3:
|
||||||
depts = get_parent_queryset(request.user.dept)
|
depts = get_parent_queryset(handler.dept)
|
||||||
user_queryset = user_queryset.filter(dept__in=depts)
|
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:
|
||||||
|
@ -200,7 +201,7 @@ class WfService(object):
|
||||||
transitions = cls.get_state_transitions(ticket.state)
|
transitions = cls.get_state_transitions(ticket.state)
|
||||||
if not transitions:
|
if not transitions:
|
||||||
return dict(permission=True, msg="工单当前状态无需操作")
|
return dict(permission=True, msg="工单当前状态无需操作")
|
||||||
current_participant_count = 1
|
current_participant_count = 0
|
||||||
participant_type = ticket.participant_type
|
participant_type = ticket.participant_type
|
||||||
participant = ticket.participant
|
participant = ticket.participant
|
||||||
state = ticket.state
|
state = ticket.state
|
||||||
|
@ -256,5 +257,95 @@ class WfService(object):
|
||||||
field_info_dict[i.field_key] = ticket.ticket_data.get(i.field_key, None)
|
field_info_dict[i.field_key] = ticket.ticket_data.get(i.field_key, None)
|
||||||
return field_info_dict
|
return field_info_dict
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def handle_ticket(cls, ticket:Ticket, transition: Transition, new_ticket_data:dict={}, handler:User=None,
|
||||||
|
suggestion:str='', created:bool=False, by_timer:bool=False, by_task:bool=False, by_hook:bool=False):
|
||||||
|
|
||||||
|
source_state = ticket.state
|
||||||
|
source_ticket_data = ticket.ticket_data
|
||||||
|
|
||||||
|
# 校验处理权限
|
||||||
|
if not handler or not created: # 没有处理人意味着系统触发不校验处理权限
|
||||||
|
result = WfService.ticket_handle_permission_check(ticket, handler)
|
||||||
|
if result.get('permission') is False:
|
||||||
|
raise PermissionDenied(result.get('msg'))
|
||||||
|
|
||||||
|
# 校验表单必填项目
|
||||||
|
if transition.field_require_check or not created:
|
||||||
|
for key, value in ticket.state.state_fields.items():
|
||||||
|
if value == State.STATE_FIELD_REQUIRED:
|
||||||
|
if key not in new_ticket_data or not new_ticket_data[key]:
|
||||||
|
raise APIException('字段{}必填'.format(key))
|
||||||
|
|
||||||
|
destination_state = cls.get_next_state_by_transition_and_ticket_info(ticket, transition, new_ticket_data)
|
||||||
|
multi_all_person = ticket.multi_all_person
|
||||||
|
if multi_all_person:
|
||||||
|
multi_all_person[handler.id] =dict(transition=transition.id)
|
||||||
|
# 判断所有人处理结果是否一致
|
||||||
|
if WfService.check_dict_has_all_same_value(multi_all_person):
|
||||||
|
participant_info = WfService.get_ticket_state_participant_info(destination_state, ticket, new_ticket_data)
|
||||||
|
destination_participant_type = participant_info.get('destination_participant_type', 0)
|
||||||
|
destination_participant = participant_info.get('destination_participant', 0)
|
||||||
|
multi_all_person = {}
|
||||||
|
else:
|
||||||
|
# 处理人没有没有全部处理完成或者处理动作不一致
|
||||||
|
destination_participant_type = ticket.participant_type
|
||||||
|
destination_state = ticket.state # 保持原状态
|
||||||
|
destination_participant = []
|
||||||
|
for key, value in multi_all_person.items():
|
||||||
|
if not value:
|
||||||
|
destination_participant.append(key)
|
||||||
|
else:
|
||||||
|
# 当前处理人类型非全部处理
|
||||||
|
participant_info = WfService.get_ticket_state_participant_info(destination_state, ticket, new_ticket_data)
|
||||||
|
destination_participant_type = participant_info.get('destination_participant_type', 0)
|
||||||
|
destination_participant = participant_info.get('destination_participant', 0)
|
||||||
|
multi_all_person = participant_info.get('multi_all_person', {})
|
||||||
|
|
||||||
|
# 更新工单信息:基础字段及自定义字段, add_relation字段 需要下个处理人是部门、角色等的情况
|
||||||
|
ticket.state = destination_state
|
||||||
|
ticket.participant_type = destination_participant_type
|
||||||
|
ticket.participant = destination_participant
|
||||||
|
ticket.multi_all_person = multi_all_person
|
||||||
|
if destination_state.type == State.STATE_TYPE_END:
|
||||||
|
ticket.act_state = Ticket.TICKET_ACT_STATE_FINISH
|
||||||
|
elif destination_state.type == State.STATE_TYPE_START:
|
||||||
|
ticket.act_state = Ticket.TICKET_ACT_STATE_DRAFT
|
||||||
|
else:
|
||||||
|
ticket.act_state = Ticket.TICKET_ACT_STATE_ONGOING
|
||||||
|
|
||||||
|
if transition.attribute_type == Transition.TRANSITION_ATTRIBUTE_TYPE_REFUSE:
|
||||||
|
ticket.act_state = Ticket.TICKET_ACT_STATE_BACK
|
||||||
|
|
||||||
|
# 只更新必填和可选的字段
|
||||||
|
if not created:
|
||||||
|
for key, value in source_state.state_fields.items():
|
||||||
|
if value in (State.STATE_FIELD_REQUIRED, State.STATE_FIELD_OPTIONAL):
|
||||||
|
source_ticket_data[key] = new_ticket_data[key]
|
||||||
|
ticket.ticket_data = source_ticket_data
|
||||||
|
ticket.save()
|
||||||
|
|
||||||
|
# 更新工单流转记录
|
||||||
|
if not by_task:
|
||||||
|
TicketFlow.objects.create(ticket=ticket, state=source_state, ticket_data=WfService.get_ticket_all_field_value(ticket),
|
||||||
|
suggestion=suggestion, participant_type=State.PARTICIPANT_TYPE_PERSONAL,
|
||||||
|
participant=handler, transition=transition)
|
||||||
|
|
||||||
|
if created:
|
||||||
|
if source_state.participant_cc:
|
||||||
|
TicketFlow.objects.create(ticket=ticket, state=source_state,
|
||||||
|
participant_type=0, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_CC,
|
||||||
|
participant=None, participant_cc=source_state.participant_cc)
|
||||||
|
|
||||||
|
# 目标状态需要抄送
|
||||||
|
if destination_state.participant_cc:
|
||||||
|
TicketFlow.objects.create(ticket=ticket, state=destination_state,
|
||||||
|
participant_type=0, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_CC,
|
||||||
|
participant=None, participant_cc=destination_state.participant_cc)
|
||||||
|
|
||||||
|
# 如果目标状态是脚本则执行
|
||||||
|
if destination_state.participant_type == State.PARTICIPANT_TYPE_ROBOT:
|
||||||
|
getattr(HandleScripts, destination_state.participant)(ticket)
|
||||||
|
|
||||||
|
return ticket
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.db import transaction
|
||||||
from django.db.models import query
|
from django.db.models import query
|
||||||
from rest_framework.utils import serializer_helpers
|
from rest_framework.utils import serializer_helpers
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
@ -17,7 +19,7 @@ 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
|
from .scripts import GetParticipants, HandleScripts
|
||||||
|
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
@ -135,6 +137,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
|
||||||
raise APIException('请指定查询分类')
|
raise APIException('请指定查询分类')
|
||||||
return super().get_queryset()
|
return super().get_queryset()
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
新建工单
|
新建工单
|
||||||
|
@ -144,7 +147,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
vdata = serializer.validated_data #校验之后的数据
|
vdata = serializer.validated_data #校验之后的数据
|
||||||
start_state = WfService.get_workflow_start_state(vdata['workflow'])
|
start_state = WfService.get_workflow_start_state(vdata['workflow'])
|
||||||
transition = vdata['transition']
|
transition = vdata.pop('transition')
|
||||||
ticket_data = vdata['ticket_data']
|
ticket_data = vdata['ticket_data']
|
||||||
|
|
||||||
save_ticket_data = {}
|
save_ticket_data = {}
|
||||||
|
@ -152,7 +155,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
|
||||||
if transition.field_require_check:
|
if transition.field_require_check:
|
||||||
for key, value in start_state.state_fields.items():
|
for key, value in start_state.state_fields.items():
|
||||||
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 and not ticket_data[key]:
|
||||||
raise APIException('字段{}必填'.format(key))
|
raise APIException('字段{}必填'.format(key))
|
||||||
save_ticket_data[key] = ticket_data[key]
|
save_ticket_data[key] = ticket_data[key]
|
||||||
elif value == State.STATE_FIELD_OPTIONAL:
|
elif value == State.STATE_FIELD_OPTIONAL:
|
||||||
|
@ -160,50 +163,22 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
|
||||||
|
|
||||||
ticket = serializer.save(state=start_state,
|
ticket = serializer.save(state=start_state,
|
||||||
create_by=request.user,
|
create_by=request.user,
|
||||||
|
create_time=timezone.now(),
|
||||||
act_state=Ticket.TICKET_ACT_STATE_DRAFT,
|
act_state=Ticket.TICKET_ACT_STATE_DRAFT,
|
||||||
belong_dept=request.user.dept,
|
belong_dept=request.user.dept,
|
||||||
ticket_data=save_ticket_data) # 先创建出来
|
ticket_data=save_ticket_data) # 先创建出来
|
||||||
|
# 更新title和sn
|
||||||
next_state = WfService.get_next_state_by_transition_and_ticket_info(ticket=ticket, transition=transition)
|
title = vdata.get('title', '')
|
||||||
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', 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:
|
|
||||||
act_state = Ticket.TICKET_ACT_STATE_FINISH
|
|
||||||
elif next_state.type == State.STATE_TYPE_START:
|
|
||||||
act_state = Ticket.TICKET_ACT_STATE_DRAFT
|
|
||||||
else:
|
|
||||||
act_state = Ticket.TICKET_ACT_STATE_ONGOING
|
|
||||||
title = vdata['title']
|
|
||||||
title_template = ticket.workflow.title_template
|
title_template = ticket.workflow.title_template
|
||||||
if title_template:
|
if title_template:
|
||||||
all_ticket_data = {**rdata, **rdata['ticket_data']}
|
all_ticket_data = {**rdata, **ticket_data}
|
||||||
title = title_template.format(**all_ticket_data)
|
title = title_template.format(**all_ticket_data)
|
||||||
# 更新一下信息
|
sn = WfService.get_ticket_sn(ticket.workflow) # 流水号
|
||||||
ticket.sn = sn
|
ticket.sn = sn
|
||||||
ticket.title = title
|
ticket.title = title
|
||||||
ticket.state=next_state
|
|
||||||
ticket.participant=destination_participant
|
|
||||||
ticket.participant_type=destination_participant_type
|
|
||||||
ticket.act_state=act_state
|
|
||||||
ticket.multi_all_person=multi_all_person
|
|
||||||
ticket.save()
|
ticket.save()
|
||||||
# 新增流转记录
|
ticket = WfService.handle_ticket(ticket=ticket, transition=transition, new_ticket_data=ticket_data,
|
||||||
TicketFlow.objects.create(ticket=ticket, state=start_state, ticket_data=WfService.get_ticket_all_field_value(ticket),
|
handler=request.user, created=True)
|
||||||
suggestion=rdata.get('suggestion',''), participant_type=State.PARTICIPANT_TYPE_PERSONAL,
|
|
||||||
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=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':'*'})
|
||||||
|
@ -219,85 +194,20 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
|
||||||
return Response(ret)
|
return Response(ret)
|
||||||
|
|
||||||
@action(methods=['post'], detail=True, perms_map={'post':'*'})
|
@action(methods=['post'], detail=True, perms_map={'post':'*'})
|
||||||
|
@transaction.atomic
|
||||||
def handle(self, request, pk=None):
|
def handle(self, request, pk=None):
|
||||||
"""
|
"""
|
||||||
处理工单
|
处理工单
|
||||||
"""
|
"""
|
||||||
try:
|
ticket = self.get_object()
|
||||||
ticket = Ticket.objects.get(pk=pk)
|
serializer = TicketHandleSerializer(data=request.data)
|
||||||
except:
|
serializer.is_valid(raise_exception=True)
|
||||||
raise APIException('工单不存在')
|
vdata = serializer.validated_data
|
||||||
data = request.data
|
new_ticket_data = ticket.ticket_data
|
||||||
result = WfService.ticket_handle_permission_check(ticket, request.user)
|
new_ticket_data.update(**vdata['ticket_data'])
|
||||||
source_state = ticket.state
|
|
||||||
source_ticket_data = ticket.ticket_data
|
|
||||||
if result.get('permission') is False:
|
|
||||||
raise PermissionDenied(result.get('msg'))
|
|
||||||
# 校验表单必填项目
|
|
||||||
transition = Transition.objects.get(pk=data['transition'])
|
|
||||||
ticket_data = data['ticket_data']
|
|
||||||
if transition.field_require_check:
|
|
||||||
for key, value in ticket.state.state_fields.items():
|
|
||||||
if value == State.STATE_FIELD_REQUIRED:
|
|
||||||
if key not in ticket_data or not ticket_data[key]:
|
|
||||||
raise APIException('字段{}必填'.format(key))
|
|
||||||
|
|
||||||
destination_state = WfService.get_next_state_by_transition_and_ticket_info(ticket, transition, ticket_data, request)
|
ticket = WfService.handle_ticket(ticket=ticket, transition=vdata['transition'],
|
||||||
multi_all_person = ticket.multi_all_person
|
new_ticket_data=new_ticket_data, handler=request.user, suggestion=vdata['suggestion'])
|
||||||
if multi_all_person:
|
|
||||||
multi_all_person[request.user.id] =dict(transition=transition.id)
|
|
||||||
# 判断所有人处理结果是否一致
|
|
||||||
if WfService.check_dict_has_all_same_value(multi_all_person):
|
|
||||||
participant_info = WfService.get_ticket_state_participant_info(destination_state, ticket, data['ticket_data'])
|
|
||||||
destination_participant_type = participant_info.get('destination_participant_type', 0)
|
|
||||||
destination_participant = participant_info.get('destination_participant', 0)
|
|
||||||
multi_all_person = {}
|
|
||||||
else:
|
|
||||||
# 处理人没有没有全部处理完成或者处理动作不一致
|
|
||||||
destination_participant_type = ticket.participant_type
|
|
||||||
destination_state = ticket.state # 保持原状态
|
|
||||||
destination_participant = []
|
|
||||||
for key, value in multi_all_person.items():
|
|
||||||
if not value:
|
|
||||||
destination_participant.append(key)
|
|
||||||
else:
|
|
||||||
# 当前处理人类型非全部处理
|
|
||||||
participant_info = WfService.get_ticket_state_participant_info(destination_state, ticket, data['ticket_data'])
|
|
||||||
destination_participant_type = participant_info.get('destination_participant_type', 0)
|
|
||||||
destination_participant = participant_info.get('destination_participant', 0)
|
|
||||||
multi_all_person = participant_info.get('multi_all_person', {})
|
|
||||||
|
|
||||||
# 更新工单信息:基础字段及自定义字段, add_relation字段 需要下个处理人是部门、角色等的情况
|
|
||||||
ticket.state = destination_state
|
|
||||||
ticket.participant_type = destination_participant_type
|
|
||||||
ticket.participant = destination_participant
|
|
||||||
ticket.multi_all_person = multi_all_person
|
|
||||||
if destination_state.type == State.STATE_TYPE_END:
|
|
||||||
ticket.act_state = Ticket.TICKET_ACT_STATE_FINISH
|
|
||||||
elif destination_state.type == State.STATE_TYPE_START:
|
|
||||||
ticket.act_state = Ticket.TICKET_ACT_STATE_DRAFT
|
|
||||||
else:
|
|
||||||
ticket.act_state = Ticket.TICKET_ACT_STATE_ONGOING
|
|
||||||
|
|
||||||
if transition.attribute_type == Transition.TRANSITION_ATTRIBUTE_TYPE_REFUSE:
|
|
||||||
ticket.act_state = Ticket.TICKET_ACT_STATE_BACK
|
|
||||||
|
|
||||||
# 只更新必填和可选的字段
|
|
||||||
for key, value in source_state.state_fields.items():
|
|
||||||
if value in (State.STATE_FIELD_REQUIRED, State.STATE_FIELD_OPTIONAL):
|
|
||||||
source_ticket_data[key] = ticket_data[key]
|
|
||||||
ticket.ticket_data = source_ticket_data
|
|
||||||
ticket.save()
|
|
||||||
|
|
||||||
# 更新工单流转记录
|
|
||||||
TicketFlow.objects.create(ticket=ticket, state=source_state, ticket_data=WfService.get_ticket_all_field_value(ticket),
|
|
||||||
suggestion=data.get('suggestion',''), participant_type=State.PARTICIPANT_TYPE_PERSONAL,
|
|
||||||
participant=request.user, transition=transition)
|
|
||||||
# 目标状态需要抄送
|
|
||||||
if destination_state.participant_cc:
|
|
||||||
TicketFlow.objects.create(ticket=ticket, state=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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,3 +21,15 @@ class WMaterialFilterSet(filters.FilterSet):
|
||||||
subplans = WpmServies.get_subplans_queyset_from_step(step)
|
subplans = WpmServies.get_subplans_queyset_from_step(step)
|
||||||
queryset = queryset.filter(subproduction_plan__in=subplans).exclude(material__type=Material.MA_TYPE_HALFGOOD)
|
queryset = queryset.filter(subproduction_plan__in=subplans).exclude(material__type=Material.MA_TYPE_HALFGOOD)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
class WProductFilterSet(filters.FilterSet):
|
||||||
|
tag = filters.CharFilter(method='filter_tag')
|
||||||
|
class Meta:
|
||||||
|
model = WProduct
|
||||||
|
fields = ['step', 'subproduction_plan', 'material', 'step__process', 'act_state', 'material__type']
|
||||||
|
|
||||||
|
def filter_tag(self, queryset, name, value):
|
||||||
|
if value == 'no_scrap':
|
||||||
|
queryset = queryset.exclude(act_state=WProduct.WPR_ACT_STATE_SCRAP)
|
||||||
|
return queryset
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 3.2.9 on 2021-12-21 01:23
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('wpm', '0035_alter_operationrecorditem_field_value'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='wprouctticket',
|
||||||
|
name='decision',
|
||||||
|
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='最终决定'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='wproduct',
|
||||||
|
name='act_state',
|
||||||
|
field=models.IntegerField(choices=[(6, '待复检'), (8, '操作准备中'), (10, '操作进行中'), (20, '待检验'), (26, '待夹层检验'), (30, '已合格'), (40, '已入库'), (50, '不合格'), (60, '待成品检验'), (70, '已报废')], default=0, verbose_name='进行状态'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -48,6 +48,16 @@ class WProduct(CommonAModel):
|
||||||
(WPR_ACT_STATE_TOFINALTEST, '待成品检验'),
|
(WPR_ACT_STATE_TOFINALTEST, '待成品检验'),
|
||||||
(WPR_ACT_STATE_SCRAP, '已报废')
|
(WPR_ACT_STATE_SCRAP, '已报废')
|
||||||
)
|
)
|
||||||
|
SCRAP_REASON_QIPAO = 10
|
||||||
|
SCRAP_REASON_PODIAN = 20
|
||||||
|
SCRAP_REASON_HUA = 30
|
||||||
|
SCRAP_REASON_OTHER = 40
|
||||||
|
scrap_reason_choices = (
|
||||||
|
(10, '气泡'),
|
||||||
|
(20, '破点'),
|
||||||
|
(30, '划伤'),
|
||||||
|
(40, '其他')
|
||||||
|
)
|
||||||
number = models.CharField('物品编号', unique=True, null=True, blank=True, max_length=50)
|
number = models.CharField('物品编号', unique=True, null=True, blank=True, max_length=50)
|
||||||
material = models.ForeignKey(Material, verbose_name='所属物料状态', on_delete=models.CASCADE)
|
material = models.ForeignKey(Material, verbose_name='所属物料状态', on_delete=models.CASCADE)
|
||||||
pre_step = models.ForeignKey(Step, verbose_name='已执行到', help_text='已执行完的步骤', null=True, blank=True, on_delete=models.CASCADE, related_name='w_pre_step')
|
pre_step = models.ForeignKey(Step, verbose_name='已执行到', help_text='已执行完的步骤', null=True, blank=True, on_delete=models.CASCADE, related_name='w_pre_step')
|
||||||
|
@ -58,6 +68,7 @@ class WProduct(CommonAModel):
|
||||||
remark = models.CharField('备注', max_length=200, null=True, blank=True)
|
remark = models.CharField('备注', max_length=200, null=True, blank=True)
|
||||||
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='当前子生产计划', on_delete=models.CASCADE, related_name='wproduct_subplan')
|
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='当前子生产计划', on_delete=models.CASCADE, related_name='wproduct_subplan')
|
||||||
|
|
||||||
|
scrap_reason = models.IntegerField('报废原因', choices=scrap_reason_choices, null=True, blank=True)
|
||||||
warehouse = models.ForeignKey(WareHouse, verbose_name='所在仓库', on_delete=models.SET_NULL, null=True, blank=True)
|
warehouse = models.ForeignKey(WareHouse, verbose_name='所在仓库', on_delete=models.SET_NULL, null=True, blank=True)
|
||||||
operation = models.ForeignKey('wpm.operation', verbose_name='当前操作',
|
operation = models.ForeignKey('wpm.operation', verbose_name='当前操作',
|
||||||
on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_operation')
|
on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_operation')
|
||||||
|
|
|
@ -415,3 +415,7 @@ class OperationRecordDetailSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
def get_record_data(self, obj):
|
def get_record_data(self, obj):
|
||||||
return OperationRecordItemSerializer(instance=obj.item_operation_record.order_by('form_field__sort'), many=True).data
|
return OperationRecordItemSerializer(instance=obj.item_operation_record.order_by('form_field__sort'), many=True).data
|
||||||
|
|
||||||
|
class ScrapSerializer(serializers.Serializer):
|
||||||
|
scrap_reason = serializers.ChoiceField(choices=WProduct.scrap_reason_choices, required=False)
|
||||||
|
# wproducts = serializers.ListField(child=serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all()), label='半成品ID列表')
|
|
@ -18,10 +18,10 @@ from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from apps.wf.models import Workflow
|
from apps.wf.models import Workflow
|
||||||
from apps.wf.serializers import WorkflowSimpleSerializer
|
from apps.wf.serializers import WorkflowSimpleSerializer
|
||||||
from apps.wpm.filters import WMaterialFilterSet
|
from apps.wpm.filters import WMaterialFilterSet, WProductFilterSet
|
||||||
from apps.wpm.models import OperationEquip, OperationWproduct, Pick, PickWproduct, WMaterial, WProduct, Operation, OperationMaterial, OperationRecord, OperationRecordItem
|
from apps.wpm.models import OperationEquip, OperationWproduct, Pick, PickWproduct, WMaterial, WProduct, Operation, OperationMaterial, OperationRecord, OperationRecordItem
|
||||||
|
|
||||||
from apps.wpm.serializers import OperationEquipListSerializer, OperationEquipUpdateSerializer, OperationMaterialCreate1ListSerailizer, OperationMaterialCreate1Serailizer, OperationMaterialCreate2ListSerailizer, OperationMaterialCreate2Serailizer, OperationMaterialCreate3Serializer, OperationMaterialListSerializer, OperationRecordDetailSerializer, OperationRecordListSerializer, OperationRecordSubmitSerializer, OperationUpdateSerializer, OperationWproductListSerializer, OperationCreateSerializer, OperationDetailSerializer, OperationListSerializer, PickHalfSerializer, PickHalfsSerializer, PickSerializer, OperationInitSerializer, OperationSubmitSerializer, WMaterialListSerializer, WProductListSerializer, WplanPutInSerializer, WpmTestFormInitSerializer, WpmTestRecordCreateSerializer, WproductPutInSerializer, WproductPutInsSerializer
|
from apps.wpm.serializers import OperationEquipListSerializer, OperationEquipUpdateSerializer, OperationMaterialCreate1ListSerailizer, OperationMaterialCreate1Serailizer, OperationMaterialCreate2ListSerailizer, OperationMaterialCreate2Serailizer, OperationMaterialCreate3Serializer, OperationMaterialListSerializer, OperationRecordDetailSerializer, OperationRecordListSerializer, OperationRecordSubmitSerializer, OperationUpdateSerializer, OperationWproductListSerializer, OperationCreateSerializer, OperationDetailSerializer, OperationListSerializer, PickHalfSerializer, PickHalfsSerializer, PickSerializer, OperationInitSerializer, OperationSubmitSerializer, ScrapSerializer, WMaterialListSerializer, WProductListSerializer, WplanPutInSerializer, WpmTestFormInitSerializer, WpmTestRecordCreateSerializer, WproductPutInSerializer, WproductPutInsSerializer
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from rest_framework import exceptions, serializers
|
from rest_framework import exceptions, serializers
|
||||||
|
@ -181,7 +181,7 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
|
||||||
perms_map={'*':'*'}
|
perms_map={'*':'*'}
|
||||||
queryset = WProduct.objects.select_related('step', 'material', 'subproduction_plan').filter(is_hidden=False)
|
queryset = WProduct.objects.select_related('step', 'material', 'subproduction_plan').filter(is_hidden=False)
|
||||||
serializer_class = WProductListSerializer
|
serializer_class = WProductListSerializer
|
||||||
filterset_fields = ['step', 'subproduction_plan', 'material', 'step__process', 'act_state', 'material__type']
|
filterset_class = WProductFilterSet
|
||||||
search_fields = ['number']
|
search_fields = ['number']
|
||||||
ordering_fields = ['id']
|
ordering_fields = ['id']
|
||||||
ordering = ['id']
|
ordering = ['id']
|
||||||
|
@ -332,26 +332,63 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
|
||||||
wproduct.save()
|
wproduct.save()
|
||||||
return Response()
|
return Response()
|
||||||
|
|
||||||
@action(methods=['post'], detail=True, perms_map={'post':'*'})
|
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=ScrapSerializer)
|
||||||
def scrap(self, request, pk=None):
|
def scrap(self, request, pk=None):
|
||||||
"""
|
"""
|
||||||
报废操作
|
报废操作
|
||||||
"""
|
"""
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
if Operation.objects.filter(ow_operation__wproduct=obj, is_submited=True).count() >4 or obj.act_state != WProduct.WPR_ACT_STATE_NOTOK:
|
serializer = ScrapSerializer(data=request.data)
|
||||||
raise exceptions.APIException('该产品不支持直接报废')
|
serializer.is_valid(raise_exception=True)
|
||||||
|
vdata = serializer.validated_data
|
||||||
|
if obj.act_state == WProduct.WPR_ACT_STATE_NOTOK:
|
||||||
|
pass
|
||||||
|
elif obj.step.process.id == 1: # 如果是冷加工可直接报废
|
||||||
|
if vdata.get('scrap_reason', None):
|
||||||
|
obj.scrap_reason = vdata['scrap_reason']
|
||||||
|
else:
|
||||||
|
raise exceptions.APIException('请填写报废原因')
|
||||||
|
else:
|
||||||
|
raise exceptions.APIException('该产品不可报废')
|
||||||
obj.act_state = WProduct.WPR_ACT_STATE_SCRAP
|
obj.act_state = WProduct.WPR_ACT_STATE_SCRAP
|
||||||
obj.update_by = request.user
|
obj.update_by = request.user
|
||||||
obj.save()
|
obj.save()
|
||||||
return Response()
|
return Response()
|
||||||
|
|
||||||
@action(methods=['get'], detail=False, perms_map={'get':'*'})
|
# @action(methods=['get'], detail=False, perms_map={'get':'*'})
|
||||||
def workflows(self, request, pk=None):
|
# def workflows(self, request, pk=None):
|
||||||
|
# """
|
||||||
|
# 可发起的工作流
|
||||||
|
# """
|
||||||
|
# wfs = Workflow.objects.filter(key__startswith= 'wp_')
|
||||||
|
# return WorkflowSimpleSerializer(instance=wfs, many=True).data
|
||||||
|
|
||||||
|
@action(methods=['get'], detail=True, perms_map={'get':'*'})
|
||||||
|
def wf_bhg(self, request, pk=None):
|
||||||
"""
|
"""
|
||||||
可发起的工作流
|
发起不合格审理单
|
||||||
"""
|
"""
|
||||||
wfs = Workflow.objects.filter(key__startswith= 'wp')
|
obj = self.get_object()
|
||||||
return WorkflowSimpleSerializer(instance=wfs, many=True).data
|
if obj.act_state != WProduct.WPR_ACT_STATE_NOTOK:
|
||||||
|
raise exceptions.APIException('非检验不合格产品不可发起不合格审理')
|
||||||
|
workflow = Workflow.objects.filter(name='不合格品审理单', is_deletd=False).first()
|
||||||
|
if workflow:
|
||||||
|
exist_data = {
|
||||||
|
'sys_wproduct':obj.id,
|
||||||
|
'sys_name':obj.material.name,
|
||||||
|
'sys_specification':obj.material.specification,
|
||||||
|
'sys_finder':request.user.id,
|
||||||
|
'sys_process':obj.step.process.id,
|
||||||
|
}
|
||||||
|
ret = {'workflow':workflow.id}
|
||||||
|
ret['exist_data'] = exist_data
|
||||||
|
return Response(ret)
|
||||||
|
else:
|
||||||
|
raise exceptions.APIException('未找到对应工作流程')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue