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

This commit is contained in:
shilixia 2021-12-29 14:02:05 +08:00
commit 6e4914b6c8
56 changed files with 2171 additions and 1041 deletions

View File

@ -60,4 +60,7 @@ export default {
#customerForm .el-form-item{
margin-bottom: 30px!important;
}
.overFlowShow .el-tabs__content{
overflow: visible;
}
</style>

View File

@ -240,3 +240,10 @@ export function getCodes() {
method: 'get'
})
}
//工单详情
export function getWorkflowInit(id) {
return request({
url: `/wf/workflow/${id}/init/`,
method: 'get'
})
}

View File

@ -19,11 +19,19 @@
@expand-change="handlerExpand"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column label="名称">
<el-table-column label="任务编号">
<template slot-scope="scope">{{ scope.row.name }}</template>
</el-table-column>
<el-table-column label="进度">
<template slot-scope="scope">{{ scope.row.name }}</template>
<el-table-column label="产品名称">
<template slot-scope="scope">
<div style="overflow: hidden; text-overflow:ellipsis; white-space: nowrap;">{{ scope.row.productName }}</div>
</template>
</el-table-column>
<el-table-column label="产品型号">
<template slot-scope="scope">{{ scope.row.productNum }}</template>
</el-table-column>
<el-table-column label="生产数量">
<template slot-scope="scope">{{ scope.row.per }}</template>
</el-table-column>
</el-table>
</div>
@ -44,19 +52,12 @@ export default {
BGScrollTop: {
handler: function(newValue) {
let table = this.$refs.tableMenu.bodyWrapper;
// console.log(newValue, table);
table.scrollTo(0, newValue);
// table.scrollTo({
// top: newValue,
// left: 0,
// behavior: "smooth"
// });
}
}
},
data() {
return {
// tableData: [],
checkList: [],
isShowHeaderBox: false,
menuOpen: false,
@ -68,7 +69,6 @@ export default {
handlerWatchScroll() {
let table = this.$refs.tableMenu.bodyWrapper;
table.addEventListener("scroll", e => {
// console.log(e.srcElement.scrollTop);
this.$emit("TableScrollTop", e.srcElement.scrollTop);
});
},

View File

@ -1,6 +1,6 @@
<template>
<div>
<el-row style="position: fixed;z-index: 2000;top: 465px;right: 10px;">
<el-row style="position: absolute;z-index: 2000;top: -38px;right: 150px;display: flex">
<el-date-picker
v-model="timeRange"
type="monthrange"
@ -11,8 +11,30 @@
value-format="yyyy-MM"
@change="timeRangeChange"
>
<!--test-->
</el-date-picker>
<div
class="toolTip"
style="width: 60px;height: 35px;padding-left: 10px; background: white;margin-left: 10px;border: 1px solid #DCDFE6;line-height: 35px;border-radius: 4px"
>
<el-dropdown trigger="click">
<span class="base">
{{currentDaySize.label}}
<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu
slot="dropdown"
:style="{ left: this.left + 'px !important' }"
>
<el-dropdown-item
v-for="item in currentDaySizeOptions"
:key="item.value + 'ee'"
@click.native="handleSetDaySize(item)"
>
{{ item.label }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</el-row>
<div class="left" style=" width: 400px ;float: left;border-right: 1px solid #f7f7f7">
<leftMenu
@ -33,24 +55,6 @@
></div>
</div>
<div class="chart" ref="chart">
<!--<div class="left" :style="{ width: rightLineX + 'px' }">
<leftMenu
:list="list"
ref="leftMenu"
:BGScrollTop.sync="BGScrollTop"
@TableScrollTop="TableScrollTop"
@handlerRowClick="handlerRowClick"
@handlerGroup="handlerGroup"
@handlerExpand="handlerExpand"
></leftMenu>
<div class="rightLine" :style="{ left: rightLineX + 'px' }"></div>
<div
class="rightLine"
:style="{ left: rightLineX + 'px' }"
ref="rightLine"
@mousedown="rightLineMousedown"
></div>
</div>-->
<div class="date">
<div class="years" v-for="item in allDays" :key="item.year">
<div
@ -197,14 +201,6 @@
width: item.widthMe + 'px',
top: item.top + 'px'
}"
@mouseover="
lineMouseover(
`line${item.id}`,
$event,
item.id,
item.parentId,
index
)"
@mouseleave="lineMouseleave"
@mouseenter="
lineMouseenter(
@ -241,27 +237,7 @@
</template>
</div>
</div>
<div class="toolTip">
<!--<div class="today base" @click="handleGoToday">今天</div>-->
<el-dropdown trigger="click">
<span class="base">
{{currentDaySize.label}}
<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu
slot="dropdown"
:style="{ left: this.left + 'px !important' }"
>
<el-dropdown-item
v-for="item in currentDaySizeOptions"
:key="item.value + 'ee'"
@click.native="handleSetDaySize(item)"
>
{{ item.label }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<transition name="el-zoom-in-center">
<div
v-show="isShowMsg"
@ -319,6 +295,8 @@
},
data() {
return {
windowWidth:0,
windowHeight:0,
leftYear:2021,
timeRange: [],
disable: true,
@ -390,6 +368,13 @@
};
},
mounted(){
if(window.innerHeight){
this.windowWidth = window.innerWidth;
this.windowHeight = window.innerHeight;
}else{
this.windowHeight=document.body.clientHeight?document.body.clientHeight:document.documentElement.clientHeight;
this.windowWidth=document.body.clientWidth?document.body.clientWidth:document.documentElement.clientWidth;
}
let currentYear = new Date().getFullYear();
this.timeRange = [currentYear+'-01',currentYear+'-12'];
this.list = [...this.proList];
@ -534,10 +519,8 @@
},
//修改后续高度
resetTop(zindex, reduce, isexpand) {
// console.log(zindex, reduce, isexpand);
let num = reduce ? -40 : 40;
if (!reduce && !isexpand) {
// console.log(2);
this.list.forEach((item, index) => {
if (index > zindex) {
item.top = item.top + 40;
@ -549,7 +532,6 @@
}
});
} else {
// console.log(1);
this.list.forEach((item, index) => {
if (index > zindex) {
item.top = item.top + num * this.list[zindex].children.length;
@ -600,7 +582,6 @@
let widthMe = width - left;
parent.widthMe = parent.widthChild = widthMe;
parent.left = left;
// return parent;
},
// 转为分组
handlerGroup(row) {
@ -674,10 +655,8 @@
let index = this.list.findIndex(k => {
return k.id == parentId;
});
// console.log(parentId);
this.list.forEach(item => {
if (item.id == parentId) {
// console.log(item, this.list);
item.children.forEach(k => {
if (k.id == id) {
k.per = per;
@ -829,6 +808,17 @@
Math.round(end / this.currentDaySize.value) *
this.currentDaySize.value -
this.currentDaySize.value;
let infoDomX=0,infoDomY = 0;
if(this.windowWidth-e.clientX>230){
infoDomX = e.clientX;
}else{
infoDomX = this.windowWidth-230;
}
if(this.windowHeight-e.clientY>200){
infoDomY = e.clientY;
}else{
infoDomY = this.windowHeight-200;
}
this.currentProjectMsg = {
name: this.computedList[index].name,
allTime: (end - start) / this.currentDaySize.value + 1,
@ -836,8 +826,8 @@
per1: this.computedList[index].per1,
startTime: this.computedWithTime(start),
endTime: this.computedWithTime(end),
left: e.clientX,
top: e.clientY + 20
left:infoDomX ,
top: infoDomY
};
/*
* left:
@ -864,7 +854,6 @@
left: 0,
top: 0
};
// this.handlerSelect();
return;
}
this.currentLineDay = {
@ -1045,7 +1034,6 @@
});
}
},
beforeDestroy() {
window.removeEventListener("scroll", this.handleScroll);
document.onmousemove = document.onmouseup = null;
@ -1055,7 +1043,7 @@
<style lang="scss" scoped>
.chart {
height: 40vh;
height: 280px;
user-select: none;
position: relative;
overflow: hidden;
@ -1123,6 +1111,7 @@
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
border-radius: 4px;
border: 1px solid #ebeef5;
pointer-events: none;
.lineMsg {
margin-bottom: 10px;
@ -1165,10 +1154,6 @@
}
.toolTip {
position: fixed;
right: 0;
top: 550px;
z-index: 999;
.base {
display: inline-block;

View File

@ -687,9 +687,17 @@
that.field = []; //检查项目
let submit = isSubmit=='1'?false:true;
that.formData.forEach((item) => {
let field_value = null;
if(item.field_type==='int'){
field_value = parseInt(that.checkForm[item.field_key])
}else if(item.field_type==='float'){
field_value = parseFloat(that.checkForm[item.field_key])
}else{
field_value = that.checkForm[item.field_key];
}
that.field.push({
id: item.id,
field_value: that.checkForm[item.field_key],
field_value: field_value,
is_testok: item.is_testok,//单项检查结果
is_hidden: item.is_hidden
});

View File

@ -788,9 +788,15 @@
that.field = []; //检查项目
let submit = isSubmit=='1'?false:true;
that.formData.forEach((item) => {
let field_value = 0;
if(item.field_type==='int'){
field_value = parseInt(that.checkForm[item.field_key])
}else if(item.field_type==='float'){
field_value = parseFloat(that.checkForm[item.field_key])
}
that.field.push({
id: item.id,
field_value: that.checkForm[item.field_key],
field_value: field_value,
is_testok: item.is_testok,//单项检查结果
is_hidden: item.is_hidden
});

View File

@ -185,14 +185,6 @@ export const asyncRoutes = [
component: () => import('@/views/pm/management'),
meta: { title: '生产任务管理', icon: 'example', perms: ['pm_resources'] }
}
,
{
path: 'gantt',
name: 'gantt',
component: () => import('@/views/pm/gantt'),
meta: { title: '甘特图', icon: 'example',perms: ['vendor_manage'] },
// hidden: true
}
]
}
,

View File

@ -300,6 +300,8 @@
},
data() {
return {
windowWidth:0,
windowHeight:0,
timeRange:[],
disable: true,
//当前项是否是子集
@ -499,11 +501,6 @@
TableScrollTop(val) {
let lineBG = this.$refs.lineBG;
lineBG.scrollTo(0, val);
// lineBG.scrollTo({
// top: val,
// left: 0,
// behavior: "smooth"
// });
},
handlerBGScroll(e) {
this.BGScrollTop = this.$refs.chart.scrollTop;
@ -862,15 +859,9 @@
) *
this.currentDaySize.value +
this.currentDaySize.value;
let end =
parseInt(this.$refs[dom][0].style.left) +
parseInt(this.$refs[dom][0].style.width);
end =
Math.round(end / this.currentDaySize.value) * this.currentDaySize.value;
this.currentLineDay = {
start,
end
};
let end = parseInt(this.$refs[dom][0].style.left) + parseInt(this.$refs[dom][0].style.width);
end = Math.round(end / this.currentDaySize.value) * this.currentDaySize.value;
this.currentLineDay = { start, end };
this.isHover = true;
this.handlerSelect(this.computedList[index]);
this.lineMouseenter(dom, e, id, parentId, index);
@ -888,13 +879,21 @@
Math.round(
parseInt(this.$refs[dom][0].style.left) / this.currentDaySize.value
) * this.currentDaySize.value;
let end =
parseInt(this.$refs[dom][0].style.left) +
parseInt(this.$refs[dom][0].style.width);
end =
Math.round(end / this.currentDaySize.value) *
this.currentDaySize.value -
this.currentDaySize.value;
let end = parseInt(this.$refs[dom][0].style.left) + parseInt(this.$refs[dom][0].style.width);
end = Math.round(end / this.currentDaySize.value) * this.currentDaySize.value - this.currentDaySize.value;
debugger;
let infoDomX=0,infoDomY = 0;
if(this.windowWidth-e.clientX>230){
infoDomX = e.clientX;
}else{
infoDomX = this.windowWidth-230;
}
if(this.windowHeight-e.clientY>200){
infoDomY = e.clientY;
}else{
infoDomY = this.windowHeight-200;
}
// if(){}else{}
this.currentProjectMsg = {
name: this.computedList[index].name,
allTime: (end - start) / this.currentDaySize.value + 1,
@ -902,8 +901,8 @@
per1: this.computedList[index].per1,
startTime: this.computedWithTime(start),
endTime: this.computedWithTime(end),
left: e.clientX ,
top: e.clientY + 20
left:infoDomX ,
top: infoDomY
};
/*
* left:
@ -1095,6 +1094,10 @@
}
},
watch: {
detailInfo(e,data) {
showDiv.style.left = (event.pageX - 300) + 'px';
showDiv.style.top = (event.pageY - 120) + 'px';
},
currentDaySize(newValue, oldValue) {
this.list.forEach(item => {
item.left = (item.left / oldValue.value) * newValue.value;
@ -1111,6 +1114,13 @@
}
},
mounted() {
if(window.innerHeight){
this.windowWidth = window.innerWidth;
this.windowHeight = window.innerHeight;
}else{
this.windowHeight=document.body.clientHeight?document.body.clientHeight:document.documentElement.clientHeight;
this.windowWidth=document.body.clientWidth?document.body.clientWidth:document.documentElement.clientWidth;
}
document.addEventListener("scroll", this.handleScroll);
this.getDay();
this.setStoneLine();

View File

@ -34,8 +34,7 @@
fit
stripe
style="width: 100%"
height="100"
v-el-height-adaptive-table="{ bottomOffset: 40 }"
height="300"
>
<el-table-column type="index" width="50" />
@ -103,10 +102,14 @@
:limit.sync="listQuery.page_size"
@pagination="getplanList"
/>
<gantt
v-if="ganttShow"
:proList="proList"
></gantt>
</el-card>
<el-card class="box-card">
<div style="height: 40px;line-height: 40px;background: #F5F7FA;padding-left: 20px;">甘特图</div>
<gantt
style="position: relative"
v-if="proList.length>0"
:proList="proList"
></gantt>
</el-card>
</div>
</template>
@ -138,6 +141,7 @@ export default {
listLoading: true,
proList: [],
};
},
computed: {},
@ -183,9 +187,12 @@ export default {
obj.planTime = [startTime, endTime];
obj.per = item.count;
obj.type = 1;
obj.productName = item.product_.name;
obj.productNum = item.product_.specification;
obj.isShow = true;
arr.push(obj);
}
debugger;
that.proList = arr;
});
}

View File

@ -87,7 +87,7 @@
@pagination="getplanList"
/>
</el-card>
<el-tabs type="border-card" v-model="activeName" @tab-click="activeNameClick">
<el-tabs class="overFlowShow" type="border-card" v-model="activeName" @tab-click="activeNameClick">
<el-tab-pane label="订单排产" name="订单排产">
<el-table
:data="orderList.results"
@ -293,6 +293,8 @@
obj.planTime = [startTime, endTime];
obj.per = item.count;
obj.type = 1;
obj.productName = item.product_.name;
obj.productNum = item.product_.specification;
obj.isShow = true;
arr.push(obj);
}

View File

@ -1,12 +1,11 @@
<template>
<div class="app-container">
<el-card>
<div style="margin-top: 2px">
<el-button type="primary" icon="el-icon-plus" @click="handleCreate"
>新增</el-button
>
</div>
<div style="margin-top: 2px">
<el-button type="primary" icon="el-icon-plus" @click="handleCreate"
>新增
</el-button>
</div>
</el-card>
<el-card style="margin-top: 2px">
<el-table
@ -14,23 +13,23 @@
:data="customfieldList"
style="width: 100%"
>
<el-table-column type="index" width="50" />
<el-table-column type="index" width="50"/>
<el-table-column label="字段标识">
<template slot-scope="scope">{{ scope.row.field_key }}</template>
</el-table-column>
<el-table-column label="字段名称">
<template slot-scope="scope">{{ scope.row.field_name }}</template>
</el-table-column>
<el-table-column label="字段类型">
<el-table-column label="字段类型">
<template slot-scope="scope">{{ scope.row.field_type }}</template>
</el-table-column>
<el-table-column label="顺序ID">
<el-table-column label="顺序ID">
<template slot-scope="scope">{{ scope.row.sort }}</template>
</el-table-column>
<el-table-column label="字段描述">
<el-table-column label="字段描述">
<template slot-scope="scope">{{ scope.row.description }}</template>
</el-table-column>
<el-table-column label="创建时间">
<el-table-column label="创建时间">
<template slot-scope="scope">{{ scope.row.create_time }}</template>
</el-table-column>
@ -44,13 +43,15 @@
<el-link
v-if="checkPermission(['customfield_update'])"
@click="handleEdit(scope)"
>编辑</el-link
>编辑
</el-link
>
<el-link
v-if="checkPermission(['customfield_delete'])"
type="danger"
@click="handleDeleteCustomfield(scope)"
>删除</el-link
>删除
</el-link
>
</template>
</el-table-column>
@ -67,18 +68,18 @@
label-position="right"
:rules="rule1">
<el-form-item label="字段标识" prop="field_key">
<el-input v-model="customfield.field_key" placeholder="字段标识" />
<el-input v-model="customfield.field_key" placeholder="字段标识"/>
</el-form-item>
<el-form-item label="字段名称" prop="field_name">
<el-input v-model="customfield.field_name" placeholder="字段名称" />
<el-input v-model="customfield.field_name" placeholder="字段名称"/>
</el-form-item>
<el-form-item label="字段描述" prop="description">
<el-input v-model="customfield.description" placeholder="字段描述" />
<el-input v-model="customfield.description" placeholder="字段描述"/>
</el-form-item>
<el-form-item label="占位符" prop="placeholder">
<el-input v-model="customfield.placeholder" placeholder="占位符" />
<el-input v-model="customfield.placeholder" placeholder="占位符"/>
</el-form-item>
<el-form-item label="字段类型" prop="field_type">
<el-form-item label="展示标签" prop="field_type">
<el-select style="width: 100%" v-model="customfield.field_type" placeholder="请选择">
<el-option
v-for="item in options"
@ -88,28 +89,53 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item label="字段标签" v-show="customfield.field_type=='select'||customfield.field_type=='selects'">
<el-input v-model="customfield.label" placeholder="选项类型"/>
</el-form-item>
<el-form-item label="选项" v-show="customfield.field_type=='select'||customfield.field_type=='selects'">
<el-button @click.prevent="addDomain" style="border: none;">
<i class="el-icon-circle-plus-outline"></i>
<span style="font-size:14px;">添加</span>
</el-button>
<el-row v-for="(domain, $index) in choiceOption" :key=domain+$index style="margin-bottom: 10px">
<el-col :span="20">
<el-input v-model="choiceOption[$index]" auto-complete="off"></el-input>
<el-row v-for="(domain, $index) in choiceOption" :key='$index+1' style="margin-bottom: 10px">
<el-col :span="10">
<template>
<el-form-item label="id">
<el-input type="number" v-model="choiceOption[$index].id" auto-complete="off" placeholder="id为整数"></el-input>
</el-form-item>
</template>
</el-col>
<el-col :span="10">
<template>
<el-form-item label="name">
<el-input v-model="choiceOption[$index].name" auto-complete="off"></el-input>
</el-form-item>
</template>
</el-col>
<el-col :span="3" style="text-align: center" v-if="$index!==0">
<i class="el-icon-remove-outline" @click.prevent="removeDomain($index,'1')" style="color: red;font-size: 16px;"></i>
<i
class="el-icon-remove-outline"
style="color: red;font-size: 16px;"
@click.prevent="removeDomain($index,'1')"
></i>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="顺序ID" prop="sort">
<el-input v-model="customfield.sort" type="number" placeholder="顺序" />
<el-form-item label="顺序ID">
<el-input v-model="customfield.sort" type="number" placeholder="顺序"/>
</el-form-item>
<el-form-item label="默认值" prop="default_value">
<el-input v-model="customfield.default_value" placeholder="默认值" />
<el-form-item label="默认值">
<el-input v-model="customfield.default_value" placeholder="默认值"/>
</el-form-item>
<el-form-item label="模板" prop="field_template">
<el-input v-model="customfield.field_template" placeholder="你有一个待办工单:{title}" />
<el-form-item label="模板">
<el-input v-model="customfield.field_template" placeholder="你有一个待办工单:{title}"/>
</el-form-item>
<el-form-item label="是否隐藏">
<el-radio-group v-model="customfield.is_hidden">
<el-radio :label="false">显示</el-radio>
<el-radio :label="true">隐藏</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div style="text-align: right">
@ -120,39 +146,53 @@
</div>
</template>
<script>
import { getWfCustomfieldList, createWfCustomfield,updateWfCustomfield,deleteWfCustomfield } from "@/api/workflow";
import checkPermission from "@/utils/permission";
import vueJsonEditor from 'vue-json-editor'
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
const defaultcustomfield = {
field_key: "",
field_name: "",
};
export default {
components: { Pagination,vueJsonEditor },
name: "CTF",
props: ["ID"],
data() {
return {
customfield: {
field_key:'',
field_name:'',
placeholder:'',
field_type:'',
sort:'',
default_value:'',
field_template:'',
field_choice:[]
},
view_permission_check:false,
hasJsonFlag:true, // json是否验证通过
hasJsonFlag1:true, // json是否验证通过
hasJsonFlag2:true, // json是否验证通过
// customfieldList: {
// count:0
// },
customfieldList:[],
options: [{
import {getWfCustomfieldList, createWfCustomfield, updateWfCustomfield, deleteWfCustomfield} from "@/api/workflow";
import checkPermission from "@/utils/permission";
import vueJsonEditor from 'vue-json-editor'
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
const defaultcustomfield = {
field_key: "",
field_name: "",
};
export default {
components: {Pagination, vueJsonEditor},
name: "CTF",
props: ["ID"],
data() {
return {
customfield: {
field_key: '',
field_name: '',
placeholder: '',
field_type: '',
sort: '',
label: 'name',
default_value: '',
field_template: '',
field_choice: [],
is_hidden:false,
},
fieldHidden: true,
fieldBlock: false,
view_permission_check: false,
hasJsonFlag: true, // json是否验证通过
hasJsonFlag1: true, // json是否验证通过
hasJsonFlag2: true, // json是否验证通过
// customfieldList: {
// count:0
// },
customfieldList: [''],
labels:[{
label: '常规',
value: 'name'
},{
label: '名称|id',
value: 'name|id'
},{
label: '人员选择',
value: 'user'
}],
options: [{
value: 'string',
label: '文本'
}, {
@ -167,172 +207,176 @@ export default {
}, {
value: 'datetime',
label: '日期时间'
},{
}, {
value: 'select',
label: '单选'
},{
}, {
value: 'selects',
label: '多选'
}, {
value: 'textarea',
label: '文本域'
}, {
},{
value: 'file',
label: '附件'
}],
/*, {
value: 'selectuser',
label: '用户名'
}, {
value: 'selectusers',
label: '多选的用户名'
}, {
value: 'file',
label: '附件'
}],
}, */
boolean_field_display:[],
choiceOption:[''],
field_choice:[],
dialogVisible: false,
dialogType: "new",
rule1: {
name: [{ required: true, message: "请输入", trigger: "blur" }],
description: [{ required: true, message: "请输入", trigger: "blur" }]
boolean_field_display: [],
choiceOption: [{id:null,name:""}],
field_choice: [],
dialogVisible: false,
dialogType: "new",
rule1: {
name: [{required: true, message: "请输入", trigger: "blur"}],
description: [{required: true, message: "请输入", trigger: "blur"}]
},
};
},
computed: {},
watch: {},
created() {
this.getList();
},
methods: {
//添加字段选项
addDomain() {
this.choiceOption.push({id:null,name:""})
},
};
},
computed: {},
watch: {},
created() {
this.getList();
},
methods: {
//添加字段选项
addDomain() {
this.choiceOption.push('')
},
//删除字段选项
removeDomain(index){
this.choiceOption.splice(index, 1)
},
checkPermission,
//删除字段选项
removeDomain(index) {
this.choiceOption.splice(index, 1)
},
checkPermission,
getList() {
getList() {
getWfCustomfieldList(this.ID).then((response) => {
if (response.data) {
this.customfieldList = response.data;
}
getWfCustomfieldList(this.ID).then((response) => {
if (response.data) {
this.customfieldList = response.data;
}
});
},
handleFilter() {
this.listQuery.page = 1;
this.getList();
},
resetFilter() {
this.listQuery = {
page: 1,
page_size: 20,
}
this.getList();
},
handleCreate() {
this.customfield = Object.assign({}, defaultcustomfield);
this.dialogType = "new";
this.dialogVisible = true;
this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});
},
handleEdit(scope) {
this.customfield = Object.assign({}, scope.row); // copy obj
this.choiceOption = scope.row.field_choice;
this.dialogType = "edit";
this.dialogVisible = true;
this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});
},
handlecfgt(scope)
{
this.$router.push({name:"configuration",params:{customfield:scope.row.id}})
}
,
handleDelete(scope) {
this.$confirm("确认删除?", "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
await deleteWorkflow(scope.row.id);
this.getList();
this.$message.success("成功");
})
.catch((err) => {
console.error(err);
});
},
},
handleFilter() {
this.listQuery.page = 1;
this.getList();
},
resetFilter() {
this.listQuery = {
page: 1,
page_size: 20,
}
this.getList();
},
handleCreate() {
this.customfield = Object.assign({}, defaultcustomfield);
this.dialogType = "new";
this.dialogVisible = true;
/*this.$nextTick(() => {
// this.$refs["Form"].clearValidate();
this.customfield.label = 1;
});*/
},
handleEdit(scope) {
debugger;
this.customfield = Object.assign({}, scope.row); // copy obj
this.choiceOption = scope.row.field_choice;
this.dialogType = "edit";
this.dialogVisible = true;
/*this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});*/
},
handlecfgt(scope) {
this.$router.push({name: "configuration", params: {customfield: scope.row.id}})
}
,
handleDelete(scope) {
this.$confirm("确认删除?", "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
await this.deleteWorkflow(scope.row.id);
this.getList();
this.$message.success("成功");
})
.catch((err) => {
console.error(err);
});
},
async confirm(form) {
this.$refs[form].validate((valid) => {
if (valid) {
const isEdit = this.dialogType === "edit";
if (isEdit) {
this.checkJson();
this.checkJson1();
this.checkJson2();
this.customfield.field_choice = this.choiceOption;
updateWfCustomfield(this.customfield.id, this.customfield).then((res) => {
if (res.code >= 200) {
this.getList();
this.dialogVisible = false;
this.$message.success("成功");
}
});
let that = this,choiceArr = [];
this.$refs[form].validate((valid) => {
if (valid) {
const isEdit = that.dialogType === "edit";
if(that.choiceOption[0]&&that.choiceOption[0].name!==''&&that.choiceOption[0].name!==null&&that.choiceOption[0].name!==undefined){
that.choiceOption.forEach((item)=>{
let obj = new Object();
obj.id = parseInt(item.id);
obj.name = item.name;
choiceArr.push(obj);
})
}
debugger;
console.log(choiceArr);
that.customfield.field_choice = choiceArr;
console.log(that.customfield);
if (isEdit) {
updateWfCustomfield(this.customfield.id, this.customfield).then((res) => {
if (res.code >= 200) {
this.getList();
this.dialogVisible = false;
this.$message.success("成功");
}
});
} else {
this.customfield.workflow = this.ID;
createWfCustomfield(this.customfield).then((res) => {
if (res.code >= 200) {
this.getList();
this.dialogVisible = false;
this.$message.success("成功");
}
});
}
} else {
this.checkJson();
this.checkJson1();
this.checkJson2();
this.customfield.workflow=this.ID;
this.customfield.field_choice = this.choiceOption;
createWfCustomfield(this.customfield).then((res) => {
if (res.code >= 200) {
this.getList();
this.dialogVisible = false;
this.$message.success("成功");
}
});
return false;
}
} else {
return false;
}
});
},
handleDeleteCustomfield(scope) {
this.$confirm("确认删除?", "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
await deleteWfCustomfield(scope.row.id);
this.getList();
this.$message.success("成功");
})
.catch((err) => {
console.error(err);
});
},
},
handleDeleteCustomfield(scope) {
this.$confirm("确认删除?", "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
await deleteWfCustomfield(scope.row.id);
this.getList();
this.$message.success("成功");
})
.catch((err) => {
console.error(err);
});
},
onJsonChange (value) {
/* onJsonChange(value) {
// console.log('更改value:', value);
// 实时保存
this.onJsonSave(value)
},
onJsonSave (value) {
onJsonSave(value) {
// console.log('保存value:', value);
this.boolean_field_display = value
this.hasJsonFlag = true
@ -343,61 +387,61 @@ export default {
},
// 检查json
checkJson(){
if (this.hasJsonFlag == false){
alert("布尔显示定义json验证失败")
checkJson() {
if (this.hasJsonFlag == false) {
// alert("布尔显示定义json验证失败")
return false
} else {
alert("布尔显示定义json验证成功")
// alert("布尔显示定义json验证成功")
return true
}
},
onJsonChange1 (value) {
onJsonChange1(value) {
// console.log('更改value:', value);
// 实时保存
this.onJsonSave1(value)
},
onJsonSave1 (value) {
onJsonSave1(value) {
this.field_choice = value
this.hasJsonFlag1 = true
},
onError1(value) {
this.hasJsonFlag1 = false
},
// 检查json
checkJson1(){
if (this.hasJsonFlag1 == false){
alert("选项json验证失败")
// 检查json
checkJson1() {
if (this.hasJsonFlag1 == false) {
// alert("选项json验证失败")
return false
} else {
alert("选项json1验证成功")
// alert("选项json1验证成功")
return true
}
},
onJsonChange2 (value) {
onJsonChange2(value) {
// console.log('更改value:', value);
// 实时保存
this.onJsonSave2(value)
},
onJsonSave2 (value) {
onJsonSave2(value) {
this.field_choice = value
this.hasJsonFlag2 = true
},
onError2(value) {
this.hasJsonFlag2 = false
},
// 检查json
checkJson2(){
if (this.hasJsonFlag2 == false){
alert("标签json验证失败")
// 检查json
checkJson2() {
if (this.hasJsonFlag2 == false) {
// alert("标签json验证失败")
return false
} else {
alert("标签json1验证成功")
// alert("标签json1验证成功")
return true
}
},
}*/
// },
},
};
},
};
</script>

View File

@ -428,20 +428,20 @@ export default {
// 检查json
checkJson(){
if (this.hasJsonFlag == false){
alert("限制表达式json验证失败")
// alert("限制表达式json验证失败")
return false
} else {
alert("限制表达式json验证成功")
// alert("限制表达式json验证成功")
return true
}
},
// 检查json
checkJson2(){
if (this.hasJsonFlag1 == false){
alert("展现表单字段json验证失败")
// alert("展现表单字段json验证失败")
return false
} else {
alert("展现表单字段json1验证成功")
// alert("展现表单字段json1验证成功")
return true
}
},

View File

@ -184,14 +184,20 @@
<el-col :span="1" style="height: 1px;"></el-col>
<el-col :span="8">
<el-select style="width: 100%" v-model="item.value" placeholder="请选择状态">
<el-option label="只读" value="1"></el-option>
<el-option label="必填" value="2"></el-option>
<el-option label="可选" value="3"></el-option>
<el-option
v-for="item in state_fields"
:label="item.label"
:value="item.value"
:key="item.value"
></el-option>
</el-select>
</el-col>
<el-col :span="2" style="text-align: center" v-if="$index!==0">
<i class="el-icon-remove-outline" @click.prevent="removeStatusChange($index)"
style="color: red;font-size: 16px;"></i>
<i
class="el-icon-remove-outline"
style="color: red;font-size: 16px;"
@click.prevent="removeStatusChange($index)"
></i>
</el-col>
</el-row>
</el-form-item>
@ -291,6 +297,21 @@
value: 9,
label: '代码获取'
}],
state_fields:[
{
value: 1,
label: '只读'
},{
value: 2,
label: '必填'
},{
value: 3,
label: '可选'
},{
value: 4,
label: '隐藏'
},
],
display_form_str: [],
limit_expression: [],
dialogVisible: false,
@ -315,8 +336,8 @@
methods: {
checkPermission(value){
debugger;
console.log(checkPermission(value))
/* debugger;
console.log(checkPermission(value))*/
checkPermission(value);
},

View File

@ -308,9 +308,9 @@
<el-select style="width: 100%" v-model="item.default_value" placeholder="请选择">
<el-option
v-for="item1 in item.field_choice"
:key="item1"
:label="item1"
:value="item1"
:key="item1.id"
:label="item1.name"
:value="item1.id"
>
</el-option>
</el-select>
@ -319,9 +319,9 @@
<el-select style="width: 100%" multiple v-model="item.default_value" placeholder="请选择">
<el-option
v-for="item1 in item.field_choice"
:key="item1"
:label="item1"
:value="item1"
:key="item1.id"
:label="item1.name"
:value="item1.id"
>
</el-option>
</el-select>

View File

@ -35,7 +35,7 @@
</el-form-item>
</el-col>
<el-row style="display: flex;flex-wrap: wrap;width:100%;">
<el-col v-for="item in ticketDetail.ticket_data_" :key="item.id" :span="12">
<el-col v-for="item in ticketDetail.ticket_data_" :key="item.id" :span="12" v-show="!item.is_hidden">
<el-form-item :label="item.field_name" v-if="item.field_state==='1'">
<span>{{ticketDetail.ticket_data[item.field_key]}}</span>
</el-form-item>
@ -73,9 +73,9 @@
<el-select style="width: 100%" v-model="ticketDetail.ticket_data[item.field_key]" placeholder="请选择">
<el-option
v-for="item1 in item.field_choice"
:key="item1"
:label="item1"
:value="item1"
:key="item1.id"
:label="item1.name"
:value="item1.id"
>
</el-option>
</el-select>
@ -84,9 +84,9 @@
<el-select style="width: 100%" multiple v-model="ticketDetail.ticket_data[item.field_key]" placeholder="请选择">
<el-option
v-for="item1 in item.field_choice"
:key="item1"
:label="item1"
:value="item1"
:key="item1.id"
:label="item1.name"
:value="item1.id"
>
</el-option>
</el-select>
@ -132,7 +132,6 @@
<el-button v-if="ticketDetail.in_add_node" class="filter-item" type="primary" @click="handleClick('3')">加签处理</el-button>
<el-button v-for="item in operationBtn" :key="item.id" class="filter-item" type="primary" @click="operationSubmit(item.id)">{{item.name}}</el-button>
</div>
</div>
</el-card>
<el-card style="margin-left: 10px">
@ -205,6 +204,7 @@
</template>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
import { getOrgAll } from "@/api/org";
import { upUrl, upHeaders } from "@/api/file";
import { getUserList } from "@/api/user";
import {getWorkflowList,getWfCustomfieldList,getWfStateList,getWfTransitionList,ticketHandle
@ -247,6 +247,7 @@
watchedName:'',
watchedCreateTime:'',
logs:[],
orgList:[],
staffs:[],
edges: [],
nodes: [],
@ -288,156 +289,189 @@
getTicketTransitions(ticketId).then(res=>{
this.operationBtn = res.data;
})
getWfFlowSteps( ticketId).then((res)=>{
if(res.data){
//流程步骤数组
that.flowSteps = res.data;
getTicketDetail( ticketId).then((res)=>{
if(res.data){
that.tooltip = that.createTooltip();
that.ticketDetail = res.data;
console.log(that.ticketDetail.state_.enable_retreat);
debugger;
let state = res.data.state;
let dat = that.flowSteps.filter((item)=>{
return item.id==state;
})
that.sort = dat[0].sort;
that.actives = that.flowSteps.indexOf(dat[0]);
if( that.flowSteps.length-that.actives >1){}else{
that.actives =that.flowSteps.length;
}
var g = new dagreD3.graphlib.Graph().setGraph({
rankdir: 'DL',
nodesep: 100,
edgesep: 10,//两条线之间的距离
ranksep: 50,//节点之间的距离
marginx: 60,
marginy: 20,
});
//获取state得到节点
getWfStateList(workflow).then((response) => {
if (response.data) {
let nodes = response.data;
// 添加节点
nodes.forEach((item) => {
if(item.sort==that.sort){
g.setNode(item.id, {
// 节点标签
label: item.name,
// 节点形状
shape: 'rect',
toolText: item.name,
//节点样式
style: "fill:#409EFF;stroke:#000",
labelStyle: "fill:#000;",
rx :5,//矩形节点圆角度
ry :5
});
}else{
g.setNode(item.id, {
// 节点标签
label: item.name,
// 节点形状
shape: 'rect',
toolText: item.name,
//节点样式
style: "fill:#fff;stroke:#000",
labelStyle: "fill:#000;",
rx :5,//矩形节点圆角度
ry :5
});
getOrgAll().then((orgRes) => {
that.orgList = orgRes.data;
getUserList({page:0}).then(userRes=>{
if(userRes.data){
that.staffs = userRes.data;
getWfFlowSteps( ticketId).then((res)=>{
if(res.data){
//流程步骤数组
that.flowSteps = res.data;
getTicketDetail( ticketId).then((res)=>{
if(res.data){
that.tooltip = that.createTooltip();
that.ticketDetail = res.data;
for (let i=0;i<that.ticketDetail.ticket_data_.length;i++) {
if(that.ticketDetail.ticket_data_[i].label==="sys_user"){
that.ticketDetail.ticket_data_[i].field_choice = that.staffs;
}
if(that.ticketDetail.ticket_data_[i].label==="deptSelect"){
that.ticketDetail.ticket_data_[i].field_choice = that.orgList;
}
}
});
g.nodes().forEach(function (v) {
console.log("Node " + v + ": " + JSON.stringify(g.node(v)));
});
//获取流转得到线 链接关系
getWfTransitionList(workflow).then((res)=>{
if(res.data){
let transitionList = res.data;
transitionList.forEach((transitions)=>{
let transition0 = transitions;
if (transition0.condition_expression.length>0){
g.setNode(transition0.source_state_.id+100000, {label: "条件表达式", style: "fill: #a4d088", shape: "diamond"});
g.setEdge(transition0.source_state_.id, transition0.source_state_.id+100000, {
// 边标签
label: transition0.name,
style: "fill:#ffffff;stroke:#c0c1c3;stroke-width:1.5px"
});
let condition_expression = transition0.condition_expression;
condition_expression.forEach(condition_expression0=>{
g.setEdge(transition0.source_state_.id+100000, condition_expression0.target_state, {
label: condition_expression0.label,
style: "fill:#ffffff;stroke:#c0c1c3;stroke-width:1.5px"
})
})
}else{
g.setEdge(transition0.source_state_.id, transition0.destination_state_.id, {
// 边标签
label: transition0.name,
// 边样式
style: "fill:#ffffff;stroke:#c0c1c3;stroke-width:1.5px" // 根据后台数据来改变连线的颜色
});
console.log(that.ticketDetail.state_.enable_retreat);
let state = res.data.state;
let dat = that.flowSteps.filter((item)=>{
return item.id==state;
})
debugger;
let state_fields = dat[0].state_fields;
if(state_fields!=={}){
for (let labe in state_fields) {
for(let j = 0;j<that.ticketDetail.ticket_data_.length;j++){
if(that.ticketDetail.ticket_data_[j].field_key===labe){
debugger;
console.log(labe)
console.log(state_fields[labe])
if(state_fields[labe]!==4){
that.ticketDetail.ticket_data_[j].is_hidden = false
}else{
that.ticketDetail.ticket_data_[j].is_hidden = true
}
}
}
})
g.nodes().length-1
g.nodes().forEach(function (v) {
console.log("Node " + v + ": " + JSON.stringify(g.node(v)));
});
// 创建渲染器
let render = new dagreD3.render();
// 选择 svg 并添加一个g元素作为绘图容器.
let svg = d3.select('#mySvg');
let svgGroup = svg.append('g');
let zoom = d3.zoom()
.on("zoom", function() {
svgGroup.attr("transform", d3.event.transform);
});
svg.call(zoom);
// 在绘图容器上运行渲染器生成流程图.
render(d3.select("svg g"), g);
// this.getTicketFlowlogs(ticketId);
getTicketFlowlog(ticketId).then(res=>{
if(res.data){
that.logs = res.data;
svgGroup
.selectAll('g.node')
.on('mouseover', (v) => {
// 假如当前toolText为"",则不展示
//这里就是自定义tooltip的内容
let filList = [], strList = [];
filList = nodes.filter((ii) => {
return ii.name === g.node(v).label
})
if (!filList.length) {
return
}
filList.map((k) => {
let filte = that.logs.filter(item=>{
return item.state == k.id;
})
//每个
let str = '处理人:'+filte[0].participant_.name;
strList.push(str)
})
that.tipVisible(strList)
})
.on('mouseout', () => {
this.tipHidden()
})
}
})
}
}
that.sort = dat[0].sort;
that.actives = that.flowSteps.indexOf(dat[0]);
if( that.flowSteps.length-that.actives >1){}else{
that.actives =that.flowSteps.length;
}
var g = new dagreD3.graphlib.Graph().setGraph({
rankdir: 'DL',
nodesep: 100,
edgesep: 10,//两条线之间的距离
ranksep: 50,//节点之间的距离
marginx: 60,
marginy: 20,
});
//获取state得到节点
getWfStateList(workflow).then((response) => {
if (response.data) {
let nodes = response.data;
}else{}
});
}
});
}
});
}
// 添加节点
nodes.forEach((item) => {
if(item.sort==that.sort){
g.setNode(item.id, {
// 节点标签
label: item.name,
// 节点形状
shape: 'rect',
toolText: item.name,
//节点样式
style: "fill:#409EFF;stroke:#000",
labelStyle: "fill:#000;",
rx :5,//矩形节点圆角度
ry :5
});
}else{
g.setNode(item.id, {
// 节点标签
label: item.name,
// 节点形状
shape: 'rect',
toolText: item.name,
//节点样式
style: "fill:#fff;stroke:#000",
labelStyle: "fill:#000;",
rx :5,//矩形节点圆角度
ry :5
});
}
});
g.nodes().forEach(function (v) {
console.log("Node " + v + ": " + JSON.stringify(g.node(v)));
});
//获取流转得到线 链接关系
getWfTransitionList(workflow).then((res)=>{
if(res.data){
let transitionList = res.data;
transitionList.forEach((transitions)=>{
let transition0 = transitions;
if (transition0.condition_expression.length>0){
g.setNode(transition0.source_state_.id+100000, {label: "条件表达式", style: "fill: #a4d088", shape: "diamond"});
g.setEdge(transition0.source_state_.id, transition0.source_state_.id+100000, {
// 边标签
label: transition0.name,
style: "fill:#ffffff;stroke:#c0c1c3;stroke-width:1.5px"
});
let condition_expression = transition0.condition_expression;
condition_expression.forEach(condition_expression0=>{
g.setEdge(transition0.source_state_.id+100000, condition_expression0.target_state, {
label: condition_expression0.label,
style: "fill:#ffffff;stroke:#c0c1c3;stroke-width:1.5px"
})
})
}else{
g.setEdge(transition0.source_state_.id, transition0.destination_state_.id, {
// 边标签
label: transition0.name,
// 边样式
style: "fill:#ffffff;stroke:#c0c1c3;stroke-width:1.5px" // 根据后台数据来改变连线的颜色
});
}
})
g.nodes().length-1
g.nodes().forEach(function (v) {
console.log("Node " + v + ": " + JSON.stringify(g.node(v)));
});
// 创建渲染器
let render = new dagreD3.render();
// 选择 svg 并添加一个g元素作为绘图容器.
let svg = d3.select('#mySvg');
let svgGroup = svg.append('g');
let zoom = d3.zoom()
.on("zoom", function() {
svgGroup.attr("transform", d3.event.transform);
});
svg.call(zoom);
// 在绘图容器上运行渲染器生成流程图.
render(d3.select("svg g"), g);
// this.getTicketFlowlogs(ticketId);
getTicketFlowlog(ticketId).then(res=>{
if(res.data){
that.logs = res.data;
svgGroup
.selectAll('g.node')
.on('mouseover', (v) => {
// 假如当前toolText为"",则不展示
//这里就是自定义tooltip的内容
let filList = [], strList = [];
filList = nodes.filter((ii) => {
return ii.name === g.node(v).label
})
if (!filList.length) {
return
}
filList.map((k) => {
let filte = that.logs.filter(item=>{
return item.state == k.id;
})
//每个
let str = '处理人:'+filte[0].participant_.name;
strList.push(str)
})
that.tipVisible(strList)
})
.on('mouseout', () => {
this.tipHidden()
})
}
})
}else{}
});
}
});
}
});
}
});
}
})
});
},
activated(){
@ -498,7 +532,6 @@
.style('left', `${d3.event.pageX + 15}px`)
.style('top', `${d3.event.pageY-10}px`)
},
// tooltip隐藏
tipHidden() {
this.tooltip
@ -588,9 +621,6 @@
}
})
},
addNode(){
},
}
}
</script>

View File

@ -247,6 +247,7 @@
>报废
</el-link>!-->
<el-link type="primary"
v-if="!scope.row.ticket"
@click="handleRetrial(scope)"
>重审
</el-link>
@ -508,98 +509,78 @@
</el-dialog>
<!--不合格半成品重审-->
<el-dialog title="不合格半成品重审" :close-on-click-modal="false" :visible.sync="limitedRetrial">
<el-form label-width="80px" label-position="right">
<el-row v-for="(item, $index) in fieldList.record_data" :key="$index">
<el-form-item
v-if="item.field_type === 'string'"
:label="item.field_name"
>
<el-input placeholder="请输入" v-model="item.sort" />
</el-form-item>
<el-form-item
v-else-if="item.field_type === 'int'"
:label="item.field_name"
>
<el-input
type="number"
placeholder="请输入"
v-model="item.sort"
/>
</el-form-item>
<el-form-item
v-else-if="item.field_type === 'float'"
:label="item.field_name"
>
<el-input
type="number"
placeholder="请输入"
v-model="item.sort"
/>
</el-form-item>
<el-form-item
v-else-if="item.field_type === 'date'"
:label="item.field_name"
>
<el-form label-width="130px" label-position="right">
<el-form-item :label="item.field_name" v-for="item in customfieldList" :key="item.id" v-show="!item.is_hidden">
<template v-if="item.field_type=='string'">
<el-input v-model="item.default_value" :placeholder="item.description" />
</template>
<template v-if="item.field_type==='int'">
<el-input v-model="item.default_value" type="number" :placeholder="item.description" oninput="value=value.replace(/[^\d]/g,'')" />
</template>
<template v-if="item.field_type==='float'">
<el-input v-model="item.default_value" type="number" :placeholder="item.description" />
</template>
<template v-if="item.field_type==='date'">
<el-date-picker
v-model="item.create_time"
v-model="item.default_value"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
style="width: 100%"
>
</el-date-picker>
</el-form-item>
<el-form-item
v-else-if="item.field_type === 'datetime'"
:label="item.field_name"
>
</template>
<template v-if="item.field_type==='datetime'">
<el-date-picker
v-model="item.create_time"
v-model="item.default_value"
type="datetime"
placeholder="选择日期"
value-format="yyyy-MM-dd HH:mm:ss"
style="width: 100%"
>
</el-date-picker>
</el-form-item>
<el-form-item
v-else-if="item.field_type === 'select'"
:label="item.field_name"
>
<el-select
style="width: 100%"
v-model="item.sort"
placeholder="请选择"
>
</template>
<template v-if="item.field_type==='select'">
<el-select style="width: 100%" v-model="item.default_value" placeholder="请选择">
<el-option
v-for="item1 in item.field_choice"
:key="item1"
:label="item1"
:value="item1"
:key="item1.id"
:label="item1.name"
:value="item1.id"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item
v-else-if="item.field_type === 'selects'"
:label="item.field_name"
>
<el-select
style="width: 100%"
v-model="optio"
multiple
placeholder="请选择"
>
</template>
<template v-if="item.field_type==='selects'">
<el-select style="width: 100%" multiple v-model="item.default_value" placeholder="请选择">
<el-option
v-for="item1 in item.field_choice"
:key="item1"
:label="item1"
:value="item1"
:key="item1.id"
:label="item1.name"
:value="item1.id"
>
</el-option>
</el-select>
</el-form-item>
</el-row>
</template>
<template v-if="item.field_type==='textarea'">
<el-input type="textarea" :rows="3" v-model="item.default_value" placeholder="内容" />
</template>
<template v-if="item.field_type==='file'">
<el-upload
ref="upload"
:action="upUrl"
:on-preview="handlePreview"
:on-success="handleUpSuccess"
:on-remove="handleRemove"
:headers="upHeaders"
:file-list="fileList"
:limit="1"
accept=".doc,.docx,.xls,.xlsx,.ppt,.pptx"
>
<el-button size="small" type="primary">上传文件</el-button>
</el-upload>
</template>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="limitedRetrial = false"> </el-button>
@ -609,12 +590,15 @@
</div>
</template>
<script>
import { getOrgAll } from "@/api/org";
import { getUserList } from "@/api/user";
import {getWarehouseList} from "@/api/inm";
import checkPermission from "@/utils/permission";
import customForm from '@/components/customForm/index';
import reviewForm from '@/components/customForm/review';
import {getwproductList, wproductTest, wproductPutin, createputins,testInit,scrap,getRetrial} from "@/api/wpm";
import checkPermission from "@/utils/permission";
import {getWarehouseList} from "@/api/inm";
import {getWfCustomfieldList,createTicket ,getWorkflowInit} from "@/api/workflow";
import {getMaterialList, getrecordformList, getrffieldList} from "@/api/mtm";
import {getwproductList, wproductTest, wproductPutin, createputins,testInit,scrap,getRetrial} from "@/api/wpm";
import {getTestRecord,getTestRecordItem,putTestRecordItem,delTestRecordItem,subTestRecordItem} from "@/api/qm";
import {genTree} from "@/utils";
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
@ -698,13 +682,17 @@
{ lable: "其他", value: 40 },
],
recordList: [],
transitions: null,
options: [],
listLoading: true,
fieldList: [],
originList: [],
is_testok: "true",
field: [],
userList: [],//用户列表
orgList: [],//部门列表
recordformList: [],
customfieldList: [],
recordform: null,
recordId: null,
fifo_detail: "",
@ -728,6 +716,15 @@
dialogFormVisibles: false,
limitedCheckRecord: false,
testrecord: {},
retrialItem: {},//复检对象
retrialResponse: {},//复检对象
//复检表单
retrialForm: {
title:'',
transition:null,
workflow:'',
ticket_data:{},
},
WarehouseData: "",
formName: '项目检查表',
};
@ -823,14 +820,64 @@
},
//不合格产品重审展示
handleRetrial(scope){
let that =this;
this.retrialItem = Object.assign({}, scope.row);
console.log(this.retrialItem);
getRetrial(scope.row.id).then(res=>{
debugger;
console.log(res)
that.retrialResponse = res.data;
getWorkflowInit(res.data.workflow).then((response) => {
if (response.data) {
debugger;
console.log(response.data);
that.retrialForm.transition = response.data.transitions[0].id;
that.customfieldList = response.data.field_list;
for (let i=0;i<that.customfieldList.length;i++) {
if(that.customfieldList[i].field_key==="wproduct"){
that.customfieldList[i].default_value = that.retrialResponse.exist_data.wproduct;
}
if(that.customfieldList[i].field_key==="wproduct_name"){
that.customfieldList[i].default_value = that.retrialResponse.exist_data.wproduct_name;
}
if(that.customfieldList[i].field_key==="wproduct_type"){
that.customfieldList[i].default_value = that.retrialResponse.exist_data.wproduct_specification;
}
if(that.customfieldList[i].field_key==="finder"){
that.customfieldList[i].default_value = that.retrialResponse.exist_data.finder;
}
if(that.customfieldList[i].field_key==="find_process"){
that.customfieldList[i].default_value = that.retrialResponse.exist_data.find_process;
}
if(that.customfieldList[i].label==="sys_user"){
that.customfieldList[i].field_choice = that.userList;
}
if(that.customfieldList[i].label==="deptSelect"){
that.customfieldList[i].field_choice = that.orgList;
}
}
}
});
that.limitedRetrial = true;
})
},
//不合格产品重审提交
retrialSubmit(){
let that = this;
let fields = this.customfieldList;
let obj = new Object();
for(let i=0;i<fields.length;i++){
obj[fields[i].field_key] = fields[i].default_value
}
this.retrialForm.ticket_data = obj;
this.retrialForm.workflow = that.retrialResponse.workflow;
this.retrialForm.title = that.retrialResponse.exist_data.wproduct_name+'的重审';
createTicket(this.retrialForm).then((res) => {
if (res.code >= 200) {
this.getList4();
this.limitedRetrial = false;
this.$message.success("成功");
}
});
},
//夹层半成品列表
getList3() {
@ -880,7 +927,6 @@
//点击检验:如果有一个直接进入如果有多个表再进行选择
handleInspection(scope,index) {
//调该物料对应的检查表
// debugger;
let that = this;
this.innerIndex = index;
// this.outerVisible = true;
@ -890,7 +936,6 @@
this.recordform = null;
getrecordformList(this.listQueryrecordform).then((response) => {
if (response.data) {
debugger;
this.recordformList = response.data;
if(response.data.length===1){
that.recordform = response.data[0].id;
@ -921,7 +966,6 @@
that.limitedCheckRecord = true;
getTestRecord({wproduct:scope.row.id}).then(res=>{
if(res.code==200){
debugger;
that.recordList = res.data.results;
}else{
this.$message.error(res.msg);
@ -930,7 +974,6 @@
},
//点击记录里的检验
handleInspectionRecord(scope){
// debugger;
let that =this;
that.recordVisible = false;
that.recordId = scope.row.id;
@ -949,7 +992,6 @@
that.hasPicture = true;
}
getTestRecordItem(scope.row.id).then((res) => {
// debugger;
let arr = [];
let fieldList = res.data.record_data;
for(let i=0;i<that.fieldList.length;i++){
@ -972,7 +1014,6 @@
}
});
}else if(that.innerIndex==='2'){//复检
debugger;
getTestRecordItem(scope.row.id).then((res) => {
if (res.data) {
that.hasPicture = false;
@ -995,8 +1036,6 @@
}
that.fieldList.push(obj)
}
debugger;
console.log(that.fieldList)
// that.fieldList = [...fieldList];
let arr = fieldList.filter(item => {
return item.field_type === 'draw'
@ -1015,11 +1054,9 @@
},
//半产品复检
handleReview() {
// debugger;
let that = this;
testInit({ wproduct: this.wproduct,form: that.recordform}).then((response) => {
if (response.data) {
debugger;
that.hasPicture = false;
that.recordId = response.data.id;
that.formName = response.data.form_.name;
@ -1054,7 +1091,6 @@
//根据选择的表渲染检查项目
submitrecordform(index) {
// debugger;
let that = this;
this.outerVisible = false;
if (that.recordform != "") {
@ -1080,7 +1116,6 @@
}
});
}else if(index==='2'){//复检
// debugger;
that.handleReview();
}
} else this.$message.error("请选择检查表!");
@ -1145,7 +1180,6 @@
params.record_data = value.record_data;
params.is_testok = value.is_testok;
putTestRecordItem(id,params).then((res) => {
// debugger;
if (res.code >= 200) {
that.recordVisible = false;
that.limitedReview = false;
@ -1202,5 +1236,15 @@
this.reload()
},
},
mounted() {
getUserList({page:0}).then(response => {
if (response.data) {
this.userList = response.data
}
});
getOrgAll().then((response) => {
this.orgList = response.data;
});
}
}
</script>

View File

@ -187,7 +187,7 @@
v-if="item.field_type === 'string'"
:label="item.field_name"
>
<el-input placeholder="请输入" v-model="item.sort" />
<el-input v-model="item.field_value" placeholder="请输入"/>
</el-form-item>
<el-form-item
v-else-if="item.field_type === 'int'"
@ -196,7 +196,7 @@
<el-input
type="number"
placeholder="请输入"
v-model="item.sort"
v-model="item.field_value"
/>
</el-form-item>
<el-form-item
@ -206,7 +206,7 @@
<el-input
type="number"
placeholder="请输入"
v-model="item.sort"
v-model="item.field_value"
/>
</el-form-item>
<el-form-item
@ -214,7 +214,7 @@
:label="item.field_name"
>
<el-date-picker
v-model="item.create_time"
v-model="item.field_value"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
@ -227,7 +227,7 @@
:label="item.field_name"
>
<el-date-picker
v-model="item.create_time"
v-model="item.field_value"
type="datetime"
placeholder="选择日期"
value-format="yyyy-MM-dd HH:mm:ss"
@ -241,7 +241,7 @@
>
<el-select
style="width: 100%"
v-model="item.sort"
v-model="item.field_value"
placeholder="请选择"
>
<el-option
@ -259,7 +259,7 @@
>
<el-select
style="width: 100%"
v-model="optio"
v-model="item.field_value"
multiple
placeholder="请选择"
>
@ -604,6 +604,7 @@ export default {
},
fieldList: {
count: 0,
name:'',
},
listQueryfield: {
page: 1,
@ -798,7 +799,9 @@ export default {
this.formID = scope.row.id;
recordInit( this.formID).then((response) => {
if (response.data) {
debugger;
this.fieldList = response.data;
this.fieldList.name = response.data.form_.name;
}
});
this.dialogVisibleForm = true;
@ -820,16 +823,21 @@ export default {
let _this = this;
_this.record_data = []; //检查项目
this.fieldList.record_data.forEach((item) => {
if(item.field_type==='int'){
item.field_value = parseInt(item.field_value)
}else if(item.field_type==='float'){
item.field_value = parseFloat(item.field_value)
}
_this.record_data.push({
id: item.id,
field_value: item.field_value,
field_value:item.field_value,
});
});
this.record.record_data = _this.record_data;
createRecord(this.formID, this.record).then((res) => {
if (res.code >= 200) {
this.getrecordLists();
this.$message.success("创建成功!");
this.$message.success("提交成功!");
this.dialogVisibleForm = false;
}

File diff suppressed because it is too large Load Diff

View File

View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class DevelopConfig(AppConfig):
name = 'apps.develop'

View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@ -0,0 +1,10 @@
from django.db.models import base
from rest_framework import urlpatterns
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from apps.develop.views import CleanDataView
urlpatterns = [
path('cleandata/', CleanDataView.as_view()),
]

View File

@ -0,0 +1,28 @@
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.permissions import IsAdminUser
from rest_framework.response import Response
from apps.inm.models import FIFO, Inventory, MaterialBatch
from apps.mtm.models import Material
from apps.pm.models import ProductionPlan
from apps.sam.models import Order
from apps.wf.models import Ticket
from apps.wpm.models import Operation
# Create your views here.
class CleanDataView(APIView):
permission_classes = [IsAdminUser]
def post(self, request, format=None):
"""
清空数据库
"""
Order.objects.all().delete(soft=False)
ProductionPlan.objects.all().delete(soft=False)
FIFO.objects.all().delete(soft=False)
Material.objects.filter(type__in=[Material.MA_TYPE_GOOD, Material.MA_TYPE_HALFGOOD]).update(count=0)
MaterialBatch.objects.filter(material__type__in=[Material.MA_TYPE_GOOD, Material.MA_TYPE_HALFGOOD]).delete()
Inventory.objects.filter(material__type__in=[Material.MA_TYPE_GOOD, Material.MA_TYPE_HALFGOOD]).delete()
Ticket.objects.all().delete(soft=False)
Operation.objects.all().delete()
return Response()

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.9 on 2021-12-27 01:48
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('wpm', '0038_auto_20211227_0948'),
('inm', '0023_auto_20211216_0945'),
]
operations = [
migrations.AlterField(
model_name='fifoitemproduct',
name='wproduct',
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='fifoitem_wproduct', to='wpm.wproduct', verbose_name='关联的动态产品'),
),
migrations.AlterField(
model_name='iproduct',
name='wproduct',
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='iproduct_wproduct', to='wpm.wproduct', verbose_name='关联的动态产品'),
),
]

View File

@ -92,7 +92,8 @@ class IProduct(BaseModel):
material = models.ForeignKey(Material, verbose_name='物料类型', on_delete=models.CASCADE)
warehouse = models.ForeignKey(WareHouse, on_delete=models.CASCADE, verbose_name='所在仓库')
batch = models.CharField('所属批次号', max_length=100, default='')
wproduct = models.ForeignKey('wpm.wproduct', on_delete=models.CASCADE, verbose_name='关联的动态产品', db_constraint=False, null=True, blank=True)
wproduct = models.ForeignKey('wpm.wproduct', on_delete=models.CASCADE, verbose_name='关联的动态产品', db_constraint=False,
null=True, blank=True, related_name='iproduct_wproduct')
is_mtested = models.BooleanField('是否军检', default=False)
is_mtestok = models.BooleanField('是否军检合格', null=True, blank=True)
remark_mtest = models.TextField('军检备注', null=True, blank=True)
@ -103,7 +104,8 @@ class FIFOItemProduct(BaseModel):
出入库产品
"""
fifoitem = models.ForeignKey(FIFOItem, verbose_name='关联出入库具体产品', on_delete=models.CASCADE)
wproduct = models.ForeignKey('wpm.wproduct', on_delete=models.CASCADE, verbose_name='关联的动态产品', db_constraint=False, null=True, blank=True)
wproduct = models.ForeignKey('wpm.wproduct', on_delete=models.CASCADE, verbose_name='关联的动态产品', db_constraint=False, null=True, blank=True,
related_name='fifoitem_wproduct')
number = models.CharField('物品编号', max_length=50)
material = models.ForeignKey(Material, verbose_name='物料类型', on_delete=models.CASCADE)
iproduct = models.ForeignKey(IProduct, verbose_name='关联库存产品', null=True, blank=True, on_delete=models.SET_NULL)

View File

@ -74,7 +74,7 @@ class FIFOItemViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
perms_map = {'*': '*'}
queryset = FIFOItem.objects.select_related('material', 'fifo').all()
serializer_class = FIFOItemSerializer
filterset_fields = ['material', 'fifo']
filterset_fields = ['material', 'fifo', 'fifo__type', 'is_tested', 'is_testok']
search_fields = []
ordering_fields = ['create_time']
ordering = ['-create_time']
@ -99,13 +99,7 @@ class FIFOItemViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
obj = serializer.save(create_by = self.request.user)
tris = []
for m in record_data: # 保存记录详情
form_field = m['form_field']
m['field_name'] = form_field.field_name
m['field_key'] = form_field.field_key
m['field_type'] = form_field.field_type
m['field_value'] = m['field_value']
m['sort'] = form_field.sort
m['need_judge'] = form_field.need_judge
m['is_testok'] = m['is_testok'] if 'is_testok' in m else True
m['test_record'] = obj
tris.append(TestRecordItem(**m))

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2021-12-27 05:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pm', '0017_auto_20211213_1401'),
]
operations = [
migrations.AddField(
model_name='productionplan',
name='process_json',
field=models.JSONField(blank=True, default=list, null=True, verbose_name='按工序的统计数'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2021-12-28 06:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pm', '0018_productionplan_process_json'),
]
operations = [
migrations.AlterField(
model_name='productionplan',
name='process_json',
field=models.JSONField(blank=True, default=dict, null=True, verbose_name='按工序的统计数'),
),
]

View File

@ -0,0 +1,48 @@
# Generated by Django 3.2.9 on 2021-12-29 01:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pm', '0019_alter_productionplan_process_json'),
]
operations = [
migrations.AddField(
model_name='productionplan',
name='count_notok',
field=models.PositiveIntegerField(default=0, verbose_name='不合格数量'),
),
migrations.AddField(
model_name='subproductionplan',
name='main_count_notok',
field=models.PositiveIntegerField(default=0, verbose_name='不合格数量'),
),
migrations.AddField(
model_name='subproductionprogress',
name='count_notok',
field=models.PositiveIntegerField(default=0, verbose_name='不合格数量'),
),
migrations.AlterField(
model_name='subproductionprogress',
name='count',
field=models.PositiveIntegerField(verbose_name='应出入数'),
),
migrations.AlterField(
model_name='subproductionprogress',
name='count_ok',
field=models.PositiveIntegerField(default=0, verbose_name='合格数量'),
),
migrations.AlterField(
model_name='subproductionprogress',
name='count_pick',
field=models.PositiveIntegerField(default=0, verbose_name='实际领用数'),
),
migrations.AlterField(
model_name='subproductionprogress',
name='count_real',
field=models.PositiveIntegerField(default=0, verbose_name='实际消耗/产出数'),
),
]

View File

@ -27,8 +27,10 @@ class ProductionPlan(CommonAModel):
count = models.PositiveIntegerField('生产数量', default=1)
count_real = models.PositiveIntegerField('实际产出数', default=0)
count_ok = models.PositiveIntegerField('合格数', default=0)
count_notok = models.PositiveIntegerField('不合格数量', default=0)
start_date = models.DateField('计划开工日期')
end_date = models.DateField('计划完工日期')
process_json = models.JSONField('按工序的统计数', default=dict, null=True, blank=True)
is_planed = models.BooleanField('是否已排产', default=False)
class Meta:
verbose_name = '生产计划'
@ -66,6 +68,7 @@ class SubProductionPlan(CommonAModel):
main_count = models.PositiveIntegerField('应产出数', default=0)
main_count_real = models.PositiveIntegerField('实际产出数', default=0)
main_count_ok = models.PositiveIntegerField('合格数', default=0)
main_count_notok = models.PositiveIntegerField('不合格数量', default=0)
steps = models.JSONField('工艺步骤', default=list)
@ -87,7 +90,8 @@ class SubProductionProgress(BaseModel):
material = models.ForeignKey(Material, verbose_name='关联物料', on_delete=models.CASCADE)
is_main = models.BooleanField('是否主产出', default=False)
type = models.IntegerField('物料应用类型', default=SubprodctionMaterial.type_choices)
count = models.IntegerField('应出入数')
count_pick = models.IntegerField('实际领用数', default=0)
count_real = models.IntegerField('实际消耗/产出数', default=0)
count_ok = models.IntegerField('合格数量', default=0)
count = models.PositiveIntegerField('应出入数')
count_pick = models.PositiveIntegerField('实际领用数', default=0)
count_real = models.PositiveIntegerField('实际消耗/产出数', default=0)
count_ok = models.PositiveIntegerField('合格数量', default=0)
count_notok = models.PositiveIntegerField('不合格数量', default=0)

View File

@ -48,11 +48,7 @@ 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__'
class PickNeedSerializer(serializers.Serializer):
warehouse = serializers.IntegerField(label="仓库ID")
@ -64,3 +60,10 @@ class SubproductionPlanSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = SubProductionPlan
fields = ['id', 'number']
class SubProductionProgressSerializer(serializers.ModelSerializer):
material_ = MaterialSimpleSerializer(source='material', read_only=True)
subproduction_plan_ = SubproductionPlanSimpleSerializer(source='subproduction_plan', read_only=True)
class Meta:
model = SubProductionProgress
fields = '__all__'

View File

@ -0,0 +1,28 @@
from django.db.models.aggregates import Sum
from apps.pm.models import ProductionPlan, SubProductionPlan
class PmService:
@classmethod
def update_plan_process_json(cls, plan:ProductionPlan):
"""
更新计划按工序统计字段
"""
ret = {}
subplans = SubProductionPlan.objects.filter(production_plan=plan, is_deleted=False)
qs = subplans.values('process', 'process__name', 'process__number').annotate(count=Sum('main_count'),
count_real=Sum('main_count_real'), count_ok=Sum('main_count_ok'), count_notok=Sum('main_count_notok'))
qs_list = list(qs)
for i in qs_list:
ret[i['process__number']] = {
'count':i['count'],
'count_real':i['count_real'],
'count_ok':i['count_ok'],
'count_notok':i['count_notok'],
'rate': round((i['count_ok']/(i['count_ok']+i['count_notok']))*100,2) if (i['count_ok']+i['count_notok']) > 0 else 0
}
plan.process_json = ret
plan.save()

View File

@ -2,6 +2,7 @@ from django.db.models.signals import post_save
from django.dispatch import receiver
from apps.mtm.models import Material
from apps.pm.models import SubProductionPlan, SubProductionProgress
from apps.pm.services import PmService
@receiver(post_save, sender=SubProductionProgress)
def update_subplan_main(sender, instance, created, **kwargs):
@ -13,8 +14,10 @@ def update_subplan_main(sender, instance, created, **kwargs):
if created:
subplan.main_product = instance.material
subplan.main_count = instance.count
subplan.main_count_real = instance.count_real
subplan.main_count_ok = instance.count_ok
subplan.main_count_notok = instance.count_notok
if instance.count_ok >= instance.count and instance.count_ok > 0:
subplan.state = SubProductionPlan.SUBPLAN_STATE_DONE
elif instance.count_ok < instance.count and instance.count_ok > 0:
@ -25,7 +28,12 @@ def update_subplan_main(sender, instance, created, **kwargs):
plan = subplan.production_plan
plan.count_real = subplan.main_count_real
plan.count_ok = subplan.main_count_ok
plan.count_notok = subplan.main_count_notok
plan.save()
# 更新计划工序统计字段
PmService.update_plan_process_json(subplan.production_plan)

View File

@ -11,7 +11,7 @@ from apps.mtm.models import Step, SubProduction, SubprodctionMaterial, UsedStep
from apps.pm.filters import SubproductionProgressFilterSet
from apps.system.mixins import CreateUpdateModelAMixin
from apps.pm.serializers import GenSubPlanSerializer, PickNeedSerializer, PlanDestorySerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer, SubProductionProgressSerializer
from rest_framework.mixins import CreateModelMixin, ListModelMixin, UpdateModelMixin
from rest_framework.mixins import CreateModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin
from apps.pm.models import ProductionPlan, SubProductionProgress, SubProductionPlan
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from django.shortcuts import render
@ -21,6 +21,7 @@ from rest_framework.response import Response
from rest_framework.decorators import action
from django.db.models import F
from utils.tools import ranstr
from django.db import transaction
# Create your views here.
def updateOrderPlanedCount(order):
@ -34,7 +35,7 @@ def updateOrderPlanedCount(order):
order.planed_count = planed_count
order.save()
class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModelMixin, GenericViewSet):
class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModelMixin, RetrieveModelMixin, GenericViewSet):
"""
生产计划
"""
@ -77,6 +78,7 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel
return Response()
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=GenSubPlanSerializer)
@transaction.atomic
def gen_subplan(self, request, pk=None):
"""
生成子计划

View File

@ -14,6 +14,8 @@ from rest_framework.decorators import action
from django.db.models import F
from rest_framework.response import Response
from django.utils import timezone
from apps.wf.models import Workflow
# Create your views here.
class CustomerViewSet(CreateUpdateCustomMixin, ModelViewSet):
"""
@ -168,6 +170,10 @@ class SaleViewSet(CreateUpdateCustomMixin, ListModelMixin, RetrieveModelMixin, C
FIFOItemProduct.objects.bulk_create(ipxs)
# 更新成品库情况
ips.update(is_saled=True)
# 更新动态产品表情况
from apps.wpm.models import WProduct
WProduct.objects.filter(iproduct_wproduct__sale_iproduct__sale=obj).update(
act_state=WProduct.WPR_ACT_STATE_SELLED)
# 更新库存
update_inm(fifo)
# 变更审核状态

View File

@ -0,0 +1,38 @@
# Generated by Django 3.2.9 on 2021-12-23 02:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wf', '0019_auto_20211221_0923'),
]
operations = [
migrations.AddField(
model_name='customfield',
name='is_hidden',
field=models.BooleanField(default=False, help_text='可用于携带不需要用户查看的信息', verbose_name='是否隐藏'),
),
migrations.AlterField(
model_name='customfield',
name='field_choice',
field=models.JSONField(blank=True, default=list, help_text='选项值格式为list, 例["id":1, "name":"张三"]', verbose_name='选项值'),
),
migrations.AlterField(
model_name='customfield',
name='field_type',
field=models.CharField(choices=[('string', '字符串'), ('int', '整型'), ('float', '浮点'), ('boolean', '布尔'), ('date', '日期'), ('datetime', '日期时间'), ('radio', '单选'), ('checkbox', '多选'), ('select', '单选下拉'), ('selects', '多选下拉'), ('cascader', '单选级联'), ('cascaders', '多选级联'), ('select_dg', '弹框单选'), ('select_dgs', '弹框多选'), ('textarea', '文本域'), ('file', '附件')], help_text='5.字符串10.整形15.浮点型20.布尔25.日期30.日期时间35.单选框40.多选框45.下拉列表50.多选下拉列表55.文本域60.用户名, 70.多选的用户名, 80.附件(只保存路径,多个使用逗号隔开)', max_length=50, verbose_name='类型'),
),
migrations.AlterField(
model_name='customfield',
name='label',
field=models.CharField(default='', help_text='处理特殊逻辑使用', max_length=1000, verbose_name='标签'),
),
migrations.AlterField(
model_name='workflow',
name='title_template',
field=models.CharField(blank=True, default='{title}', help_text='工单字段的值可以作为参数写到模板中,格式如:你有一个待办工单:{title}', max_length=50, null=True, verbose_name='标题模板'),
),
]

View File

@ -0,0 +1,33 @@
# Generated by Django 3.2.9 on 2021-12-24 06:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wf', '0020_auto_20211223_1006'),
]
operations = [
migrations.AlterField(
model_name='customfield',
name='description',
field=models.CharField(blank=True, help_text='字段的描述信息,可用于显示在字段的下方对该字段的详细描述', max_length=100, null=True, verbose_name='描述'),
),
migrations.AlterField(
model_name='customfield',
name='field_template',
field=models.TextField(blank=True, help_text='文本域类型字段前端显示时可以将此内容作为字段的placeholder', null=True, verbose_name='文本域模板'),
),
migrations.AlterField(
model_name='customfield',
name='field_type',
field=models.CharField(choices=[('string', '字符串'), ('int', '整型'), ('float', '浮点'), ('boolean', '布尔'), ('date', '日期'), ('datetime', '日期时间'), ('radio', '单选'), ('checkbox', '多选'), ('select', '单选下拉'), ('selects', '多选下拉'), ('cascader', '单选级联'), ('cascaders', '多选级联'), ('select_dg', '弹框单选'), ('select_dgs', '弹框多选'), ('textarea', '文本域'), ('file', '附件')], help_text='string, int, float, date, datetime, radio, checkbox, select, selects, cascader, cascaders, cascader, cascaders,textarea, file', max_length=50, verbose_name='类型'),
),
migrations.AlterField(
model_name='customfield',
name='placeholder',
field=models.CharField(blank=True, help_text='用户工单详情表单中作为字段的占位符显示', max_length=100, null=True, verbose_name='占位符'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2021-12-27 01:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wf', '0021_auto_20211224_1426'),
]
operations = [
migrations.AlterField(
model_name='customfield',
name='is_hidden',
field=models.BooleanField(default=False, help_text='可用于携带不需要用户查看的字段信息', verbose_name='是否隐藏'),
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.9 on 2021-12-27 05:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wf', '0022_alter_customfield_is_hidden'),
]
operations = [
migrations.AlterField(
model_name='customfield',
name='field_type',
field=models.CharField(choices=[('string', '字符串'), ('int', '整型'), ('float', '浮点'), ('boolean', '布尔'), ('date', '日期'), ('datetime', '日期时间'), ('radio', '单选'), ('checkbox', '多选'), ('select', '单选下拉'), ('selects', '多选下拉'), ('cascader', '单选级联'), ('cascaders', '多选级联'), ('select_dg', '弹框单选'), ('select_dgs', '弹框多选'), ('textarea', '文本域'), ('file', '附件')], help_text='string, int, float, date, datetime, radio, checkbox, select, selects, cascader, cascaders, select_dg, select_dgs,textarea, file', max_length=50, verbose_name='类型'),
),
migrations.AlterField(
model_name='customfield',
name='label',
field=models.CharField(default='', help_text='处理特殊逻辑使用,比如sys_user用于获取用户作为选项', max_length=1000, verbose_name='标签'),
),
]

View File

@ -150,26 +150,32 @@ class CustomField(CommonAModel):
('checkbox', '多选'),
('select', '单选下拉'),
('selects', '多选下拉'),
('cascader', '单选级联'),
('cascaders', '多选级联'),
('select_dg', '弹框单选'),
('select_dgs', '弹框多选'),
('textarea', '文本域'),
('selectuser', '单选用户'),
('selectusers', '多选用户'),
('file', '附件'),
('draw', '绘图')
('file', '附件')
)
workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='所属工作流')
field_type = models.CharField('类型', max_length=50, choices=field_type_choices, help_text='5.字符串10.整形15.浮点型20.布尔25.日期30.日期时间35.单选框40.多选框45.下拉列表50.多选下拉列表55.文本域60.用户名, 70.多选的用户名, 80.附件(只保存路径,多个使用逗号隔开)')
field_type = models.CharField('类型', max_length=50, choices=field_type_choices,
help_text='string, int, float, date, datetime, radio, checkbox, select, selects, cascader, cascaders, select_dg, select_dgs,textarea, file')
field_key = models.CharField('字段标识', max_length=50, help_text='字段类型请尽量特殊,避免与系统中关键字冲突')
field_name = models.CharField('字段名称', max_length=50)
sort = models.IntegerField('排序', default=0, help_text='工单基础字段在表单中排序为:流水号0,标题20,状态id40,状态名41,创建人80,创建时间100,更新时间120.前端展示工单信息的表单可以根据这个id顺序排列')
default_value = models.CharField('默认值', null=True, blank=True, max_length=100, help_text='前端展示时,可以将此内容作为表单中的该字段的默认值')
description = models.CharField('描述', max_length=100, blank=True, default='', help_text='字段的描述信息,可用于显示在字段的下方对该字段的详细描述')
placeholder = models.CharField('占位符', max_length=100, blank=True, default='', help_text='用户工单详情表单中作为字段的占位符显示')
field_template = models.TextField('文本域模板', default='', blank=True, help_text='文本域类型字段前端显示时可以将此内容作为字段的placeholder')
description = models.CharField('描述', max_length=100, blank=True, null=True, help_text='字段的描述信息,可用于显示在字段的下方对该字段的详细描述')
placeholder = models.CharField('占位符', max_length=100, blank=True, null=True, help_text='用户工单详情表单中作为字段的占位符显示')
field_template = models.TextField('文本域模板', null=True, blank=True, help_text='文本域类型字段前端显示时可以将此内容作为字段的placeholder')
boolean_field_display = models.JSONField('布尔类型显示名', default=dict, blank=True,
help_text='当为布尔类型时候,可以支持自定义显示形式。{"1":"","0":""}或{"1":"需要","0":"不需要"},注意数字也需要引号')
field_choice = models.JSONField('radio、checkbox、select的选项', default=dict, blank=True,
help_text='radio,checkbox,select,multiselect类型可供选择的选项格式为json如:{"1":"中国", "2":"美国"},注意数字也需要引号')
label = models.JSONField('标签', blank=True, default=dict, help_text='自定义标签json格式调用方可根据标签自行处理特殊场景逻辑loonflow只保存文本内容')
field_choice = models.JSONField('选项值', default=list, blank=True,
help_text='选项值格式为list, 例["id":1, "name":"张三"]')
label = models.CharField('标签', max_length=1000, default='', help_text='处理特殊逻辑使用,比如sys_user用于获取用户作为选项')
# hook = models.CharField('hook', max_length=1000, default='', help_text='获取下拉选项用于动态选项值')
is_hidden = models.BooleanField('是否隐藏', default=False, help_text='可用于携带不需要用户查看的字段信息')
class Ticket(CommonBModel):
"""

View File

@ -38,12 +38,33 @@ class TransitionSerializer(serializers.ModelSerializer):
queryset = queryset.select_related('source_state','destination_state')
return queryset
class AllField(serializers.Field):
def to_representation(self, value):
return value
def to_internal_value(self, data):
return data
class FieldChoiceSerializer(serializers.Serializer):
id = AllField(label='ID')
name = serializers.CharField(label='名称')
class CustomFieldSerializer(serializers.ModelSerializer):
class Meta:
model = CustomField
fields = '__all__'
class CustomFieldCreateUpdateSerializer(serializers.ModelSerializer):
field_choice = FieldChoiceSerializer(label='选项列表', many=True, required=False)
class Meta:
model = CustomField
fields = ['workflow', 'field_type', 'field_key', 'field_name',
'sort', 'default_value', 'description', 'placeholder', 'field_template',
'boolean_field_display', 'field_choice', 'label', 'is_hidden']
class TicketSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = Ticket
@ -107,8 +128,29 @@ class TicketDetailSerializer(serializers.ModelSerializer):
key = i['field_key']
i['field_state'] = state_fields.get(key, 1)
i['field_value'] = ticket_data.get(key, None)
i['field_display'] = i['field_value'] # 该字段是用于查看详情直接展示
if i['field_value']:
if 'sys_user' in i['label']:
if isinstance(i['field_value'], list):
i['field_display'] = ','.join(list(User.objects.filter(id__in=i['field_value']).values_list('name', flat=True)))
else:
i['field_display'] = User.objects.get(id=i['field_value']).name
elif i['field_type'] in ['radio', 'select']:
for m in i['field_choice']:
if m['id'] == i['field_value']:
i['field_display'] = m['name']
elif i['field_type'] in ['checkbox', 'selects']:
d_list = []
for m in i['field_choice']:
if m['id'] in i['field_value']:
d_list.append(m['name'])
i['field_display'] = ','.join(d_list)
return all_fields_l
def filter_display(self, item, field_value):
if item['id'] == field_value:
return
class TicketFlowSerializer(serializers.ModelSerializer):
participant_ = UserSimpleSerializer(source='participant', read_only=True)
state_ = StateSimpleSerializer(source='state', read_only=True)

View File

@ -9,7 +9,7 @@ from django.core.exceptions import AppRegistryNotReady
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin
from apps.wf.serializers import CustomFieldSerializer, StateSerializer, TicketAddNodeEndSerializer, TicketAddNodeSerializer, TicketCloseSerializer, TicketCreateSerializer, TicketDestorySerializer, TicketFlowSerializer, TicketFlowSimpleSerializer, TicketHandleSerializer, TicketRetreatSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer, TicketListSerializer, TicketDetailSerializer
from apps.wf.serializers import CustomFieldCreateUpdateSerializer, CustomFieldSerializer, StateSerializer, TicketAddNodeEndSerializer, TicketAddNodeSerializer, TicketCloseSerializer, TicketCreateSerializer, TicketDestorySerializer, TicketFlowSerializer, TicketFlowSimpleSerializer, TicketHandleSerializer, TicketRetreatSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer, TicketListSerializer, TicketDetailSerializer
from django.shortcuts import get_object_or_404, render
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from rest_framework.decorators import action, api_view
@ -111,6 +111,11 @@ class CustomFieldViewSet(CreateModelMixin, UpdateModelMixin, RetrieveModelMixin,
filterset_fields = ['workflow', 'field_type']
ordering = ['sort']
def get_serializer_class(self):
if self.action in ['create', 'update']:
return CustomFieldCreateUpdateSerializer
return super().get_serializer_class()
class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin, ListModelMixin, RetrieveModelMixin, GenericViewSet):
perms_map = {'*':'*'}
queryset = Ticket.objects.all()
@ -133,7 +138,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
return super().get_serializer_class()
def filter_queryset(self, queryset):
if not self.request.query_params.get('category', None):
if not self.detail and not self.request.query_params.get('category', None):
raise APIException('请指定查询分类')
return super().filter_queryset(queryset)

View File

@ -25,6 +25,7 @@ class WMaterialFilterSet(filters.FilterSet):
class WProductFilterSet(filters.FilterSet):
tag = filters.CharFilter(method='filter_tag')
production_plan = filters.NumberFilter(field_name='subproduction_plan__production_plan')
class Meta:
model = WProduct
fields = ['step', 'subproduction_plan', 'material', 'step__process', 'act_state', 'material__type']

View File

@ -0,0 +1,28 @@
# Generated by Django 3.2.9 on 2021-12-27 01:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wpm', '0037_wproduct_scrap_reason'),
]
operations = [
migrations.AddField(
model_name='wproduct',
name='ng_sign',
field=models.PositiveSmallIntegerField(blank=True, choices=[(10, '返工'), (20, '返修'), (30, '报废'), (40, '让步接收'), (50, '偏离许可'), (60, '降级使用'), (70, '退回供方'), (80, '召回')], 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, '已报废'), (80, '已售出')], default=0, verbose_name='进行状态'),
),
migrations.AlterField(
model_name='wprouctticket',
name='decision',
field=models.PositiveSmallIntegerField(blank=True, choices=[(10, '返工'), (20, '返修'), (30, '报废'), (40, '让步接收'), (50, '偏离许可'), (60, '降级使用'), (70, '退回供方'), (80, '召回')], null=True, verbose_name='最终决定'),
),
]

View File

@ -0,0 +1,58 @@
# Generated by Django 3.2.9 on 2021-12-28 06:17
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 = [
('inm', '0024_auto_20211227_0948'),
('qm', '0022_auto_20211216_1401'),
('mtm', '0042_alter_recordformfield_field_type'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('pm', '0019_alter_productionplan_process_json'),
('wf', '0023_auto_20211227_1318'),
('wpm', '0038_auto_20211227_0948'),
]
operations = [
migrations.AlterField(
model_name='wprouctticket',
name='step',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.step', verbose_name='所在步骤/发现步骤'),
),
migrations.CreateModel(
name='WproductFlow',
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='删除标记')),
('number', models.CharField(blank=True, max_length=50, null=True, unique=True, verbose_name='物品编号')),
('act_state', models.IntegerField(choices=[(6, '待复检'), (8, '操作准备中'), (10, '操作进行中'), (20, '待检验'), (26, '待夹层检验'), (30, '已合格'), (40, '已入库'), (50, '不合格'), (60, '待成品检验'), (70, '已报废'), (80, '已售出')], default=0, verbose_name='进行状态')),
('is_hidden', models.BooleanField(default=False, verbose_name='是否隐藏')),
('remark', models.CharField(blank=True, max_length=200, null=True, verbose_name='备注')),
('scrap_reason', models.IntegerField(blank=True, choices=[(10, '气泡'), (20, '破点'), (30, '划伤'), (40, '其他')], null=True, verbose_name='报废原因')),
('ng_sign', models.PositiveSmallIntegerField(blank=True, choices=[(10, '返工'), (20, '返修'), (30, '报废'), (40, '让步接收'), (50, '偏离许可'), (60, '降级使用'), (70, '退回供方'), (80, '召回')], null=True, verbose_name='不合格标记')),
('is_lastlog', models.BooleanField(default=True, verbose_name='是否该子计划下的最后一条日志')),
('child', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='wpm.wproductflow')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproductflow_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('material', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='所属物料状态')),
('operation', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='wpm.operation', verbose_name='当前操作')),
('pre_step', models.ForeignKey(blank=True, help_text='已执行完的步骤', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='wl_pre_step', to='mtm.step', verbose_name='已执行到')),
('step', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='wl_step', to='mtm.step', verbose_name='所在步骤')),
('subproduction_plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pm.subproductionplan', verbose_name='当前子生产计划')),
('test', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='qm.testrecord', verbose_name='当前检验')),
('ticket', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='wf.ticket', verbose_name='当前工单')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproductflow_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
('warehouse', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='inm.warehouse', verbose_name='所在仓库')),
('wproduct', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='wpm.wproduct', verbose_name='关联产品')),
],
options={
'abstract': False,
},
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2021-12-28 07:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wpm', '0039_auto_20211228_1417'),
]
operations = [
migrations.AlterField(
model_name='wproductflow',
name='number',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='物品编号'),
),
]

View File

@ -36,6 +36,7 @@ class WProduct(CommonAModel):
WPR_ACT_STATE_NOTOK = 50
WPR_ACT_STATE_TOFINALTEST = 60
WPR_ACT_STATE_SCRAP = 70
WPR_ACT_STATE_SELLED = 80
act_state_choices=(
(WPR_ACT_STATE_TORETEST, '待复检'),
(WPR_ACT_STATE_DOWAIT, '操作准备中'),
@ -46,7 +47,8 @@ class WProduct(CommonAModel):
(WPR_ACT_STATE_INM, '已入库'),
(WPR_ACT_STATE_NOTOK, '不合格'),
(WPR_ACT_STATE_TOFINALTEST, '待成品检验'),
(WPR_ACT_STATE_SCRAP, '已报废')
(WPR_ACT_STATE_SCRAP, '已报废'),
(WPR_ACT_STATE_SELLED, '已售出'),
)
SCRAP_REASON_QIPAO = 10
SCRAP_REASON_PODIAN = 20
@ -58,6 +60,26 @@ class WProduct(CommonAModel):
(30, '划伤'),
(40, '其他')
)
NG_BACK_WORK = 10
NG_BACK_FIX = 20
NG_SCRAP = 30
NG_ACCEPT = 40
NG_PERMIT = 50
NG_DOWN = 60
NG_BACK_FROM = 70
NG_RECALL = 80
ng_choices = (
(NG_BACK_WORK, '返工'),
(NG_BACK_FIX, '返修'),
(NG_SCRAP, '报废'),
(NG_ACCEPT, '让步接收'),
(NG_PERMIT, '偏离许可'),
(NG_DOWN, '降级使用'),
(NG_BACK_FROM, '退回供方'),
(NG_RECALL, '召回')
)
number = models.CharField('物品编号', unique=True, null=True, blank=True, max_length=50)
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')
@ -69,6 +91,9 @@ class WProduct(CommonAModel):
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)
ng_sign = models.PositiveSmallIntegerField('不合格标记', choices=ng_choices, 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='当前操作',
on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_operation')
@ -89,14 +114,43 @@ class WprouctTicket(CommonAModel):
"""
玻璃审批工单
"""
number = models.CharField('物品编号', null=True, blank=True, max_length=50)
wproduct = models.ForeignKey(WProduct, verbose_name='关联产品', on_delete=models.CASCADE)
material = models.ForeignKey(Material, verbose_name='所在物料状态', on_delete=models.CASCADE)
step = models.ForeignKey(Step, verbose_name='所在步骤', on_delete=models.CASCADE)
step = models.ForeignKey(Step, verbose_name='所在步骤/发现步骤', on_delete=models.CASCADE)
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='所在子生产计划', on_delete=models.CASCADE)
ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单', on_delete=models.CASCADE, related_name='wt_ticket')
decision = models.CharField('最终决定', null=True, blank=True, max_length=100)
decision = models.PositiveSmallIntegerField('最终决定', choices=WProduct.ng_choices, null=True, blank=True)
class WproductFlow(CommonAModel):
"""
动态产品表日志
"""
wproduct = models.ForeignKey(WProduct, on_delete=models.CASCADE, verbose_name='关联产品', null=True, blank=True)
number = models.CharField('物品编号', null=True, blank=True, max_length=50)
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='wl_pre_step')
step = models.ForeignKey(Step, verbose_name='所在步骤', on_delete=models.CASCADE, null=True, blank=True, related_name='wl_step')
act_state = models.IntegerField('进行状态', default=0, choices=WProduct.act_state_choices)
is_hidden = models.BooleanField('是否隐藏', default=False)
child = models.ForeignKey('self', blank=True, null=True, on_delete=models.CASCADE)
remark = models.CharField('备注', max_length=200, null=True, blank=True)
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='当前子生产计划', on_delete=models.CASCADE)
scrap_reason = models.IntegerField('报废原因', choices= WProduct.scrap_reason_choices, null=True, blank=True)
ng_sign = models.PositiveSmallIntegerField('不合格标记', choices= WProduct.ng_choices, 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='当前操作',
on_delete=models.SET_NULL, null=True, blank=True)
test = models.ForeignKey('qm.testrecord', verbose_name='当前检验',
on_delete=models.SET_NULL, null=True, blank=True)
ticket = models.ForeignKey('wf.ticket', verbose_name='当前工单',
on_delete=models.SET_NULL, null=True, blank=True)
is_lastlog = models.BooleanField('是否该子计划下的最后一条日志', default=True)
class Pick(CommonADModel):
"""

View File

@ -382,6 +382,7 @@ class OperationMaterialCreate2Serailizer(serializers.ModelSerializer):
class OperationMaterialCreate2ListSerailizer(serializers.ListSerializer):
child=OperationMaterialCreate2Serailizer()
class OperationMaterialCreate3Serializer(serializers.ModelSerializer):
material = serializers.PrimaryKeyRelatedField(required=True, queryset=Material.objects.all())
class Meta:

View File

@ -1,9 +1,13 @@
from django.utils import timezone
from typing import List
from django.db.models.expressions import F
from apps.pm.models import SubProductionPlan, SubProductionProgress
from apps.mtm.models import Material, Step, SubprodctionMaterial
from apps.qm.models import TestRecord
from apps.system.models import User
from apps.wpm.models import WProduct
from apps.wf.models import State, TicketFlow, Transition
from apps.wpm.models import WProduct, WproductFlow, WprouctTicket
from utils.tools import ranstr
class WpmServies(object):
@ -46,21 +50,72 @@ class WpmServies(object):
if is_testok:
if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST: # 复检
wproduct.act_state = WProduct.WPR_ACT_STATE_DOWAIT
elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST and wproduct.material.type == Material.MA_TYPE_GOOD: # 成品检验
wproduct.act_state = WProduct.WPR_ACT_STATE_TOFINALTEST
else:
wproduct.act_state = WProduct.WPR_ACT_STATE_OK
if wproduct.number is None: # 产生半成品编号
wproduct.number = 'WP'+ranstr(7)
# 更新子计划状态
# 更新子计划主产品数
instance = SubProductionProgress.objects.get(subproduction_plan=wproduct.subproduction_plan,
is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT)
instance.count_ok = instance.count_ok + 1 # 这个地方可能会有问题
instance.save()
# 去除ng_sign
if wproduct.ng_sign:
wt = WprouctTicket.objects.order_by('id').last() #取最后的工单
if wt.step.process == test.step.process:
wproduct.ng_sign = None
ticket = wt.ticket
ticket_data = wt.ticket_data
ticket_data['retest_result'] = 1
ticket.update_by = user
ticket.save()
# 创建处理日志
TicketFlow.objects.create(ticket=ticket, state=ticket.state,
participant_type=State.PARTICIPANT_TYPE_PERSONAL,
intervene_type=0,
participant=user)
# 更新子计划相关进度
cls.update_subproduction_progress_main(sp=wproduct.subproduction_plan)
else:# 如果不合格
wproduct.act_state = WProduct.WPR_ACT_STATE_NOTOK
# 需要走不合格品审理的工单
# 如果已经是返工返修的产品
if wproduct.ng_sign:
wt = WprouctTicket.objects.order_by('id').last() #取最后的工单
if wt.step.process == test.step.process:
ticket = wt.ticket
ticket_data = wt.ticket_data
ticket_data['retest_result'] = 0
ticket.update_by = user
ticket.save()
# 创建处理日志
TicketFlow.objects.create(ticket=ticket, state=ticket.state,
participant_type=State.PARTICIPANT_TYPE_PERSONAL,
intervene_type=0,
participant=user)
wproduct.update_by = user
wproduct.update_time = timezone.now()
wproduct.test = None
wproduct.save()
wproduct.save()
@classmethod
def update_subproduction_progress_main(cls, sp:SubProductionPlan):
"""
根据产品变动日志更新生产进度
"""
objs = WproductFlow.objects.filter(subproduction_plan=sp, is_lastlog=True)
count_ok = objs.filter(act_state__in=[WProduct.WPR_ACT_STATE_INM,
WProduct.WPR_ACT_STATE_OK, WProduct.WPR_ACT_STATE_SELLED]).count()
count_notok = objs.filter(act_state__in=[WProduct.WPR_ACT_STATE_NOTOK, WProduct.WPR_ACT_STATE_SCRAP]).count()
count_real = objs.exclude(act_state__in=[WProduct.WPR_ACT_STATE_TORETEST,
WProduct.WPR_ACT_STATE_DOWAIT, WProduct.WPR_ACT_STATE_DOING]).count()
ins = SubProductionProgress.objects.filter(subproduction_plan=sp,
is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT).first()
if ins:
ins.count_ok = count_ok
ins.count_notok = count_notok
ins.count_real = count_real
ins.save()

View File

@ -1,9 +1,14 @@
from django.db.models.expressions import F
from django.db.models.signals import post_save
from apps.mtm.models import Step, SubprodctionMaterial
from apps.pm.models import SubProductionPlan, SubProductionProgress
from apps.qm.models import TestRecord
from apps.wf.models import Ticket
from django.dispatch import receiver
from apps.wpm.models import WProduct, WprouctTicket
from rest_framework import exceptions
from apps.wpm.models import WProduct, WproductFlow, WprouctTicket
from apps.wpm.models import OperationWproduct
from apps.wpm.services import WpmServies
@receiver(post_save, sender=Ticket)
@ -41,20 +46,53 @@ def handleTicket(sender, instance, created, **kwargs):
执行操作决定
"""
ticket_data = instance.ticket_data
wt = instance.wt_ticket
wt = WprouctTicket.objects.get(ticket=instance)
wp = wt.wproduct
decision = WProduct.NG_BACK_WORK
if 'decision_1' in ticket_data and ticket_data['decision_1']:
wt.decision = ticket_data['decision_1']
if ticket_data['decision_1'] in ['返工', '返修']:
pass
elif ticket_data['decision_1'] in ['让步接收']:
wp.act_state = WProduct.WPR_ACT_STATE_OK
decision = ticket_data['decision_1']
elif 'decision_2' in ticket_data and ticket_data['decision_2']:
wp.decision = ticket_data['decision_2']
if ticket_data['decision_2'] in ['返工', '返修']:
pass
elif ticket_data['decision_2'] in ['让步接收']:
wp.act_state = WProduct.WPR_ACT_STATE_OK
wt.save()
wp.save()
decision = ticket_data['decision_2']
wp.ng_sign = decision
if decision in [WProduct.NG_BACK_WORK, WProduct.NG_BACK_FIX]:
step = Step.objects.get(id=ticket_data['back_step'])
wp.step = step
# 找到当时所属的计划
sp = SubProductionPlan.objects.filter(ow_subplan__wproduct=wp,
ow_subplan__operation__is_submited=True, ow_subplan__operation__step=step).first()
if sp:
wp.subproduction_plan = sp
wt.save()
wp.ticket = None # 解除当前工单
wp.act_state = WProduct.WPR_ACT_STATE_DOWAIT
wp.save()
# 更新子计划合格进度
WpmServies.update_subproduction_progress_main(sp=sp)
else:
raise exceptions.APIException('返回步骤点错误')
elif decision in [WProduct.NG_ACCEPT, WProduct.NG_PERMIT]:
wp.act_state = WProduct.WPR_ACT_STATE_OK
wp.ng_sign = decision
wt.save()
wp.ticket = None # 解除当前工单
wp.save()
@receiver(post_save, sender=WProduct)
def update_wproduct_log(sender, instance, created, **kwargs):
"""
更新产品变动日志
"""
# update_fields = kwargs['update_fields']
WproductFlow.objects.filter(wproduct=instance, subproduction_plan=instance.subproduction_plan).update(is_lastlog=False)
ins = WproductFlow()
ins.wproduct = instance
for f in WproductFlow._meta.fields:
if f.name not in ['id', 'create_time', 'update_time', 'wproduct', 'is_lastlog']:
setattr(ins, f.name, getattr(instance, f.name, None))
ins.save()

View File

@ -1,3 +1,4 @@
from django.db.models.expressions import F
from django.shortcuts import render
from rest_framework.generics import CreateAPIView, GenericAPIView
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin
@ -179,14 +180,21 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
半成品
"""
perms_map={'*':'*'}
queryset = WProduct.objects.select_related('step', 'material', 'subproduction_plan').filter(is_hidden=False)
queryset = WProduct.objects.select_related('step', 'material', 'subproduction_plan')
serializer_class = WProductListSerializer
filterset_class = WProductFilterSet
search_fields = ['number']
ordering_fields = ['id']
ordering = ['id']
def get_queryset(self):
queryset = self.queryset
if self.request.query_params.get('tag', None) != 'show_hidden':
queryset = queryset.filter(is_hidden=False)
return queryset
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=WpmTestFormInitSerializer)
@transaction.atomic
def test_init(self, request, pk=None):
"""
检验记录创建及初始化
@ -225,6 +233,8 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
savedict['type'] = TestRecord.TEST_COMB
tr = TestRecord.objects.create(**savedict)
wproduct.test = tr
wproduct.update_by = request.user
wproduct.update_time = timezone.now()
wproduct.save()
# 创建检验条目
for i in RecordFormField.objects.filter(form=form, is_deleted=False):
@ -282,7 +292,12 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
FIFOItemProduct.objects.bulk_create(ips)
# 更新库存并修改半成品进行状态
update_inm(fifo)
wproducts.update(act_state=WProduct.WPR_ACT_STATE_INM, warehouse=warehouse, update_by=request.user, update_time=timezone.now())
for i in wproducts:
i.act_state = WProduct.WPR_ACT_STATE_INM
i.warehouse = warehouse
i.update_by = request.user
i.update_time = timezone.now()
i.save()
return Response()
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=WproductPutInSerializer)
@ -343,7 +358,8 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
vdata = serializer.validated_data
if obj.act_state == WProduct.WPR_ACT_STATE_NOTOK:
pass
elif obj.step.process.id == 1: # 如果是冷加工可直接报废
elif obj.step.process.id == 1 and \
obj.act_state in [WProduct.WPR_ACT_STATE_DOWAIT, WProduct.WPR_ACT_STATE_TOTEST]: # 如果是冷加工可直接报废
if vdata.get('scrap_reason', None):
obj.scrap_reason = vdata['scrap_reason']
else:
@ -352,6 +368,7 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
raise exceptions.APIException('该产品不可报废')
obj.act_state = WProduct.WPR_ACT_STATE_SCRAP
obj.update_by = request.user
obj.update_time = timezone.now()
obj.save()
return Response()
@ -369,12 +386,14 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
发起不合格审理单
"""
obj = self.get_object()
if obj.act_state != WProduct.WPR_ACT_STATE_NOTOK:
raise exceptions.APIException('非检验不合格产品不可发起不合格审理')
if obj.act_state != WProduct.WPR_ACT_STATE_NOTOK or obj.ng_sign is not None\
or obj.ticket is not None:
raise exceptions.APIException('该产品不可发起不合格审理')
workflow = Workflow.objects.filter(name='不合格品审理单', is_deleted=False).first()
if workflow:
exist_data = {
'wproduct':obj.id,
'wproduct_number':obj.number,
'wproduct_name':obj.material.name,
'wproduct_specification':obj.material.specification,
'finder':request.user.id,
@ -537,11 +556,10 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd
else:
wp.act_state = WProduct.WPR_ACT_STATE_TOTEST
wp.material = wsp.main_product
# 更新子计划进度
instance = SubProductionProgress.objects.get(subproduction_plan=wsp,
is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT)
instance.count_real = instance.count_real + 1 # 这个地方可能会有问题,不够严谨
instance.save()
# 更新子计划生产进度
# 如果产品有返工标记不做计算
if wp.ng_sign not in [WProduct.NG_BACK_FIX, WProduct.NG_BACK_WORK]:
WpmServies.update_subproduction_progress_main(sp=wsp)
wp.operation = None
wp.update_by = request.user
wp.save()
@ -554,7 +572,8 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd
act_state=WProduct.WPR_ACT_STATE_DOWAIT, remark='',
subproduction_plan=i.subproduction_plan)
for x in range(i.count):
WProduct.objects.create(**wpr)
WProduct.objects.create(**wpr)
WpmServies.update_subproduction_progress_main(sp=i.subproduction_plan)
elif step.type == Step.STEP_TYPE_COMB:
oms_w = OperationMaterial.objects.filter(operation=op, type=SubprodctionMaterial.SUB_MA_TYPE_OUT,
subproduction_progress__is_main=True)
@ -571,9 +590,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd
else:
wproduct.act_state = WProduct.WPR_ACT_STATE_TOTEST
# 更新子计划进度
instance = oms_w.subproduction_progress
instance.count_real = instance.count_real + 1 # 这个地方可能会有问题,不够严谨
instance.save()
WpmServies.update_subproduction_progress_main(sp=oms_w.subproduction_plan)
wproduct.create_by = request.user
wproduct.save()
# 隐藏原半成品

View File

@ -58,7 +58,8 @@ INSTALLED_APPS = [
'apps.qm',
'apps.pm',
'apps.wpm',
'apps.srm'
'apps.srm',
'apps.develop'
]
MIDDLEWARE = [

View File

@ -70,6 +70,7 @@ urlpatterns = [
path('api/pm/', include('apps.pm.urls')),
path('api/wpm/', include('apps.wpm.urls')),
path('api/srm/', include('apps.srm.urls')),
path('api/develop/', include('apps.develop.urls')),
# 工具
path('api/utils/signature/', GenSignature.as_view()),
path('api/utils/develop/', UpdateDevelop.as_view()),