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

This commit is contained in:
shilixia 2021-12-16 16:28:19 +08:00
commit e4706e2635
40 changed files with 1457 additions and 1506 deletions

View File

@ -57,4 +57,41 @@ export function deleteTestitem(id, data) {
data
})
}
//检测记录
export function getTestRecord(query) {
return request({
url: '/qm/testrecord/',
method: 'get',
params: query
})
}
//检测记录
export function getTestRecordItem(id) {
return request({
url: `/qm/testrecord/${id}/`,
method: 'get'
})
}
//检测记录更改保存
export function putTestRecordItem(id,data) {
return request({
url: `/qm/testrecord/${id}/`,
method: 'put',
data
})
}
//检测记录删除
export function delTestRecordItem(id) {
return request({
url: `/qm/testrecord/${id}/`,
method: 'delete'
})
}
//检测记录提交
export function subTestRecordItem(id,data) {
return request({
url: `/qm/testrecord/${id}/submit/`,
method: 'post',
data
})
}

9
hb_client/src/api/srm.js Normal file
View File

@ -0,0 +1,9 @@
import request from '@/utils/request'
//生产进度表
export function getPlanGantt(data) {
return request({
url: '/srm/gantt/plan/',
method: 'get',
params: data
})
}

View File

@ -181,8 +181,8 @@ export function getrecordList(query) {
//操作提交自定义表
export function createRecord(id, data) {
return request({
url: `/wpm/operation_record/${id}/submit/`,
method: 'post',
url: `/wpm/operation_record/${id}/`,
method: 'put',
data
})
}

View File

@ -9,7 +9,7 @@
<el-row>
<el-col
v-for="(item, $index) in formData"
v-show="filterBlock(item.parent,item.display_expression)"
v-show="filterBlock(item.parent,item.display_expression,$index,item.field_key)"
:key="$index"
:span="12"
style="position: relative"
@ -123,7 +123,8 @@
<el-row v-show="hasPicture">
<el-form-item label="图表">
<div>
<img id="canvasImg" :src="img" style="width:500px;height: 300px;display: none">
<!--<img id="canvasImg" :src="img" style="width:500px;height: 300px;display: none">-->
<img id="canvasImg" src="./../../assets/glass.png" style="width:500px;height: 300px;display: none">
<div style="position: relative;display: flex;flex-direction: column;
border: 1px solid #DCDFE6;">
<canvas id="canvas" width="500" height="300">
@ -155,7 +156,8 @@
</el-form-item>
<div class="dialog-footer" style="text-align: right">
<el-button @click="innerVisible = false"> </el-button>
<el-button type="primary" @click="submitfield">提交检查项目</el-button>
<el-button type="primary" @click="submitfield('1')">保存</el-button>
<el-button type="primary" @click="submitfield('2')">提交</el-button>
</div>
</el-row>
@ -182,29 +184,49 @@
type:Number,
default:0
},
recordId: {
type:Number,
default:null
},
origin_test: {
type:Number,
default:null
},
hasPicture:{
type:Boolean,
default:false
},
recordItem:{
type:Boolean,
default:false
}
},
mounted() {
debugger;
let that = this;
that.checkForm = {};
this.form = this.formID;
let formData=this.results;
that.wproductId=this.wproduct;
formData.forEach(item => {
let obj = new Object();
obj = item;
obj.is_testok = null;//是否合格
that.formData.push(obj)
});
that.formData=[...that.formData];
that.formData=[...formData];
debugger;
for(let i=0;i<this.results.length;i++){
let key = this.results[i].field_key;
//checkForm接收表单数据的对象
let valu = this.results[i].field_value;
if(valu!==null&&valu!==''&&valu!==undefined){
that.checkForm[key]=valu;
that.$set(that.checkForm,key,valu);
that.is_save = true;
}else{
that.checkForm[key]='';
that.$set(that.checkForm,key,'')
that.$set(that.checkForm,key,null)
}
}
let listJudge = this.formData.filter(item => {
return item.need_judge === true;
@ -217,9 +239,9 @@
let imag= this.formData.filter(item => {
return item.field_type === 'draw';
});
that.img = new Image();
/* that.img = new Image();
that.img.crossOrigin = '';
that.img = 'http://47.95.0.242:2222'+imag[0].draw_template;
that.img = 'http://47.95.0.242:2222'+imag[0].draw_template;*/
setTimeout(function(){
that.canvasInit();
},500);
@ -252,11 +274,12 @@
judgeList:[],//判定数组
is_testok:true,
testokTrue:true,
is_save:false,
testokFalse:false,
}
},
methods:{
filterBlock(parent,rule){
filterBlock(parent,rule,index,field_key){
// debugger;
let that = this;
if(parent!==''&&parent!==null&&parent!==undefined){
@ -305,6 +328,12 @@
rea = true;
}
}
if(rea){
that.formData[index].is_hidden = false;
}else{
that.formData[index].is_hidden = true;
that.checkForm[field_key] = null;
}
return rea;
}else{
let temp =rule.match(reg)[0];
@ -312,13 +341,21 @@
let a = rule.replace(key, 'yy');
a = a.replace(key, 'yy');
let yy = "'"+that.checkForm[key]+"'";
if(eval(eval(a))){
that.formData[index].is_hidden = false;
}else{
that.formData[index].is_hidden = true;
that.checkForm[field_key] = null;
}
return eval(eval(a));
// return eval("'"+y+"'"+str);
}
}else{
that.formData[index].is_hidden = true;
that.checkForm[field_key] = null;
return false;
}
}else{
that.formData[index].is_hidden = false;
return true;
}
},
@ -638,7 +675,7 @@
}
},
//提交检查项目
submitfield() {
submitfield(isSubmit) {
let that = this;
let drawArr = that.formData.filter(item=>{
return item.field_type==='draw';
@ -657,29 +694,58 @@
if(res){
let key = drawArr[0].field_key;
that.imgUrl=res.data.file;
that.checkForm[key] = that.imgUrl;
that.fieldData();
that.checkForm[key] = res.data.file;
that.fieldData(isSubmit);
}
});
}else{
that.fieldData();
that.fieldData(isSubmit);
}
},
fieldData(){
fieldData(isSubmit){
let that = this;
that.field = []; //检查项目
let submit = isSubmit=='1'?false:true;
if(that.recordItem){//有记录
that.formData.forEach((item) => {
if(!item.is_hidden){
that.field.push({
id: item.id,
field_value: that.checkForm[item.field_key],
is_testok: item.is_testok,//单项检查结果
is_hidden: item.is_hidden
});
}
});
that.testrecord.record_data = that.field;//检查项列表
that.testrecord.is_testok = that.is_testok;//检查表检查结果
that.testrecord.id = that.recordId;//记录id
if(submit){//提交
this.$emit('recordSubmit',that.testrecord);
}else {//保存
this.$emit('recordSave',that.testrecord);
}
}else{//第一次操作时
that.formData.forEach((item) => {
if(!item.is_hidden){
that.field.push({
form_field: item.id,
field_value: that.checkForm[item.field_key],
is_testok: item.is_testok//单项检查结果
is_testok: item.is_testok,//单项检查结果
is_hidden: item.is_hidden
});
}
});
that.testrecord.form = that.formID;//检查表
if(that.origin_test!==null){
that.testrecord.origin_test = that.origin_test;
}
that.testrecord.record_data = that.field;//检查项列表
that.testrecord.is_testok = that.is_testok;//检查表检查结果
that.testrecord.form = that.formID;//检查表
that.testrecord.wproduct = that.wproductId;//半成品ID
this.$emit('formFunc',that.testrecord);
that.testrecord.is_submited = submit;//提交状态false保存
this.$emit('formFunc',that.testrecord);//第一次保存或提交
}
},
}
}

View File

@ -185,7 +185,7 @@ export const asyncRoutes = [
component: () => import('@/views/pm/gantt'),
meta: { title: '甘特图', perms: ['vendor_manage'] },
hidden: true
},
}
]
}
,

View File

@ -19,36 +19,13 @@
@expand-change="handlerExpand"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column
v-for="col in columns"
:prop="col.prop"
:key="col.prop"
:label="col.label"
:align="col.align"
:resizable="col.resizable"
>
<template slot-scope="scope">
<span v-if="col.prop == 'type'">{{
projectType[scope.row.type]
}}</span>
<span v-else>{{ scope.row[col.prop] }}</span>
</template>
<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>
</el-table>
<el-collapse-transition>
</el-collapse-transition>
<transition name="el-zoom-in-top">
<div class="menulist" v-show="menuOpen" ref="menulist">
<div
class="item"
v-for="(item, index) in menuLists"
@click="handleMenuClick(index, item)"
:key="item + index"
>
{{ item }}
</div>
</div>
</transition>
</div>
</template>
@ -83,70 +60,6 @@ export default {
checkList: [],
isShowHeaderBox: false,
menuOpen: false,
headerList: {
name: true,
ower: true,
per: true,
type: true
},
projectType: {
1: "计划项目",
2: "里程碑",
3: "分组"
},
menuLists: ["编辑", "里程碑", "分组", "删除"],
columns: [
{
prop: "name",
label: "名称",
width: "200px",
resizable: false
},
{
prop: "ower",
label: "负责人",
align: "center",
resizable: false
},
{
prop: "per",
label: "进度",
align: "center",
resizable: false
},
{
prop: "type",
label: "项目类型",
align: "center",
resizable: false
}
],
columnsCopy: [
{
prop: "name",
label: "名称",
width: "200px",
resizable: false
},
{
prop: "ower",
label: "负责人",
align: "center",
resizable: false
},
{
prop: "per",
label: "进度",
align: "center",
resizable: false
},
{
prop: "type",
label: "项目类型",
align: "center",
resizable: false
}
],
//当前点击的row
currentRow: {}
};
@ -166,123 +79,14 @@ export default {
// console.log(row, expanded);
this.$emit("handlerExpand", row, expand);
},
handlerSave() {
this.isShowHeaderBox = false;
let arr = [];
this.columnsCopy.forEach(item => {
if (this.headerList[item.prop] == true) {
arr.push(item);
}
});
this.columns = arr;
},
maskClick() {
this.isShowHeaderBox = false;
this.menuOpen = false;
this.currentRow = {};
},
handlerRowClick(row, column) {
// console.log(column);
if (row.type != 3) {
handlerRowClick(row) {
this.$emit("handlerRowClick", row);
}
},
handleRowMore(row, e) {
this.menuLists = ["编辑", "里程碑", "分组", "删除"];
if (row.parentId) {
this.menuLists = ["编辑", "里程碑", "删除"];
}
if (row.type == "1") {
this.menuLists[1] = "里程碑";
} else if (row.type == "2") {
this.menuLists[1] = "计划项目";
} else if (row.type == "3") {
this.menuLists = ["编辑", "添加任务", "删除分组"];
}
this.menuOpen = true;
this.currentRow = row;
let s = this.$refs.menulist.style;
s.top = e.y - 10 - 40 + "px";
s.left = e.x + 20 + "px";
},
handleShowHeaderCheckBox() {
this.isShowHeaderBox = !this.isShowHeaderBox;
},
handleMenuClick(index, name) {
this.menuOpen = false;
if (index == 3) {
this.$confirm("此操作将永久删除该项目, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
center: true
})
.then(() => {
this.$emit("handlerDel", this.currentRow);
})
.catch(() => {});
} else if (index == 1) {
if (this.currentRow.type == 3) {
console.log("添加任务");
this.$emit("handleGroupAdd", this.currentRow);
return;
}
this.$confirm(`确定转为${this.menuLists[1]}`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "info",
center: true
})
.then(() => {
if (this.currentRow.type == "1") {
this.$emit("milestone", this.currentRow);
} else if (this.currentRow.type == "2") {
this.$emit("planProject", this.currentRow);
}
})
.catch(() => {});
} else if (index == 0) {
this.$emit("handlerEdit", this.currentRow);
} else if (index == 2) {
console.log(name);
if (this.currentRow.type == 3 && name == "删除分组") {
this.$confirm("此操作将永久删除该分组及其子项目, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
center: true
})
.then(() => {
this.$emit("handlerDel", this.currentRow);
})
.catch(() => {});
return;
}
if (name == "删除") {
this.$confirm("此操作将永久删除该项目, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
center: true
})
.then(() => {
this.$emit("handlerDel", this.currentRow);
})
.catch(() => {});
return;
}
this.$confirm(`确定转为分组`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "info",
center: true
})
.then(() => {
this.$emit("handlerGroup", this.currentRow);
})
.catch(() => {});
}
}
},
mounted() {
this.handlerWatchScroll();

View File

@ -1,9 +1,22 @@
<template>
<div class="slider" ref="slider" :style="{ width: '100%' }">
<div class="process" :style="{ width }" style="font-size: 12px;color: #ffffff;text-align: right">
<div
class="slider"
ref="slider"
:style="{ width: '100%', display: inline-block}"
>
<div
class="process"
:style="{ width }"
style="font-size: 12px;color: #ffffff;text-align: right"
>
{{ per }}
</div>
<div class="process1" :style="{width:process1Width}" style="font-size: 12px;color: #ffffff;text-align: right"> {{ per1 }}</div>
<div class="process1"
:style="{width:process1Width}"
style="font-size: 12px;color: #ffffff;text-align: right"
>
{{ per1 }}
</div>
</div>
</template>
<script>
@ -92,21 +105,13 @@ export default {
};
</script>
<style>
.box {
margin: 100px auto 0;
width: 80%;
}
.clear:after {
content: "";
display: block;
clear: both;
}
.slider {
position: relative;
height: 20px;
background: #e4e7ed;
border-radius: 3px;
cursor: move;
cursor: text;
user-select: none;
}
.slider .process {
@ -127,16 +132,8 @@ export default {
border-radius: 3px;
background: #11c750;
}
.slider .thunk {
position: absolute;
left: 100px;
top: -7px;
width: 20px;
height: 20px;
}
.slider .block {
transition: 0.2s all;
}
.slider .block i {
font-size: 25px;
position: relative;
@ -144,20 +141,7 @@ export default {
top: 15px;
cursor: pointer;
}
.slider .tips {
position: absolute;
left: -3px;
bottom: 0px;
font-size: 12px;
line-height: 24px;
min-width: 15px;
text-align: center;
padding: 1px 5px;
background: #000;
border-radius: 5px;
height: 24px;
color: #fff;
}
.slider .tips i {
position: absolute;
margin-left: -5px;
@ -166,8 +150,5 @@ export default {
font-size: 16px;
color: #000;
}
.slider .block:hover {
transform: scale(1.1);
opacity: 0.6;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -34,10 +34,15 @@
<el-table-column align="center" label="操作" width="220px">
<template slot-scope="scope">
<el-link
v-if="checkPermission(['warehouse_update'])"
@click="handleInspection(scope)"
v-if="checkPermission(['warehouse_update'])&&scope.row.test===null"
@click="handleInspection(scope,'1')"
>检验
</el-link>
<el-link
v-if="scope.row.test!==null"
@click="checkRecord(scope)"
>检验记录
</el-link>
</template>
</el-table-column>
</el-table>
@ -81,7 +86,7 @@
<template slot-scope="scope">
<el-link
v-if="checkPermission(['warehouse_update'])"
@click="handleReview(scope)"
@click="handleInspection(scope,'2')"
>
检验
</el-link>
@ -204,7 +209,7 @@
</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
@ -215,6 +220,7 @@
>
</el-option>
</el-select>
<!--第一次操作时的展示-->
<el-dialog
width="60%"
:title="formName"
@ -226,6 +232,25 @@
:hasPicture="hasPicture"
:formID="recordform"
:wproduct="wproduct"
:origin_test="origin_test"
@formFunc="formFunc"
/>
</el-dialog>
<!--复检检验表单-->
<el-dialog
width="60%"
:title="formName"
:visible.sync="limitedReview"
append-to-body
>
<reviewForm
:results="fieldList"
:originList="originList"
:formID="recordform"
:hasPicture="hasPicture"
:recordItem="recordItem"
:wproduct="wproduct"
:origintest="origintest"
@formFunc="formFunc"
/>
</el-dialog>
@ -233,24 +258,29 @@
<el-button @click="outerVisible = false">
</el-button>
<el-button type="primary" @click="submitrecordform">填写检查项目</el-button>
<el-button type="primary" @click="submitrecordform(innerIndex)">填写检查项目</el-button>
</div>
</el-dialog>
<!--保存过的检查表显示-->
<el-dialog
width="60%"
:title="formName"
:visible.sync="limitedReview"
:visible.sync="recordVisible"
>
<reviewForm
<customForm
v-if="recordVisible"
:results="fieldList"
:originList="originList"
:formID="recordform"
:hasPicture="hasPicture"
:formID="recordform"
:wproduct="wproduct"
:origintest="origintest"
@formFunc="formFunc"
:recordId="recordId"
:recordItem="recordItem"
@recordSubmit="recordSubmit"
@recordSave="recordSave"
/>
</el-dialog>
<!--半成品入库-->
<el-dialog title="半成品入库" :close-on-click-modal="false" :visible.sync="dialogFormVisible">
<el-form :model="form">
<el-form-item label="仓库" :label-width="formLabelWidth">
@ -278,6 +308,53 @@
<el-button type="primary" @click="putin"> </el-button>
</div>
</el-dialog>
<!--检验记录-->
<el-dialog title="检验记录" :close-on-click-modal="false" :visible.sync="limitedCheckRecord">
<el-table
:data="recordList"
border
height="400"
@close="pageRefresh"
@closed="pageRefresh"
>
<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="dialogFormVisibles">
<el-form :model="form">
<el-form-item label="仓库" :label-width="formLabelWidth">
@ -314,10 +391,12 @@
import checkPermission from "@/utils/permission";
import {getWarehouseList} from "@/api/inm";
import {getMaterialList, getrecordformList, getrffieldList} from "@/api/mtm";
import {getTestRecord,getTestRecordItem,putTestRecordItem,delTestRecordItem,subTestRecordItem} from "@/api/qm";
import {genTree} from "@/utils";
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
const defaultetestitem = {};
export default {
inject:['reload'],
components: {Pagination, customForm,reviewForm},
data() {
return {
@ -363,6 +442,13 @@
60: "待成品检验",
26:"待夹层检验",
},
checkTypes:{
10:"子工序检验",
20:"工序检验",
30:"工序复检",
36:"夹层检验",
40:"成品检验",
},
choice: [
{
value: true,
@ -373,6 +459,7 @@
label: "不合格",
},
],
recordList: [],
options: [],
listLoading: true,
fieldList: [],
@ -381,19 +468,26 @@
field: [],
recordformList: [],
recordform: null,
recordId: null,
fifo_detail: "",
listQueryrecordform: {
page: 0,
},
recordVisible:false,
innerIndex:null,
origintest:null,
mutipID: [],
wproduct: null,
isPost: false,
origin_test: null,
hasPicture: false,
recordItem: false,
outerVisible: false,
innerVisible: false,
limitedReview: false,
dialogFormVisible: false,
dialogFormVisibles: false,
limitedCheckRecord: false,
testrecord: {},
WarehouseData: "",
formName: '项目检查表',
@ -423,18 +517,15 @@
},
//复检半成品列表
getList2() {
this.listQuery2.act_state = 6;
getwproductList(this.listQuery2).then((response) => {
if (response.data) {
this.wproductList2 = response.data;
}
});
},
//已合格半成品
getList1() {
this.listQuery1.act_state = 30;
this.listQuery1.material__type = 2;
getwproductList(this.listQuery1).then((response) => {
@ -446,7 +537,6 @@
},
//夹层半成品列表
getList3() {
this.listQuery3.act_state = 26;
getwproductList(this.listQuery3).then((response) => {
if (response.data) {
@ -492,56 +582,48 @@
}
});
},
handleInspection(scope) {
//点击检验:如果有一个直接进入如果有多个表再进行选择
handleInspection(scope,index) {
//调该物料对应的检查表
// this.step = step;
let that = this;
this.innerIndex = index;
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 = "";
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(index);
}
}
});
},
//半产品复检
handleReview(scope) {
//验记录
checkRecord(scope){
let that = this;
this.wproduct = scope.row.id;//半成品ID
wproductReview({ wproduct: this.wproduct}).then((response) => {
if (response.data) {
that.hasPicture = false;
that.formName = response.data.name;
let fieldList = response.data.form_fields;
that.fieldList = [...fieldList];
that.origintest = response.data.origin_test.id;
that.recordform = response.data.origin_test.form;
let originList = response.data.origin_test.record_data_;
that.originList = [...originList];
let arr = fieldList.filter(item => {
return item.field_type === 'draw'
});
if (arr.length > 0) {
that.hasPicture = true;
that.wproduct = scope.row.id;//半成品ID
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);
}
this.limitedReview = true;
}
});
})
},
recordformChange() {
//点击记录里的检验
handleInspectionRecord(scope){
let that =this;
let arr = this.recordformList.filter(item => {
return item.id === that.recordform;
});
that.formName = arr[0].name;
},
//根据选择的表渲染检查项目
submitrecordform() {
let that = this;
if (that.recordform != "") {
that.recordVisible = false;
that.recordId = scope.row.id;
that.recordform = scope.row.form;
getrffieldList({form: this.recordform, page: 1, page_size: 100}).then((response) => {
if (response.data) {
that.hasPicture = false;
@ -553,39 +635,105 @@
if (arr.length > 0) {
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++){
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 = fieldList[j].id;
obj.is_testok = fieldList[j].is_testok;
obj.field_value = fieldList[j].field_value;
}
}
arr.push(obj)
}
that.fieldList = arr;
that.recordItem = true;
that.$nextTick(()=>{
that.recordVisible = true;
});
})
}
});
},
//点击记录里的查看
handleRecordDetail(scope){
},
//半产品复检
handleReview() {
let that = this;
wproductReview({ wproduct: this.wproduct,form: that.recordform}).then((response) => {
if (response.data) {
that.hasPicture = false;
that.formName = response.data.name;
let fieldList = response.data.form_fields;
that.fieldList = [...fieldList];
that.origintest = response.data.origin_test;
that.recordform = response.data.origin_test_.form;
let originList = response.data.origin_test_.record_data;
that.originList = [...originList];
let arr = fieldList.filter(item => {
return item.field_type === 'draw'
});
if (arr.length > 0) {
that.hasPicture = true;
}
this.limitedReview = true;
}
});
},
//选择物料检查表
recordformChange() {
let that = this;
let arr = this.recordformList.filter(item => {
return item.id === that.recordform;
});
that.formName = arr[0].name;
},
//根据选择的表渲染检查项目
submitrecordform(index) {
let that = this;
if (that.recordform != "") {
if(index==='1'){
wproductReview({ wproduct: that.wproduct,form: that.recordform}).then((response) => {
if (response.data) {
debugger;
that.hasPicture = false;
that.origin_test = response.data.origin_test;
that.formName = response.data.name;
/* let fieldList = response.data.form_fields;
that.fieldList = [...fieldList];*/
let list = [];
let fieldList = response.data.form_fields;
for(let i=0;i<fieldList.length;i++){
let obj = fieldList[i];
obj.is_testok = true;
obj.field_value = null;
list.push(obj)
}
that.fieldList = list;
let arr = fieldList.filter(item => {
return item.field_type === 'draw'
});
if (arr.length > 0) {
that.hasPicture = true;
}
that.recordItem =false;
that.innerVisible = true;
}
});
}else if(index==='2'){
that.handleReview();
}
} else this.$message.error("请选择检查表!");
},
/* submitfield() {
let _this = this;
_this.field = []; //检查项目
this.fieldList.forEach((item) => {
_this.field.push({
form_field: item.id,
field_value: item.sort,
is_testok: item.is_testok//单项检查结果
});
});
this.testrecord.form = this.recordform;//检查表
this.testrecord.record_data = _this.field;//检查项列表
this.testrecord.is_testok = this.is_testok;//检查表检查结果
this.testrecord.wproduct = this.wproduct;//半成品ID
wproductTest(this.testrecord).then((res) => {
if (res.code >= 200) {
this.innerVisible = false;
this.outerVisible = false;
this.getList();
this.getList1();
this.getList2();
this.getList3();
}
});
},*/
//半成品入库
handlePutin(scope) {
this.dialogFormVisible = true;
@ -601,17 +749,101 @@
}
});
},
//提交检查项目
formFunc(value) {
wproductTest(value).then((res) => {
delTestRecord(scope){
let that = this;
this.$confirm("确认删除?", "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
await delTestRecordItem(scope.row.id).then(res=>{
if(res.code>=200){
this.innerVisible = false;
this.outerVisible = false;
this.getList();
this.getList1();
this.getList2();
that.$message.success("成功");
that.refreshRecord();
}else{
this.$message.error(res.msg);
}
});
})
.catch((err) => {
console.error(err);
});
},
//更新检验记录列表
refreshRecord(){
let that = this;
getTestRecord({wproduct:that.wproduct}).then(res=>{
if(res.code==200){
that.recordList = res.data.results;
}else{
this.$message.error(res.msg);
}
})
},
//记录保存检查项目
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.recordVisible = false;
that.reload();
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.recordVisible = false;
that.reload();
that.refreshRecord();
}
});
}else{
this.$message.error(res.msg)
}
}) .catch((err) => {
console.error(err);
});
},
//第一次保存提交检查项目
formFunc(value) {
let that = this;
if(that.isPost){
return;
}else{
that.isPost = true;
wproductTest(value).then((res) => {
that.isPost = false;
if (res.code >= 200) {
that.innerVisible = false;
that.outerVisible = false;
that.reload()
}
}).catch(()=>{
this.isPost = true;
});
}
},
pageRefresh(){
this.reload()
},
},
}

View File

@ -516,7 +516,6 @@
.box-card {
height: 300px;
}
</style>
</style>
<script>
import {
@ -783,7 +782,7 @@ export default {
recordconfirm() {
let _this = this;
_this.record_data = []; //检查项目
this.fieldList.forEach((item) => {
this.fieldList.form_fields.forEach((item) => {
_this.record_data.push({
form_field: item.id,
field_value: item.sort,

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.9 on 2021-12-16 01:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inm', '0022_auto_20211208_1408'),
]
operations = [
migrations.AlterField(
model_name='fifoitem',
name='is_tested',
field=models.BooleanField(default=False, verbose_name='是否已检验'),
),
migrations.AlterField(
model_name='fifoitem',
name='is_testok',
field=models.BooleanField(default=False, verbose_name='是否检验合格'),
),
]

View File

@ -75,8 +75,8 @@ class FIFOItem(BaseModel):
"""
出入库详细条目
"""
is_tested = models.BooleanField('是否已检', default=False)
is_testok = models.BooleanField('是否检合格', default=False)
is_tested = models.BooleanField('是否已检', default=False)
is_testok = models.BooleanField('是否检合格', default=False)
warehouse = models.ForeignKey(WareHouse, on_delete=models.CASCADE, verbose_name='仓库')
material = models.ForeignKey(Material, verbose_name='物料类型', on_delete=models.CASCADE)
count = models.PositiveIntegerField('数量', default=0)

View File

@ -39,7 +39,7 @@ class InventoryViewSet(ListModelMixin, GenericViewSet):
仓库物料表
"""
perms_map = {'*': '*'}
queryset = Inventory.objects.select_related('material', 'warehouse').all()
queryset = Inventory.objects.select_related('material', 'warehouse').filter(count__gt=0).all()
serializer_class = InventorySerializer
filterset_fields = ['material', 'warehouse']
search_fields = []
@ -48,7 +48,7 @@ class InventoryViewSet(ListModelMixin, GenericViewSet):
class MaterialBatchViewSet(ListModelMixin, GenericViewSet):
perms_map = {'*': '*'}
queryset = MaterialBatch.objects.select_related('material', 'warehouse').all()
queryset = MaterialBatch.objects.select_related('material', 'warehouse').filter(count__gt=0).all()
serializer_class = MaterialBatchSerializer
# filterset_fields = ['material', 'warehouse']
filterset_class = MbFilterSet
@ -87,14 +87,14 @@ class FIFOItemViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=InmTestRecordCreateSerializer)
def test(self, request, pk=None):
"""
"""
serializer = InmTestRecordCreateSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
vdata = serializer.validated_data
record_data = vdata.pop('record_data')
if 'is_testok' not in vdata:
raise APIException('未填写检结论')
raise APIException('未填写检结论')
with transaction.atomic():
obj = serializer.save(create_by = self.request.user)
tris = []
@ -111,7 +111,7 @@ class FIFOItemViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
tris.append(TestRecordItem(**m))
TestRecordItem.objects.bulk_create(tris)
# 如果检合格
# 如果检合格
if obj.fifo_item:
obj.fifo_item.is_testok = True if obj.is_testok else False
obj.fifo_item.is_tested = True

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2021-12-14 07:04
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mtm', '0041_alter_material_type'),
]
operations = [
migrations.AlterField(
model_name='recordformfield',
name='field_type',
field=models.CharField(choices=[('string', '字符串'), ('int', '整型'), ('float', '浮点'), ('boolean', '布尔'), ('date', '日期'), ('time', '时间'), ('datetime', '日期时间'), ('radio', '单选'), ('checkbox', '多选'), ('select', '单选下拉'), ('selects', '多选下拉'), ('textarea', '文本域'), ('table', '表格'), ('draw', '绘图')], max_length=50, verbose_name='类型'),
),
]

View File

@ -143,6 +143,7 @@ class RecordFormField(CommonAModel):
FIELD_SELECTS = 'selects'
FIELD_TEXTAREA = 'textarea'
FIELD_DRAW = 'draw'
FIELD_TABLE = 'table'
FIELD_FROMSYSTEM = 'fromsystem'
field_type_choices = (
('string', '字符串'),
@ -157,6 +158,7 @@ class RecordFormField(CommonAModel):
('select', '单选下拉'),
('selects', '多选下拉'),
('textarea', '文本域'),
('table', '表格'),
('draw', '绘图'),
)
high_rule_choices = (

View File

@ -0,0 +1,49 @@
# Generated by Django 3.2.9 on 2021-12-13 06:01
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('pm', '0016_auto_20211209_1638'),
]
operations = [
migrations.AlterField(
model_name='productionplan',
name='count',
field=models.PositiveIntegerField(default=1, verbose_name='生产数量'),
),
migrations.AlterField(
model_name='productionplan',
name='count_ok',
field=models.PositiveIntegerField(default=0, verbose_name='合格数'),
),
migrations.AlterField(
model_name='productionplan',
name='count_real',
field=models.PositiveIntegerField(default=0, verbose_name='实际产出数'),
),
migrations.AlterField(
model_name='subproductionplan',
name='main_count',
field=models.PositiveIntegerField(default=0, verbose_name='应产出数'),
),
migrations.AlterField(
model_name='subproductionplan',
name='main_count_ok',
field=models.PositiveIntegerField(default=0, verbose_name='合格数'),
),
migrations.AlterField(
model_name='subproductionplan',
name='main_count_real',
field=models.PositiveIntegerField(default=0, verbose_name='实际产出数'),
),
migrations.AlterField(
model_name='subproductionplan',
name='production_plan',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='subplan_plan', to='pm.productionplan', verbose_name='关联主生产计划'),
),
]

View File

@ -24,9 +24,9 @@ class ProductionPlan(CommonAModel):
number = models.CharField('编号', max_length=50, unique=True)
order = models.ForeignKey(Order, verbose_name='关联订单', null=True, blank=True, on_delete=models.SET_NULL)
product = models.ForeignKey(Material, verbose_name='生产产品', on_delete=models.CASCADE)
count = models.IntegerField('生产数量', default=1)
count_real = models.IntegerField('实际产出数', default=0)
count_ok = models.IntegerField('合格数', default=0)
count = models.PositiveIntegerField('生产数量', default=1)
count_real = models.PositiveIntegerField('实际产出数', default=0)
count_ok = models.PositiveIntegerField('合格数', default=0)
start_date = models.DateField('计划开工日期')
end_date = models.DateField('计划完工日期')
is_planed = models.BooleanField('是否已排产', default=False)
@ -63,9 +63,9 @@ class SubProductionPlan(CommonAModel):
process = models.ForeignKey(Process, verbose_name='关联大工序', on_delete=models.CASCADE)
main_product = models.ForeignKey(Material, verbose_name='主要产品', on_delete=models.CASCADE, null=True, blank=True)
main_count = models.IntegerField('应产出数', default=0)
main_count_real = models.IntegerField('实际产出数', default=0)
main_count_ok = models.IntegerField('合格数', default=0)
main_count = models.PositiveIntegerField('应产出数', default=0)
main_count_real = models.PositiveIntegerField('实际产出数', default=0)
main_count_ok = models.PositiveIntegerField('合格数', default=0)
steps = models.JSONField('工艺步骤', default=list)

View File

@ -23,8 +23,8 @@ def update_subplan_main(sender, instance, created, **kwargs):
if subplan.main_product.type == Material.MA_TYPE_GOOD:
# 如果是产品,更新主计划进度
plan = subplan.production_plan
plan.count_real = subplan.count_real
plan.count_ok = subplan.count_ok
plan.count_real = subplan.main_count_real
plan.count_ok = subplan.main_count_ok
plan.save()

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.9 on 2021-12-13 06:01
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('wpm', '0031_auto_20211209_1638'),
('qm', '0016_auto_20211210_1338'),
]
operations = [
migrations.AlterField(
model_name='testrecord',
name='wproduct',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='test_wproduct', to='wpm.wproduct', verbose_name='关联的动态产品'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2021-12-14 04:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('qm', '0017_alter_testrecord_wproduct'),
]
operations = [
migrations.AddField(
model_name='testrecord',
name='is_submited',
field=models.BooleanField(default=False, verbose_name='是否提交'),
),
]

View File

@ -0,0 +1,26 @@
# Generated by Django 3.2.9 on 2021-12-14 07:04
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('qm', '0018_testrecord_is_submited'),
]
operations = [
migrations.RemoveField(
model_name='testrecorditem',
name='need_judge',
),
migrations.RemoveField(
model_name='testrecorditem',
name='sort',
),
migrations.AddField(
model_name='testrecorditem',
name='is_hidden',
field=models.BooleanField(default=False, verbose_name='是否隐藏'),
),
]

View File

@ -0,0 +1,37 @@
# Generated by Django 3.2.9 on 2021-12-16 01:45
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('mtm', '0042_alter_recordformfield_field_type'),
('qm', '0019_auto_20211214_1504'),
]
operations = [
migrations.RemoveField(
model_name='testrecorditem',
name='field_key',
),
migrations.RemoveField(
model_name='testrecorditem',
name='field_name',
),
migrations.RemoveField(
model_name='testrecorditem',
name='field_type',
),
migrations.AlterField(
model_name='testrecorditem',
name='form_field',
field=models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.CASCADE, to='mtm.recordformfield', verbose_name='关联自定义表格字段'),
),
migrations.AlterField(
model_name='testrecorditem',
name='test_record',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='item_test_record', to='qm.testrecord', verbose_name='关联的检验记录'),
),
]

View File

@ -0,0 +1,21 @@
# Generated by Django 3.2.9 on 2021-12-16 02:20
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('qm', '0020_auto_20211216_0945'),
]
operations = [
migrations.RemoveField(
model_name='testrecord',
name='is_testok_robot',
),
migrations.RemoveField(
model_name='testrecorditem',
name='is_testok_robot',
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.9 on 2021-12-16 06:01
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('mtm', '0042_alter_recordformfield_field_type'),
('qm', '0021_auto_20211216_1020'),
]
operations = [
migrations.AlterField(
model_name='testrecorditem',
name='field_value',
field=models.JSONField(blank=True, null=True, verbose_name='录入值'),
),
migrations.AlterField(
model_name='testrecorditem',
name='form_field',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.recordformfield', verbose_name='关联自定义表格字段'),
),
]

View File

@ -1,7 +1,7 @@
from django.db import models
from django.db.models.enums import Choices
from apps.mtm.models import RecordForm, RecordFormField
from apps.system.models import CommonAModel, File
from apps.system.models import CommonADModel, CommonAModel, File
from utils.model import BaseModel
# Create your models here.
class Standard(CommonAModel):
@ -43,7 +43,7 @@ class AnalysisItem(CommonAModel):
class Meta:
verbose_name = '检验分析项'
class TestRecord(CommonAModel):
class TestRecord(CommonADModel):
"""
检验记录
"""
@ -62,7 +62,6 @@ class TestRecord(CommonAModel):
form = models.ForeignKey('mtm.recordform', verbose_name='所用表格', on_delete=models.CASCADE)
type = models.PositiveSmallIntegerField(choices=type_choice, default=TEST_PROCESS)
is_testok = models.BooleanField('是否合格', default=True)
is_testok_robot = models.BooleanField('自动判定的是否合格', default=True)
number = models.CharField('产品编号', null=True, blank=True, max_length=50)
wproduct = models.ForeignKey('wpm.wproduct', verbose_name='关联的动态产品', on_delete=models.CASCADE, null=True, blank=True, related_name='test_wproduct')
material = models.ForeignKey('mtm.material', verbose_name='关联的物料状态', on_delete=models.CASCADE, null=True, blank=True)
@ -70,6 +69,7 @@ class TestRecord(CommonAModel):
subproduction_plan = models.ForeignKey('pm.subproductionplan', verbose_name='关联的生产子计划', on_delete=models.CASCADE, null=True, blank=True)
fifo_item = models.ForeignKey('inm.fifoitem', verbose_name='关联的出入库批次', on_delete=models.CASCADE, null=True, blank=True)
origin_test = models.ForeignKey('self', verbose_name='原检验记录', on_delete=models.CASCADE, null=True, blank=True)
is_submited = models.BooleanField('是否提交', default=False)
remark = models.TextField('备注', default='')
@ -77,13 +77,8 @@ class TestRecordItem(BaseModel):
"""
记录表格字段值
"""
form_field = models.ForeignKey(RecordFormField, verbose_name='关联字段', on_delete=models.CASCADE, db_constraint=False)
field_name = models.CharField('字段名', max_length=50)
field_key = models.CharField('字段标识', max_length=50)
field_type = models.CharField('字段类型', choices=RecordForm.type_choices, max_length=50)
field_value = models.JSONField('录入值', default=dict, blank=True)
need_judge = models.BooleanField('是否需要判定', default=False)
sort = models.IntegerField('排序号', default=1)
form_field = models.ForeignKey(RecordFormField, verbose_name='关联自定义表格字段', on_delete=models.CASCADE)
field_value = models.JSONField('录入值', null=True, blank=True)
is_hidden = models.BooleanField('是否隐藏', default=False)
is_testok = models.BooleanField('是否合格', null=True, blank=True)
is_testok_robot = models.BooleanField('自动判定的是否合格', null=True, blank=True)
test_record = models.ForeignKey(TestRecord, verbose_name='关联的检测记录', on_delete=models.CASCADE, related_name='item_test_record')
test_record = models.ForeignKey(TestRecord, verbose_name='关联的检验记录', on_delete=models.CASCADE, related_name='item_test_record')

View File

@ -1,8 +1,9 @@
from rest_framework import serializers
from apps.mtm.models import RecordForm, RecordFormField
from apps.mtm.serializers import RecordFormFieldSerializer, RecordFormSimpleSerializer
from apps.system.serializers import FileSimpleSerializer
from apps.system.serializers import FileSimpleSerializer, UserSimpleSerializer
from .models import Standard, TestItem, TestRecord, TestRecordItem
from django.db import transaction
class StandardCreateUpdateSerializer(serializers.ModelSerializer):
class Meta:
@ -38,9 +39,25 @@ class AnalysisItemSerializer(serializers.ModelSerializer):
class TestRecordItemCreateSerializer(serializers.ModelSerializer):
class Meta:
model = TestRecordItem
fields = ['form_field', 'field_value']
fields = ['form_field', 'field_value', 'is_testok']
class TestRecordItemUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = TestRecordItem
fields = ['id', 'field_value', 'is_testok', 'is_hidden']
class TestRecordItemSerializer(serializers.ModelSerializer):
field_key = serializers.CharField(source='form_field.field_key', read_only=True)
field_name = serializers.CharField(source='form_field.field_name', read_only=True)
field_type = serializers.CharField(source='form_field.field_type', read_only=True)
need_judge = serializers.BooleanField(source='form_field.need_judge', read_only=True)
field_choice = serializers.JSONField(source='form_field.field_choice', read_only=True)
rule_expression = serializers.JSONField(source='form_field.rule_expression', read_only=True)
display_expression = serializers.JSONField(source='form_field.display_expression', read_only=True)
is_hidden = serializers.BooleanField(source='form_field.is_hidden', read_only=True)
help_text = serializers.CharField(source='form_field.help_text', read_only=True)
sort = serializers.IntegerField(source='form_field.sort', read_only=True)
class Meta:
model = TestRecordItem
fields = '__all__'
@ -52,28 +69,67 @@ class TestRecordCreateSerializer(serializers.ModelSerializer):
fields = ['form', 'record_data', 'is_testok', 'fifo_item']
class TestRecordListSerializer(serializers.ModelSerializer):
form_ = RecordFormSimpleSerializer(source='form', read_only=True)
create_by_ = UserSimpleSerializer(source='create_by', read_only=True)
class Meta:
model = TestRecord
fields = '__all__'
class TestRecordDetailBaseSerializer(serializers.ModelSerializer):
form_ = RecordFormSimpleSerializer(source='form', read_only=True)
record_data = serializers.SerializerMethodField()
create_by_ = UserSimpleSerializer(source='create_by', read_only=True)
class Meta:
model = TestRecord
fields = '__all__'
def get_record_data(self, obj):
return TestRecordItemSerializer(instance=obj.item_test_record.order_by('form_field__sort'), many=True).data
class TestRecordDetailSerializer(serializers.ModelSerializer):
form_ = RecordFormSimpleSerializer(source='form', read_only=True)
record_data_ = TestRecordItemSerializer(source='item_test_record', read_only=True, many=True)
# record_data = TestRecordItemSerializer(source='item_test_record', read_only=True, many=True)
record_data = serializers.SerializerMethodField()
origin_test_ = TestRecordDetailBaseSerializer(source='origin_test', read_only=True)
class Meta:
model = TestRecord
fields = '__all__'
@staticmethod
def setup_eager_loading(queryset):
queryset = queryset.select_related('form','fifo_item')
return queryset
def get_record_data(self, obj):
return TestRecordItemSerializer(instance=obj.item_test_record.order_by('form_field__sort'), many=True).data
# def get_record_data_(self, obj):
# record_data = obj.record_data
# all_fields = RecordFormField.objects.filter(form=obj.form, is_deletd=False).order_by('sort')
# all_fields_l = RecordFormFieldSerializer(instance=all_fields, many=True).data
# for i in all_fields_l:
# key = i['field_key']
# i['field_value'] = record_data.get(key, None)
# return all_fields_l
def to_representation(self, instance):
ret = super().to_representation(instance)
if instance.origin_test and instance.type == TestRecord.TEST_PROCESS_RE:
origin_test = ret['origin_test_']
o_dict = {}
for i in origin_test['record_data']:
o_dict[i['field_key']] = i['field_value']
for i in ret['record_data']:
i['origin_value'] = o_dict[i['field_key']] if i['field_key'] in o_dict else None
return super().to_representation(instance)
class TestRecordItemUpdatexSerializer(serializers.Serializer):
id = serializers.PrimaryKeyRelatedField(queryset=TestRecordItem.objects.all())
field_value = serializers.JSONField(allow_null=True, required=False)
is_testok = serializers.BooleanField(allow_null=True, required=False)
is_hidden = serializers.BooleanField(default=False)
class TestRecordUpdateSerializer(serializers.ModelSerializer):
record_data = TestRecordItemUpdatexSerializer(many=True, write_only=True)
class Meta:
model = TestRecord
fields = ['is_testok', 'record_data']
def update(self, instance, validated_data):
record_data = validated_data.pop('record_data')
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.save()
for i in record_data:
tri = i['id']
tri.field_value = i['field_value']
tri.is_testok = i['is_testok']
tri.is_hidden = i['is_hidden']
tri.save()
return instance

View File

@ -1,5 +1,6 @@
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
from apps.qm.serializers import StandardCreateUpdateSerializer, StandardSerializer, TestItemCreateUpdateSerializer, TestItemSerializer, TestRecordCreateSerializer, TestRecordDetailSerializer, TestRecordListSerializer
from rest_framework import exceptions, serializers
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin
from apps.qm.serializers import StandardCreateUpdateSerializer, StandardSerializer, TestItemCreateUpdateSerializer, TestItemSerializer, TestRecordCreateSerializer, TestRecordDetailSerializer, TestRecordListSerializer, TestRecordUpdateSerializer
from apps.qm.models import Standard, TestItem, TestRecord, TestRecordItem
from django.shortcuts import render
from rest_framework.viewsets import GenericViewSet, ModelViewSet
@ -8,6 +9,10 @@ from rest_framework.exceptions import APIException
from rest_framework.response import Response
from rest_framework import status
from django.db import transaction
from rest_framework.decorators import action
from apps.wpm.models import WProduct
from apps.wpm.services import WpmServies
# Create your views here.
class StandardViewSet(CreateUpdateModelAMixin, ModelViewSet):
"""
@ -43,12 +48,12 @@ class TestItemViewSet(CreateUpdateModelAMixin, ModelViewSet):
return TestItemCreateUpdateSerializer
return TestItemSerializer
class TestRecordViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
class TestRecordViewSet(ListModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin, GenericViewSet):
"""
记录
记录
"""
perms_map = {'*': '*'}
queryset = TestRecord.objects.select_related('fifo_item', 'form').all()
queryset = TestRecord.objects.select_related('fifo_item', 'form').prefetch_related('item_test_record').all()
serializer_class = TestRecordListSerializer
filterset_fields = ['wproduct', 'material', 'step', 'subproduction_plan', 'fifo_item', 'origin_test', 'type']
ordering = ['-id']
@ -58,15 +63,42 @@ class TestRecordViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
return TestRecordListSerializer
elif self.action == 'retrieve':
return TestRecordDetailSerializer
elif self.action == 'update':
return TestRecordUpdateSerializer
return super().get_serializer_class()
def update(self, request, *args, **kwargs):
obj = self.get_object()
if obj.is_submited:
raise exceptions.APIException('该记录已提交不可编辑')
return super().update(request, *args, **kwargs)
def destroy(self, request, *args, **kwargs):
obj = self.get_object()
if obj.is_submited:
raise exceptions.APIException('该记录已提交不可删除')
return super().destroy(request, *args, **kwargs)
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=TestRecordUpdateSerializer)
def submit(self, request, pk=None):
obj = self.get_object()
# 校验是否有未填项目
if obj.type != TestRecord.TEST_PROCESS_RE:
if TestRecordItem.objects.filter(field_value__isnull=True, is_hidden=False, test_record=obj).exists():
raise exceptions.APIException('存在未填写项目')
with transaction.atomic():
obj.is_submited=True
obj.save()
WpmServies.update_wproduct_by_test(obj, request.user)
return Response()
# def create(self, request, *args, **kwargs):
# serializer = self.get_serializer(data=request.data)
# serializer.is_valid(raise_exception=True)
# vdata = serializer.validated_data
# record_data = vdata.pop('record_data')
# if 'is_testok' not in vdata:
# raise APIException('未填写检测结论')
# raise APIException('未填写检结论')
# with transaction.atomic():
# obj = serializer.save(create_by = self.request.user)
# tris = []
@ -83,7 +115,7 @@ class TestRecordViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
# tris.append(TestRecordItem(**m))
# TestRecordItem.objects.bulk_create(tris)
# # 如果检合格
# # 如果检合格
# if obj.fifo_item:
# obj.fifo_item.is_testok = True if obj.is_testok else False
# obj.fifo_item.is_tested = True

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2021-12-13 06:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wf', '0017_auto_20211203_1501'),
]
operations = [
migrations.AddField(
model_name='workflow',
name='key',
field=models.CharField(blank=True, max_length=20, null=True, unique=True, verbose_name='工作流标识'),
),
]

View File

@ -13,9 +13,9 @@ class Workflow(CommonAModel):
工作流
"""
name = models.CharField('名称', max_length=50)
key = models.CharField('工作流标识', unique=True, max_length=20)
key = models.CharField('工作流标识', unique=True, max_length=20, null=True, blank=True)
sn_prefix = models.CharField('流水号前缀', max_length=50, default='hb')
description = models.CharField('描述', max_length=200)
description = models.CharField('描述', max_length=200, null=True, blank=True)
view_permission_check = models.BooleanField('查看权限校验', default=True, help_text='开启后,只允许工单的关联人(创建人、曾经的处理人)有权限查看工单')
limit_expression = models.JSONField('限制表达式', default=dict, blank=True, help_text='限制周期({"period":24} 24小时), 限制次数({"count":1}在限制周期内只允许提交1次), 限制级别({"level":1} 针对(1单个用户 2全局)限制周期限制次数,默认特定用户);允许特定人员提交({"allow_persons":"zhangsan,lisi"}只允许张三提交工单,{"allow_depts":"1,2"}只允许部门id为1和2的用户提交工单{"allow_roles":"1,2"}只允许角色id为1和2的用户提交工单)')
display_form_str = models.JSONField('展现表单字段', default=list, blank=True, help_text='默认"[]",用于用户只有对应工单查看权限时显示哪些字段,field_key的list的json,如["days","sn"],内置特殊字段participant_info.participant_name:当前处理人信息(部门名称、角色名称)state.state_name:当前状态的状态名,workflow.workflow_name:工作流名称')
@ -68,7 +68,7 @@ class State(CommonAModel):
STATE_FIELD_READONLY= 1 # 字段只读
STATE_FIELD_REQUIRED = 2 # 字段必填
STATE_FIELD_OPTIONAL = 3 # 字段可选
STATE_FIELD_HIDDEN = 4 # 字段隐藏
state_filter_choices=(
(0, ''),
(1, '和工单同属一及上级部门'),
@ -83,7 +83,7 @@ class State(CommonAModel):
enable_retreat = models.BooleanField('允许撤回', default=False, help_text='开启后允许工单创建人在此状态直接撤回工单到初始状态')
participant_type = models.IntegerField('参与者类型', choices=state_participanttype_choices, default=1, blank=True, help_text='0.无处理人,1.个人,2.多人,3.部门,4.角色,5.变量(支持工单创建人,创建人的leader),6.脚本,7.工单的字段内容(如表单中的"测试负责人",需要为用户名或者逗号隔开的多个用户名),8.父工单的字段内容。 初始状态请选择类型5参与人填create_by')
participant = models.JSONField('参与者', default=list, blank=True, help_text='可以为空(无处理人的情况,如结束状态)、userid、userid列表\部门id\角色id\变量(create_by,create_by_tl)\脚本记录的id等包含子工作流的需要设置处理人为loonrobot')
state_fields = models.JSONField('表单字段', default=dict, help_text='json格式字典存储,包括读写属性1只读2必填3可选. 示例:{"create_time":1,"title":2, "sn":1}, 内置特殊字段participant_info.participant_name:当前处理人信息(部门名称、角色名称)state.state_name:当前状态的状态名,workflow.workflow_name:工作流名称') # json格式存储,包括读写属性1只读2必填3可选4不显示, 字典的字典
state_fields = models.JSONField('表单字段', default=dict, help_text='json格式字典存储,包括读写属性1只读2必填3可选, 4:隐藏 示例:{"create_time":1,"title":2, "sn":1}, 内置特殊字段participant_info.participant_name:当前处理人信息(部门名称、角色名称)state.state_name:当前状态的状态名,workflow.workflow_name:工作流名称') # json格式存储,包括读写属性1只读2必填3可选4不显示, 字典的字典
distribute_type = models.IntegerField('分配方式', default=1, choices=state_distribute_choices, help_text='1.主动接单(如果当前处理人实际为多人的时候,需要先接单才能处理) 2.直接处理(即使当前处理人实际为多人,也可以直接处理) 3.随机分配(如果实际为多人,则系统会随机分配给其中一个人) 4.全部处理(要求所有参与人都要处理一遍,才能进入下一步)')
filter_policy = models.IntegerField('参与人过滤策略', default=0, choices=state_filter_choices)
participant_cc = models.JSONField('抄送给', default=list, blank=True, help_text='抄送给(userid列表)')
@ -197,7 +197,7 @@ class Ticket(CommonBModel):
('worked', '我处理的'),
('cc', '抄送我的')
)
title = models.CharField('标题', max_length=500, blank=True, default='', help_text="工单标题")
title = models.CharField('标题', max_length=500, null=True, blank=True, help_text="工单标题")
workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='关联工作流')
sn = models.CharField('流水号', max_length=25, help_text="工单的流水号")
state = models.ForeignKey(State, on_delete=models.CASCADE, verbose_name='当前状态', related_name='ticket_state')
@ -213,18 +213,6 @@ class Ticket(CommonBModel):
multi_all_person = models.JSONField('全部处理的结果', default=dict, blank=True, help_text='需要当前状态处理人全部处理时实际的处理结果json格式')
# class TicketCustomField(BaseModel):
# """
# 工单数据,自定义字段值
# """
# ticket = models.ForeignKey(Ticket, verbose_name='关联工单', on_delete=models.CASCADE)
# form_field = models.ForeignKey(CustomField, verbose_name='关联字段', on_delete=models.SET_NULL, db_constraint=False, null=True, blank=True)
# field_name = models.CharField('字段名', max_length=50)
# field_key = models.CharField('字段标识', max_length=50)
# field_type = models.CharField('字段类型', choices=CustomField.field_type_choices, max_length=50)
# field_value = models.JSONField('录入值', default=dict, blank=True)
# sort = models.IntegerField('排序号', default=1)
class TicketFlow(BaseModel):
"""
工单流转日志

View File

@ -49,7 +49,7 @@ class TicketSimpleSerializer(serializers.ModelSerializer):
fields = '__all__'
class TicketCreateSerializer(serializers.ModelSerializer):
transition = serializers.IntegerField(label='流转ID')
transition = serializers.PrimaryKeyRelatedField(queryset=Transition.objects.all(), write_only=True)
class Meta:
model=Ticket
fields=['title','workflow', 'ticket_data', 'transition']

View File

@ -54,6 +54,13 @@ class WfService(object):
"""
return CustomField.objects.filter(is_deleted=False, workflow=workflow).order_by('sort')
@staticmethod
def get_workflow_custom_fields_list(workflow:Workflow):
"""
获取工单字段key List
"""
return list(CustomField.objects.filter(is_deleted=False, workflow=workflow).order_by('sort').values_list('field_key', flat=True))
@classmethod
def get_ticket_transitions(cls, ticket:Ticket):
"""

View File

@ -77,6 +77,11 @@ class WorkflowViewSet(CreateUpdateModelAMixin, ModelViewSet):
ret['workflow'] = pk
ret['transitions'] = TransitionSerializer(instance=transitions, many=True).data
field_list = CustomFieldSerializer(instance=WfService.get_workflow_custom_fields(wf), many=True).data
for i in field_list:
if i['field_key'] in start_state.state_fields:
i['field_attribute'] = start_state.state_fields[i['field_key']]
else:
i['field_attribute'] = State.STATE_FIELD_READONLY
ret['field_list'] = field_list
return Response(ret)
@ -137,15 +142,27 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
rdata = request.data
serializer = self.get_serializer(data=rdata)
serializer.is_valid(raise_exception=True)
start_state = WfService.get_workflow_start_state(rdata['workflow'])
transition = Transition.objects.get(pk=rdata['transition'])
ticket_data = rdata['ticket_data']
vdata = serializer.validated_data #校验之后的数据
start_state = WfService.get_workflow_start_state(vdata['workflow'])
transition = vdata['transition']
ticket_data = vdata['ticket_data']
save_ticket_data = {}
#校验必填项
if transition.field_require_check:
for key, value in start_state.state_fields.items(): #校验必填项
for key, value in start_state.state_fields.items():
if value == State.STATE_FIELD_REQUIRED:
if key not in ticket_data or not ticket_data[key]:
raise APIException('字段{}必填'.format(key))
ticket = serializer.save(state=start_state, create_by=request.user, act_state=Ticket.TICKET_ACT_STATE_DRAFT, belong_dept=request.user.dept) # 先创建出来
save_ticket_data[key] = ticket_data[key]
elif value == State.STATE_FIELD_OPTIONAL:
save_ticket_data[key] = ticket_data[key]
ticket = serializer.save(state=start_state,
create_by=request.user,
act_state=Ticket.TICKET_ACT_STATE_DRAFT,
belong_dept=request.user.dept,
ticket_data=save_ticket_data) # 先创建出来
next_state = WfService.get_next_state_by_transition_and_ticket_info(ticket=ticket, transition=transition)
participant_info = WfService.get_ticket_state_participant_info(state=next_state, ticket=ticket, ticket_data=ticket.ticket_data)
@ -159,7 +176,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
act_state = Ticket.TICKET_ACT_STATE_DRAFT
else:
act_state = Ticket.TICKET_ACT_STATE_ONGOING
title = rdata.get('title', '')
title = vdata['title']
title_template = ticket.workflow.title_template
if title_template:
all_ticket_data = {**rdata, **rdata['ticket_data']}
@ -224,6 +241,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
if value == State.STATE_FIELD_REQUIRED:
if key not in ticket_data or not ticket_data[key]:
raise APIException('字段{}必填'.format(key))
destination_state = WfService.get_next_state_by_transition_and_ticket_info(ticket, transition, ticket_data, request)
multi_all_person = ticket.multi_all_person
if multi_all_person:
@ -265,7 +283,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
ticket.act_state = Ticket.TICKET_ACT_STATE_BACK
# 只更新必填和可选的字段
for key, value in ticket.state.state_fields.items():
for key, value in source_state.state_fields.items():
if value in (State.STATE_FIELD_REQUIRED, State.STATE_FIELD_OPTIONAL):
source_ticket_data[key] = ticket_data[key]
ticket.ticket_data = source_ticket_data

View File

@ -0,0 +1,36 @@
# Generated by Django 3.2.9 on 2021-12-14 04:45
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('wf', '0018_workflow_key'),
('qm', '0018_testrecord_is_submited'),
('wpm', '0031_auto_20211209_1638'),
]
operations = [
migrations.AddField(
model_name='wproduct',
name='test',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wp_test', to='qm.testrecord', verbose_name='当前检验'),
),
migrations.AddField(
model_name='wproduct',
name='ticket',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wp_ticket', to='wf.ticket', verbose_name='当前工单'),
),
migrations.AlterField(
model_name='wproduct',
name='operation',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wp_operation', to='wpm.operation', verbose_name='当前操作'),
),
migrations.AlterField(
model_name='wprouctticket',
name='ticket',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='wt_ticket', to='wf.ticket', verbose_name='关联工单'),
),
]

View File

@ -0,0 +1,29 @@
# Generated by Django 3.2.9 on 2021-12-16 01:45
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('wpm', '0032_auto_20211214_1245'),
]
operations = [
migrations.RemoveField(
model_name='operationrecorditem',
name='field_key',
),
migrations.RemoveField(
model_name='operationrecorditem',
name='field_name',
),
migrations.RemoveField(
model_name='operationrecorditem',
name='field_type',
),
migrations.RemoveField(
model_name='operationrecorditem',
name='sort',
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.9 on 2021-12-16 03:27
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('mtm', '0042_alter_recordformfield_field_type'),
('wpm', '0033_auto_20211216_0945'),
]
operations = [
migrations.AlterField(
model_name='operationrecorditem',
name='form_field',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ori_form_field', to='mtm.recordformfield', verbose_name='关联字段'),
),
migrations.AlterField(
model_name='operationrecorditem',
name='operation_record',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='item_operation_record', to='wpm.operationrecord', verbose_name='关联的生产记录'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2021-12-16 06:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wpm', '0034_auto_20211216_1127'),
]
operations = [
migrations.AlterField(
model_name='operationrecorditem',
name='field_value',
field=models.JSONField(blank=True, null=True, verbose_name='录入值'),
),
]

View File

@ -1,4 +1,5 @@
import re
from rest_framework import exceptions
from django.db import models
from django.db.models.base import Model
import django.utils.timezone as timezone
@ -7,6 +8,7 @@ from apps.inm.models import FIFO, WareHouse
from apps.pm.models import ProductionPlan, SubProductionPlan, SubProductionProgress
from apps.qm.models import TestRecord
from apps.system.models import CommonADModel, CommonAModel, CommonBModel, Organization, User, Dict, File
from apps.wf.models import Ticket
from utils.model import SoftModel, BaseModel
from simple_history.models import HistoricalRecords
from apps.mtm.models import Material, Process, RecordFormField, Step, RecordForm, SubprodctionMaterial
@ -54,15 +56,54 @@ class WProduct(CommonAModel):
remark = models.CharField('备注', max_length=200, null=True, blank=True)
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='当前子生产计划', on_delete=models.CASCADE, related_name='wproduct_subplan')
warehouse = models.ForeignKey(WareHouse, verbose_name='所在仓库', on_delete=models.SET_NULL, null=True, blank=True)
operation = models.ForeignKey('wpm.operation', verbose_name='关联操作',
operation = models.ForeignKey('wpm.operation', verbose_name='当前操作',
on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_operation')
test = models.ForeignKey('qm.testrecord', verbose_name='当前检验',
on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_test')
ticket = models.ForeignKey('wf.ticket', verbose_name='当前工单',
on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_ticket')
@property
def last_process_test(self):
"""
最后的工序自检
最后提交的工序自检
"""
return self.test_wproduct.filter(type=TestRecord.TEST_PROCESS).order_by('-id').first()
return self.test_wproduct.filter(type=TestRecord.TEST_PROCESS, is_submited=True).order_by('-id').first()
# @property
# def last_test(self):
# """
# 最后提交的检验
# """
# return self.test_wproduct.filter(is_submited=True).order_by('-id').first()
# @property
# def current_test(self):
# """
# 当前未提交的检验
# """
# trs = self.test_wproduct.filter(is_submited=False).order_by('-id')
# if trs.count() == 1:
# return trs[0]
# elif trs.count() == 0:
# return None
# else:
# raise exceptions.APIException('存在多条未提交检验记录')
# @property
# def current_ticket(self):
# """
# 当前是否有进行中工单
# """
# tickets = Ticket.objects.filter(wt_ticket__wproduct=self, act_state=Ticket.TICKET_ACT_STATE_ONGOING).order_by('-id')
# if tickets.count() == 1:
# return tickets[0]
# elif tickets.count() == 0:
# return None
# else:
# raise exceptions.APIException('存在多条进行中工单')
class WprouctTicket(CommonAModel):
"""
玻璃审批工单
@ -72,7 +113,7 @@ class WprouctTicket(CommonAModel):
material = models.ForeignKey(Material, 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)
ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单', on_delete=models.CASCADE, related_name='wt_ticket')
class Pick(CommonADModel):
"""
@ -146,13 +187,9 @@ class OperationRecordItem(BaseModel):
"""
记录表格字段值
"""
form_field = models.ForeignKey(RecordFormField, verbose_name='关联字段', on_delete=models.CASCADE, db_constraint=False)
field_name = models.CharField('字段名', max_length=50)
field_key = models.CharField('字段标识', max_length=50)
field_type = models.CharField('字段类型', choices=RecordForm.type_choices, max_length=50)
field_value = models.JSONField('录入值', default=dict, blank=True)
sort = models.IntegerField('排序号', default=1)
operation_record = models.ForeignKey(OperationRecord, verbose_name='关联的生产记录', on_delete=models.CASCADE)
form_field = models.ForeignKey(RecordFormField, verbose_name='关联字段', on_delete=models.CASCADE, related_name='ori_form_field')
field_value = models.JSONField('录入值', null=True, blank=True)
operation_record = models.ForeignKey(OperationRecord, verbose_name='关联的生产记录', on_delete=models.CASCADE, related_name='item_operation_record')
class OperationEquip(BaseModel):
operation = models.ForeignKey(Operation, verbose_name='关联操作', on_delete=models.CASCADE, related_name='oe_operation')

View File

@ -65,7 +65,7 @@ class PickSerializer(serializers.Serializer):
if isLowLevel:
iproducts = i.pop('iproducts')
i['fifo'] = fifo
i['is_testok'] = True # 默认检合格
i['is_testok'] = True # 默认检合格
i['subproduction_plan'] = sp
fifoitem = FIFOItem.objects.create(**i)
# 创建再下一个层级
@ -244,19 +244,18 @@ class DoOutputSerializer(serializers.Serializer):
material = serializers.PrimaryKeyRelatedField(queryset=Material.objects.all(), label='物料ID')
count_output = serializers.IntegerField(min_value=0, label='产出数量')
class OperationRecordItemSerializer(serializers.ModelSerializer):
class Meta:
model = OperationRecordItem
fields = ['form_field', 'field_value']
class OperationRecordItemUpdateSerializer(serializers.Serializer):
id = serializers.PrimaryKeyRelatedField(queryset=OperationRecordItem.objects.all())
field_value = serializers.JSONField(allow_null=True, required=False)
class OperationRecordSubmitSerializer(serializers.ModelSerializer):
record_data = OperationRecordItemSerializer(many=True)
record_data = OperationRecordItemUpdateSerializer(many=True)
class Meta:
model = OperationRecord
fields = ['record_data']
class OperationRecordSerializer(serializers.ModelSerializer):
record_data = OperationRecordItemSerializer(many=True)
record_data = OperationRecordItemUpdateSerializer(many=True)
class Meta:
model = OperationRecord
fields = ['form', 'record_data']
@ -282,27 +281,29 @@ class OperationSubmitSerializer(serializers.Serializer):
class WpmTestRecordItemCreateSerializer(serializers.ModelSerializer):
class Meta:
model = TestRecordItem
fields = ['form_field', 'field_value', 'is_testok']
fields = ['form_field', 'field_value', 'is_testok', 'is_hidden']
class WpmTestRecordCreateSerializer(serializers.ModelSerializer):
record_data = WpmTestRecordItemCreateSerializer(many=True)
wproduct = serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all(), required=True)
is_testok = serializers.BooleanField(required=False)
origin_test = serializers.PrimaryKeyRelatedField(queryset=TestRecord.objects.all(), default=None)
class Meta:
model = TestRecord
fields = ['form', 'record_data', 'is_testok', 'wproduct']
fields = ['form', 'record_data', 'is_testok', 'wproduct', 'is_submited', 'origin_test']
class WpmTestFormInitSerializer(serializers.Serializer):
wproduct = serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all(), required=True)
form = serializers.PrimaryKeyRelatedField(queryset=RecordForm.objects.all(), required=False)
form = serializers.PrimaryKeyRelatedField(queryset=RecordForm.objects.all(), required=True)
# def validate(self, attrs):
# wproduct = attrs['wproduct']
# form = attrs.get('form', None)
# if wproduct.act_state != WProduct.WPR_ACT_STATE_TORETEST:
# if not form:
# raise exceptions.APIException('请指定检查表')
# return super().validate(attrs)
def validate(self, attrs):
wproduct = attrs['wproduct']
form = attrs.get('form', None)
if wproduct.act_state != WProduct.WPR_ACT_STATE_TORETEST:
if not form:
raise exceptions.APIException('请指定检查表')
return super().validate(attrs)
class WplanPutInSerializer(serializers.Serializer):
warehouse = serializers.PrimaryKeyRelatedField(queryset=WareHouse.objects.all(), label="仓库ID")
@ -391,3 +392,25 @@ class OperationMaterialCreate3Serializer(serializers.ModelSerializer):
return super().create(validated_data)
class OperationRecordItemSerializer(serializers.ModelSerializer):
field_key = serializers.CharField(source='form_field.field_key', read_only=True)
field_name = serializers.CharField(source='form_field.field_name', read_only=True)
field_type = serializers.CharField(source='form_field.field_type', read_only=True)
field_choice = serializers.JSONField(source='form_field.field_choice', read_only=True)
is_hidden = serializers.BooleanField(source='form_field.is_hidden', read_only=True)
help_text = serializers.CharField(source='form_field.help_text', read_only=True)
sort = serializers.IntegerField(source='form_field.sort', read_only=True)
class Meta:
model = OperationRecordItem
fields = '__all__'
class OperationRecordDetailSerializer(serializers.ModelSerializer):
form_ = RecordFormSimpleSerializer(source='form', read_only=True)
record_data = serializers.SerializerMethodField()
create_by_ = UserSimpleSerializer(source='create_by', read_only=True)
class Meta:
model = OperationRecord
fields = '__all__'
def get_record_data(self, obj):
return OperationRecordItemSerializer(instance=obj.item_operation_record.order_by('form_field__sort'), many=True).data

View File

@ -1,7 +1,10 @@
from typing import List
from apps.pm.models import SubProductionPlan
from apps.mtm.models import Step, SubprodctionMaterial
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 utils.tools import ranstr
class WpmServies(object):
@classmethod
@ -32,3 +35,32 @@ class WpmServies(object):
splans = SubProductionPlan.objects.filter(is_deleted=False,
subproduction__usedstep_subproduction__step=step, state=SubProductionPlan.SUBPLAN_STATE_WORKING)
return splans
@classmethod
def update_wproduct_by_test(cls, test:TestRecord, user:User):
"""
根据检验结果更新玻璃及相关状态
"""
is_testok = test.is_testok
wproduct = test.wproduct
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()
else:# 如果不合格
wproduct.act_state = WProduct.WPR_ACT_STATE_NOTOK
# 需要走不合格品审理的工单
wproduct.update_by = user
wproduct.test = None
wproduct.save()

View File

@ -16,10 +16,11 @@ from apps.qm.serializers import TestRecordDetailSerializer
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
from rest_framework.decorators import action
from apps.wf.models import Workflow
from apps.wpm.filters import WMaterialFilterSet
from apps.wpm.models import OperationEquip, OperationWproduct, Pick, PickWproduct, WMaterial, WProduct, Operation, OperationMaterial, OperationRecord, OperationRecordItem
from apps.wpm.serializers import OperationEquipListSerializer, OperationEquipUpdateSerializer, OperationMaterialCreate1ListSerailizer, OperationMaterialCreate1Serailizer, OperationMaterialCreate2ListSerailizer, OperationMaterialCreate2Serailizer, OperationMaterialCreate3Serializer, OperationMaterialListSerializer, OperationRecordListSerializer, OperationRecordSubmitSerializer, OperationUpdateSerializer, OperationWproductListSerializer, OperationCreateSerializer, OperationDetailSerializer, OperationListSerializer, PickHalfSerializer, PickHalfsSerializer, PickSerializer, OperationInitSerializer, OperationSubmitSerializer, WMaterialListSerializer, WProductListSerializer, WplanPutInSerializer, WpmTestFormInitSerializer, WpmTestRecordCreateSerializer, WproductPutInSerializer, WproductPutInsSerializer
from apps.wpm.serializers import OperationEquipListSerializer, OperationEquipUpdateSerializer, OperationMaterialCreate1ListSerailizer, OperationMaterialCreate1Serailizer, OperationMaterialCreate2ListSerailizer, OperationMaterialCreate2Serailizer, OperationMaterialCreate3Serializer, OperationMaterialListSerializer, OperationRecordDetailSerializer, OperationRecordListSerializer, OperationRecordSubmitSerializer, OperationUpdateSerializer, OperationWproductListSerializer, OperationCreateSerializer, OperationDetailSerializer, OperationListSerializer, PickHalfSerializer, PickHalfsSerializer, PickSerializer, OperationInitSerializer, OperationSubmitSerializer, WMaterialListSerializer, WProductListSerializer, WplanPutInSerializer, WpmTestFormInitSerializer, WpmTestRecordCreateSerializer, WproductPutInSerializer, WproductPutInsSerializer
from rest_framework.response import Response
from django.db import transaction
from rest_framework import exceptions, serializers
@ -187,100 +188,51 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=WpmTestFormInitSerializer)
def test_init(self, request, pk=None):
"""
检验表单初始化
检验记录创建及初始化
"""
serializer = WpmTestFormInitSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
vdata = serializer.validated_data
wproduct = vdata['wproduct']
form = vdata.get('form', None)
form = vdata['form']
if wproduct.test:
raise exceptions.APIException('存在进行中检验')
# 如果是复检记录, 需要带入原数据
if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST:
# 查找最近一条检验记录
trs = wproduct.last_process_test
if trs:
origin_test = TestRecordDetailSerializer(instance=trs).data
data = RecordFormDetailSerializer(instance=trs.form).data
data['origin_test'] = origin_test
o_dict = {}
for i in origin_test['record_data_']:
o_dict[i['field_key']] = i['field_value']
for i in data['form_fields']:
i['origin_value'] = o_dict[i['field_key']] if i['field_key'] in o_dict else None
else:
raise exceptions.APIException('原工序检验记录不存在')
else:
data = RecordFormDetailSerializer(instance=form).data
# 后续加入系统自带数据
return Response(data)
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=WpmTestRecordCreateSerializer)
@transaction.atomic
def test(self, request, pk=None):
"""
检测
"""
serializer = WpmTestRecordCreateSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
vdata = serializer.validated_data
record_data = vdata.pop('record_data')
wproduct = vdata['wproduct']
# 根据情况创建一条检验记录
if wproduct.act_state not in [WProduct.WPR_ACT_STATE_TOTEST,
WProduct.WPR_ACT_STATE_TORETEST, WProduct.WPR_ACT_STATE_TOFINALTEST, WProduct.WPR_ACT_STATE_TOCOMBTEST]:
raise exceptions.APIException('该产品当前状态不可检验')
if 'is_testok' not in vdata:
raise exceptions.APIException('未填写检测结论')
savedict = dict(create_by = self.request.user,
material=wproduct.material, number=wproduct.number, subproduction_plan=wproduct.subproduction_plan, step=wproduct.step)
savedict = dict(
create_by = self.request.user,
wproduct=wproduct,
material=wproduct.material,
number=wproduct.number,
subproduction_plan=wproduct.subproduction_plan,
step=wproduct.step,
form=form)
if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST:
# 查找最近一条检验记录
trs = wproduct.last_process_test
savedict['origin_test'] = trs
if not trs:
raise exceptions.APIException('原工序检验记录不存在')
savedict['type'] = TestRecord.TEST_PROCESS_RE
elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOFINALTEST:
savedict['type'] = TestRecord.TEST_FINAL
elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOCOMBTEST:
savedict['type'] = TestRecord.TEST_COMB
obj = serializer.save(**savedict)
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['sort'] = form_field.sort
m['need_judge'] = form_field.need_judge
m['is_testok'] = m['is_testok'] if 'is_testok' in m else None
m['test_record'] = obj
tris.append(TestRecordItem(**m))
TestRecordItem.objects.bulk_create(tris)
# 如果检测合格, 变更动态产品进行状态
if obj.is_testok:
if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST: # 复检
obj.origin_test = wproduct.last_process_test
obj.save()
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()
else:# 如果不合格
wproduct.act_state = WProduct.WPR_ACT_STATE_NOTOK
# 需要走不合格品审理单
wproduct.update_by = request.user
tr = TestRecord.objects.create(**savedict)
wproduct.test = tr
wproduct.save()
return Response()
# 创建检验条目
for i in RecordFormField.objects.filter(form=form, is_deleted=False):
tri = TestRecordItem()
tri.test_record = tr
tri.form_field = i
tri.is_hidden = i.is_hidden
tri.save()
return Response(TestRecordDetailSerializer(instance=tr).data)
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=WproductPutInsSerializer)
@transaction.atomic
@ -379,6 +331,12 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
wproduct.save()
return Response()
@action(methods=['get'], detail=False, perms_map={'post':'*'})
def workflows(self, request, pk=None):
"""
可发起的工作流
"""
wfs = Workflow.objects.get()
class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin, GenericViewSet):
"""
生产操作记录
@ -454,6 +412,13 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd
opr.form = i
opr.is_filled = False
opr.save()
opri_list = []
for m in RecordFormField.objects.filter(form=i, is_deleted=False):
opri_dict = {}
opri_dict['operation_record'] = opr
opri_dict['form_field'] = m
opri_list.append(OperationRecordItem(**opri_dict))
OperationRecordItem.objects.bulk_create(opri_list)
# 查询需要使用的生产设备
for i in step.equipments.all():
ope = OperationEquip()
@ -537,7 +502,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd
WProduct.objects.create(**wpr)
elif step.type == Step.STEP_TYPE_COMB:
oms_w = OperationMaterial.objects.filter(operation=op, type=SubprodctionMaterial.SUB_MA_TYPE_OUT,
subproduction_progress__ismain=True)
subproduction_progress__is_main=True)
if len(oms_w) == 1:
oms_w = oms_w[0]
# 校验单片数量是否正确, 暂时未写
@ -560,7 +525,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd
wps = WProduct.objects.filter(ow_wproduct__operation = op)
wps.update(is_hidden=True, child=wproduct, update_by=request.user, update_time=timezone.now())
else:
raise exceptions.APIException('产出物料错误')
raise exceptions.APIException('产出物料未填写或填写错误')
op.is_submited = True
op.save()
return Response()
@ -619,7 +584,7 @@ class OperationEquipViewSet(ListModelMixin, DestroyModelMixin, UpdateModelMixin,
instance.delete()
return Response()
class OperationRecordViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
class OperationRecordViewSet(ListModelMixin, DestroyModelMixin, UpdateModelMixin, RetrieveModelMixin, GenericViewSet):
"""
操作使用的自定义表格
"""
@ -630,6 +595,12 @@ class OperationRecordViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
ordering_fields = ['id']
ordering = ['-id']
def get_serializer_class(self):
if self.action == 'update':
return OperationRecordSubmitSerializer
elif self.action == 'retrieve':
return OperationRecordDetailSerializer
return super().get_serializer_class()
@transaction.atomic()
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
@ -638,34 +609,20 @@ class OperationRecordViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
instance.delete()
return Response()
@action(methods=['get'], detail=True, perms_map={'get':'*'})
def init(self, request, pk=None):
'''
表格初始化
'''
obj = self.get_object()
data = RecordFormDetailSerializer(instance=obj.form).data
# 后续加入系统带入数据
return Response(data)
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=OperationRecordSubmitSerializer)
def submit(self, request, pk=None):
serializer = OperationRecordSubmitSerializer(data=request.data, context={'request':self.request})
def update(self, request, *args, **kwargs):
serializer = OperationRecordSubmitSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
vdata = serializer.validated_data
opr = self.get_object()
wrds = []
for m in vdata['record_data']: # 保存记录详情
form_field = m['form_field']
m['form_field'] = form_field
m['field_name'] = form_field.field_name
m['field_key'] = form_field.field_key
m['field_type'] = form_field.field_type
m['sort'] = form_field.sort
m['operation_record'] = opr
wrds.append(OperationRecordItem(**m))
OperationRecordItem.objects.bulk_create(wrds)
if opr.operation.is_submited:
raise exceptions.APIException('操作已提交不可修改')
for i in vdata['record_data']:
ori = i['id']
ori.field_value = i['field_value']
ori.save()
opr.is_filled = True
opr.update_by = request.user
opr.save()
return Response()