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,8 +102,12 @@
:limit.sync="listQuery.page_size"
@pagination="getplanList"
/>
</el-card>
<el-card class="box-card">
<div style="height: 40px;line-height: 40px;background: #F5F7FA;padding-left: 20px;">甘特图</div>
<gantt
v-if="ganttShow"
style="position: relative"
v-if="proList.length>0"
:proList="proList"
></gantt>
</el-card>
@ -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,11 +1,10 @@
<template>
<div class="app-container">
<el-card>
<div style="margin-top: 2px">
<el-button type="primary" icon="el-icon-plus" @click="handleCreate"
>新增</el-button
>
>新增
</el-button>
</div>
</el-card>
<el-card style="margin-top: 2px">
@ -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>
@ -78,7 +79,7 @@
<el-form-item label="占位符" prop="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,29 +89,54 @@
</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-form-item label="顺序ID">
<el-input v-model="customfield.sort" type="number" placeholder="顺序"/>
</el-form-item>
<el-form-item label="默认值" prop="default_value">
<el-form-item label="默认值">
<el-input v-model="customfield.default_value" placeholder="默认值"/>
</el-form-item>
<el-form-item label="模板" prop="field_template">
<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">
<el-button type="danger" @click="dialogVisible = false">取消</el-button>
@ -140,10 +166,14 @@ export default {
placeholder: '',
field_type: '',
sort: '',
label: 'name',
default_value: '',
field_template: '',
field_choice:[]
field_choice: [],
is_hidden:false,
},
fieldHidden: true,
fieldBlock: false,
view_permission_check: false,
hasJsonFlag: true, // json是否验证通过
hasJsonFlag1: true, // json是否验证通过
@ -151,7 +181,17 @@ export default {
// customfieldList: {
// count:0
// },
customfieldList:[],
customfieldList: [''],
labels:[{
label: '常规',
value: 'name'
},{
label: '名称|id',
value: 'name|id'
},{
label: '人员选择',
value: 'user'
}],
options: [{
value: 'string',
label: '文本'
@ -177,19 +217,20 @@ export default {
value: 'textarea',
label: '文本域'
},{
value: 'file',
label: '附件'
}],
/*, {
value: 'selectuser',
label: '用户名'
}, {
value: 'selectusers',
label: '多选的用户名'
}, {
value: 'file',
label: '附件'
}],
}, */
boolean_field_display: [],
choiceOption:[''],
choiceOption: [{id:null,name:""}],
field_choice: [],
dialogVisible: false,
dialogType: "new",
@ -207,7 +248,7 @@ export default {
methods: {
//添加字段选项
addDomain() {
this.choiceOption.push('')
this.choiceOption.push({id:null,name:""})
},
//删除字段选项
removeDomain(index) {
@ -216,7 +257,6 @@ export default {
checkPermission,
getList() {
getWfCustomfieldList(this.ID).then((response) => {
if (response.data) {
this.customfieldList = response.data;
@ -224,8 +264,6 @@ export default {
});
},
handleFilter() {
this.listQuery.page = 1;
this.getList();
@ -241,22 +279,23 @@ export default {
this.customfield = Object.assign({}, defaultcustomfield);
this.dialogType = "new";
this.dialogVisible = true;
this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});
/*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.$nextTick(() => {
this.$refs["Form"].clearValidate();
});
});*/
},
handlecfgt(scope)
{
handlecfgt(scope) {
this.$router.push({name: "configuration", params: {customfield: scope.row.id}})
}
,
@ -267,7 +306,7 @@ export default {
type: "error",
})
.then(async () => {
await deleteWorkflow(scope.row.id);
await this.deleteWorkflow(scope.row.id);
this.getList();
this.$message.success("成功");
})
@ -277,14 +316,23 @@ export default {
},
async confirm(form) {
let that = this,choiceArr = [];
this.$refs[form].validate((valid) => {
if (valid) {
const isEdit = this.dialogType === "edit";
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) {
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();
@ -293,11 +341,7 @@ export default {
}
});
} 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();
@ -327,7 +371,7 @@ export default {
});
},
onJsonChange (value) {
/* onJsonChange(value) {
// console.log('更改value:', value);
// 实时保存
this.onJsonSave(value)
@ -345,10 +389,10 @@ export default {
// 检查json
checkJson() {
if (this.hasJsonFlag == false) {
alert("布尔显示定义json验证失败")
// alert("布尔显示定义json验证失败")
return false
} else {
alert("布尔显示定义json验证成功")
// alert("布尔显示定义json验证成功")
return true
}
},
@ -367,10 +411,10 @@ export default {
// 检查json
checkJson1() {
if (this.hasJsonFlag1 == false) {
alert("选项json验证失败")
// alert("选项json验证失败")
return false
} else {
alert("选项json1验证成功")
// alert("选项json1验证成功")
return true
}
},
@ -389,13 +433,13 @@ export default {
// 检查json
checkJson2() {
if (this.hasJsonFlag2 == false) {
alert("标签json验证失败")
// alert("标签json验证失败")
return false
} else {
alert("标签json1验证成功")
// alert("标签json1验证成功")
return true
}
},
}*/
// },
},

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,6 +289,11 @@
getTicketTransitions(ticketId).then(res=>{
this.operationBtn = res.data;
})
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){
//流程步骤数组
@ -296,13 +302,37 @@
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;
}
}
console.log(that.ticketDetail.state_.enable_retreat);
debugger;
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
}
}
}
}
}
that.sort = dat[0].sort;
that.actives = that.flowSteps.indexOf(dat[0]);
if( that.flowSteps.length-that.actives >1){}else{
@ -320,6 +350,7 @@
getWfStateList(workflow).then((response) => {
if (response.data) {
let nodes = response.data;
// 添加节点
nodes.forEach((item) => {
if(item.sort==that.sort){
@ -439,6 +470,9 @@
});
}
});
}
})
});
},
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>
</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-row>
</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=>{
that.retrialResponse = res.data;
getWorkflowInit(res.data.workflow).then((response) => {
if (response.data) {
debugger;
console.log(res)
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,6 +823,11 @@ 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,
@ -829,7 +837,7 @@ export default {
createRecord(this.formID, this.record).then((res) => {
if (res.code >= 200) {
this.getrecordLists();
this.$message.success("创建成功!");
this.$message.success("提交成功!");
this.dialogVisibleForm = false;
}

View File

@ -35,8 +35,19 @@
<el-link
v-if="checkPermission(['warehouse_update'])"
@click="handleInspection(scope)"
>检验</el-link
>检验
</el-link
>
<el-link
v-if="checkPermission(['warehouse_update'])&&scope.row.test===null"
@click="handleInspection(scope)"
>检验
</el-link>
<el-link
v-if="scope.row.test!==null"
@click="checkRecord(scope)"
>检验记录
</el-link>
</template>
</el-table-column>
</el-table>
@ -53,7 +64,8 @@
<el-tab-pane label="已合格成品">
<el-card style="margin-top: 2px">
<el-button type="primary" icon="el-icon-plus" @click="handleCreate"
>批量入库</el-button
>批量入库
</el-button
>
<el-table
v-loading="listLoading"
@ -92,7 +104,8 @@
<el-link
v-if="checkPermission(['warehouse_update'])"
@click="handlePutin(scope)"
>入库</el-link
>入库
</el-link
>
</template>
</el-table-column>
@ -107,8 +120,122 @@
</el-card>
</el-tab-pane>
</el-tabs>
<!--物料检查表&&-->
<el-dialog title="物料检查表" :close-on-click-modal="false" :visible.sync="outerVisible">
<el-select style="width: 100%" v-model="recordform" placeholder="请选择" @change="recordformChange">
<el-option
v-for="item in recordformList"
:key="item.name"
:label="item.name"
:value="item.id"
>
</el-option>
</el-select>
<!-- &lt;!&ndash;第一次操作时的展示&ndash;&gt;
<el-dialog
width="60%"
:title="formName"
:visible.sync="innerVisible"
append-to-body
>
<customForm
:results="fieldList"
:hasPicture="hasPicture"
:formID="recordform"
:wproduct="wproduct"
:recordId="recordId"
@recordSubmit="recordSubmit"
@recordSave="recordSave"
/>
</el-dialog>
&lt;!&ndash;复检检验表单&ndash;&gt;
<el-dialog
width="60%"
:title="formName"
:visible.sync="limitedReview"
append-to-body
>
<reviewForm
:results="fieldList"
:originList="originList"
:formID="recordform"
:hasPicture="hasPicture"
:wproduct="wproduct"
:origintest="origintest"
@formFunc="formFunc"
/>
</el-dialog>-->
<div slot="footer" class="dialog-footer">
<el-button @click="outerVisible = false">
</el-button>
<el-button type="primary" @click="submitrecordform()">填写检查项目</el-button>
</div>
</el-dialog>
<!--检查表显示-->
<el-dialog
width="60%"
:title="formName"
:visible.sync="innerVisible"
:close-on-click-modal="false"
@close="recordCancel"
>
<customForm
:results="fieldList"
:hasPicture="hasPicture"
:formID="recordform"
:wproduct="wproduct"
:recordId="recordId"
@recordSubmit="recordSubmit"
@recordSave="recordSave"
@recordCancel="recordCancel"
/>
</el-dialog>
<!--检验记录-->
<el-dialog title="检验记录" :close-on-click-modal="false" :visible.sync="limitedCheckRecord">
<el-table
:data="recordList"
border
height="400"
>
<el-table-column type="index" width="50"/>
<el-table-column label="表单名称">
<template slot-scope="scope">{{ scope.row.form_.name }}</template>
</el-table-column>
<el-table-column label="检查类型">
<template slot-scope="scope">{{ checkTypes[scope.row.type] }}</template>
</el-table-column>
<el-table-column label="是否提交">
<template slot-scope="scope">
<span v-if="scope.row.is_submited">已提交</span>
<span v-else>未提交</span>
</template>
</el-table-column>
<el-table-column align="center" label="操作">
<template slot-scope="scope">
<el-link
v-if="!scope.row.is_submited"
@click="handleInspectionRecord(scope)"
>检验
</el-link>
<el-link
v-else
@click="handleRecordDetail(scope)"
>查看
</el-link>
<el-link
@click="delTestRecord(scope)"
>删除
</el-link>
</template>
</el-table-column>
</el-table>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false"> </el-button>
<el-button type="primary" @click="putin"> </el-button>
</div>
</el-dialog>
<!--<el-dialog title="物料检查表" :close-on-click-modal="false" :visible.sync="outerVisible">
<el-select style="width: 100%" v-model="recordform" placeholder="请选择">
<el-option
v-for="item in recordformList"
@ -252,20 +379,22 @@
<div slot="footer" class="dialog-footer">
<el-button @click="innerVisible = false"> </el-button>
<el-button type="primary" @click="submitfield"
>提交检查项目</el-button
>提交检查项目
</el-button
>
</div>
</el-dialog>
<div slot="footer" class="dialog-footer">
<el-button @click="outerVisible = false"> </el-button>
<el-button type="primary" @click="submitrecordform"
>填写检查项目</el-button
>填写检查项目
</el-button
>
</div>
</el-dialog>
</el-dialog>-->
<el-dialog title="成品入库" :close-on-click-modal="false" :visible.sync="dialogFormVisible">
<el-form :model="form">
<el-form-item label="仓库" :label-width="formLabelWidth">
<el-form-item label="仓库">
<el-select
style="width: 100%"
v-model="form.warehouse"
@ -280,7 +409,7 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item label="备注" :label-width="formLabelWidth">
<el-form-item label="备注">
<el-input v-model="form.remark"></el-input>
</el-form-item>
@ -292,7 +421,7 @@
</el-dialog>
<el-dialog title="半成品批量入库" :close-on-click-modal="false" :visible.sync="dialogFormVisibles">
<el-form :model="form">
<el-form-item label="仓库" :label-width="formLabelWidth">
<el-form-item label="仓库">
<el-select
style="width: 100%"
v-model="form.warehouse"
@ -307,7 +436,7 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item label="备注" :label-width="formLabelWidth">
<el-form-item label="备注">
<el-input v-model="form.remark"></el-input>
</el-form-item>
@ -320,15 +449,18 @@
</div>
</template>
<script>
import { getwproductList ,wproductTest,wproductPutin,createputins} from "@/api/wpm";
import {testInit} from "@/api/wpm";
import checkPermission from "@/utils/permission";
import {getWarehouseList} from "@/api/inm";
import {getMaterialList, getrecordformList, getrffieldList} from "@/api/mtm";
import {genTree} from "@/utils";
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
import customForm from '@/components/customForm/index';
import {getwproductList, wproductTest, wproductPutin, createputins} from "@/api/wpm";
import {getTestRecord,getTestRecordItem,putTestRecordItem,delTestRecordItem,subTestRecordItem} from "@/api/qm";
const defaultetestitem = {};
export default {
components: { Pagination },
components: {Pagination,customForm},
data() {
return {
testitem: defaultetestitem,
@ -363,6 +495,13 @@ export default {
50: "不合格",
60: "待成品检验",
},
checkTypes:{
10:"子工序检验",
20:"工序检验",
30:"工序复检",
36:"夹层检验",
40:"成品检验",
},
choice: [
{
value: true,
@ -375,20 +514,26 @@ export default {
],
options: [],
listLoading: true,
fieldList: "",
wproduct: null,
recordId: null,
fieldList: null,
is_testok: "true",
field: [],
recordList: [],
recordformList: [],
recordform: "",
recordform: null,
fifo_detail: "",
listQueryrecordform: {
page: 0,
},
hasPicture: false,
outerVisible: false,
innerVisible: false,
dialogFormVisible: false,
dialogFormVisibles: false,
limitedCheckRecord: false,
testrecord: {},
formName: "检验表",
WarehouseData: "",
};
},
@ -398,7 +543,7 @@ export default {
this.getList();
this.getList1();
this.getLists();
// this.getLists();
},
methods: {
checkPermission,
@ -444,7 +589,11 @@ export default {
});
console.log(_this.mutipID);
createputins({warehouse:this.form.warehouse,wproducts: _this.mutipID,remark:this.form.remark}).then((res) => {
createputins({
warehouse: this.form.warehouse,
wproducts: _this.mutipID,
remark: this.form.remark
}).then((res) => {
if (res.code >= 200) {
this.$message.success("批量入库成功!");
this.dialogFormVisibles = false;
@ -461,31 +610,6 @@ export default {
}
});
},
handleInspection(scope) {
//调该物料对应的检查表
this.outerVisible = true;
this.wproduct=scope.row.id;//半成品ID
this.wproductactstate=scope.row.act_state;//半成品状态
this.listQueryrecordform.material = scope.row.material;//
this.listQueryrecordform.type = 2;
this.recordform="";
getrecordformList(this.listQueryrecordform).then((response) => {
if (response.data) {
this.recordformList = response.data;
}
});
},
//根据选择的表渲染检查项目
submitrecordform() {
if (this.recordform != "") {
getrffieldList({ form: this.recordform, page: 0 }).then((response) => {
if (response.data) {
this.fieldList = response.data;
this.innerVisible = true;
}
});
} else this.$message.error("请选择检查表!");
},
//提交检查项目
submitfield() {
@ -507,7 +631,6 @@ export default {
this.testrecord.wproduct = this.wproduct;//半成品ID
wproductTest(this.testrecord).then((res) => {
if (res.code >= 200) {
this.innerVisible = false;
@ -519,8 +642,6 @@ export default {
});
},
//半成品入库
handlePutin(scope) {
@ -536,7 +657,204 @@ export default {
this.getList1();
}
});
},
//点击table中的检验
handleInspection(scope) {
let that = this;
//调该物料对应的检查表
this.wproduct = scope.row.id;//成品ID
this.listQueryrecordform.material = scope.row.material;//
this.listQueryrecordform.type = 2;
this.recordform = null;
getrecordformList(this.listQueryrecordform).then((response) => {
if (response.data) {
this.recordformList = response.data;
if(response.data.length===1){
that.recordform = response.data[0].id;
that.formName = response.data[0].name;
that.submitrecordform();
}else{
//弹出列表选择框
this.outerVisible = true;
}
}
});
},
//根据选择的表渲染检查项目
submitrecordform() {
let that = this;
if (this.recordform != "") {
testInit({ wproduct: that.wproduct,form: that.recordform}).then((response) => {
if (response.data) {
that.hasPicture = false;
that.recordId = response.data.id;
getTestRecordItem(response.data.id).then((res) => {
that.formName = res.data.form_.name;
let fieldList = res.data.record_data;
that.fieldList = [...fieldList];
let arr = fieldList.filter(item => {
return item.field_type === 'draw'
});
if (arr.length > 0) {
that.hasPicture = true;
}
that.$nextTick(()=>{
that.innerVisible = true;
});
})
}
});
} else this.$message.error("请选择检查表!");
},
//选择物料检查表
recordformChange() {
let that = this;
let arr = this.recordformList.filter(item => {
return item.id === that.recordform;
});
that.formName = arr[0].name;
},
//更新检验记录列表
refreshRecord(){
let that = this;
getTestRecord({wproduct:that.wproduct,page:0}).then(res=>{
if(res.code==200){
that.recordList = res.data;
}else{
this.$message.error(res.msg);
}
})
},
//检验记录
checkRecord(scope){
let that = this;
that.wproduct = scope.row.id;//半成品ID
that.limitedCheckRecord = true;
getTestRecord({wproduct:scope.row.id,page:0}).then(res=>{
if(res.code==200){
that.recordList = res.data;
}else{
this.$message.error(res.msg);
}
})
},
//点击记录里的检验
handleInspectionRecord(scope){
let that =this;
that.innerVisible = false;
that.recordId = scope.row.id;
that.recordform = scope.row.form;
that.formName = scope.row.form_.name;
getrffieldList({form: this.recordform, page: 1, page_size: 100}).then((response) => {
if (response.data) {
that.hasPicture = false;
let fieldList = response.data.results;
that.fieldList = [...fieldList];
let arr = fieldList.filter(item => {
return item.field_type === 'draw'
});
if (arr.length > 0) {
that.hasPicture = true;
}
getTestRecordItem(scope.row.id).then((res) => {
let arr = [];
let fieldList = res.data.record_data;
for(let i=0;i<that.fieldList.length;i++){
let obj = that.fieldList[i];
obj.is_testok = null;
for(let j=0;j<fieldList.length;j++){
if(that.fieldList[i].field_key===fieldList[j].field_key){
obj.id = parseInt(fieldList[j].id);
obj.is_testok = fieldList[j].is_testok;
obj.field_value = fieldList[j].field_value;
}
}
arr.push(obj)
}
that.fieldList = arr;
that.$nextTick(()=>{
that.innerVisible = true;
});
})
}
});
},
delTestRecord(scope){
let that = this;
this.$confirm("确认删除?", "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
await delTestRecordItem(scope.row.id).then(res=>{
if(res.code>=200){
that.$message.success("成功");
that.refreshRecord();
this.getList();
this.getList1();
}else{
this.$message.error(res.msg);
}
});
})
.catch((err) => {
console.error(err);
});
},
//保存检查项目
recordSave(value) {
let that = this;
let id = value.id;
let params = {};
params.record_data = value.record_data;
params.is_testok = value.is_testok;
putTestRecordItem(id,params).then((res) => {
if (res.code >= 200) {
that.innerVisible = false;
that.limitedReview = false;
that.getList();
that.getList1();
that.refreshRecord();
}else{
this.$message.error(res.msg)
}
}) .catch((err) => {
console.error(err);
});
},
//记录提交检查项目
recordSubmit(value) {
let that = this;
let id = value.id;
let params = {};
params.record_data = value.record_data;
params.is_testok = value.is_testok;
putTestRecordItem(id,params).then((res) => {
if (res.code >= 200) {
subTestRecordItem(id,params).then((res) => {
if (res.code >= 200) {
that.innerVisible = false;
that.limitedReview = false;
that.limitedCheckRecord = false;
this.getList();
this.getList1();
that.refreshRecord();
}
});
}else{
this.$message.error(res.msg)
}
}) .catch((err) => {
console.error(err);
});
},
//第一次保存提交检查项目
recordCancel() {
this.outerVisible = false;
this.innerVisible = false;
},
},
};
</script>

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()
@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
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()
@ -555,6 +573,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd
subproduction_plan=i.subproduction_plan)
for x in range(i.count):
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()),