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

This commit is contained in:
shilixia 2021-12-03 15:18:55 +08:00
commit 58d1ccc9bf
17 changed files with 1303 additions and 426 deletions

View File

@ -44,5 +44,7 @@ export default {
top: 15px;
right: 18px;
}
.bigDialog .el-dialog{
width: 70%!important;
}
</style>

View File

@ -0,0 +1,594 @@
<template>
<div>
<el-form
ref="checkForm"
label-width="100px"
:model="checkForm"
>
<el-row>
<el-col
v-for="(item, $index) in formData"
v-show="filterBlock(item.parent,item.display_expression)"
:key="$index"
:span="12"
>
<el-form-item
v-if="item.field_type === 'string'"
:label="item.field_name"
>
<el-input
v-model="checkForm[item.field_key]"
placeholder="请输入"
@input="keyChange(item.field_key)"
/>
</el-form-item>
<el-form-item
v-else-if="item.field_type === 'int'"
:label="item.field_name"
>
<el-input
v-model="checkForm[item.field_key]"
type="number"
placeholder="请输入"
@input="keyChange(item.field_key)"
/>
</el-form-item>
<el-form-item
v-else-if="item.field_type === 'float'"
:label="item.field_name"
>
<el-input
v-model="checkForm[item.field_key]"
type="number"
placeholder="请输入"
@input="keyChange(item.field_key)"
/>
</el-form-item>
<el-form-item
v-else-if="item.field_type === 'date'"
:label="item.field_name"
>
<el-date-picker
v-model="checkForm[item.field_key]"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
style="width: 100%"
@change="keyChange(item.field_key)"
/>
<!--</el-date-picker>-->
</el-form-item>
<el-form-item
v-else-if="item.field_type === 'datetime'"
:label="item.field_name"
>
<el-date-picker
v-model="checkForm[item.field_key]"
type="datetime"
placeholder="选择日期"
value-format="yyyy-MM-dd HH:mm:ss"
style="width: 100%"
@change="keyChange(item.field_key)"
/>
<!--</el-date-picker>-->
</el-form-item>
<el-form-item
v-else-if="item.field_type === 'select'"
:label="item.field_name"
>
<el-select
v-model="checkForm[item.field_key]"
style="width: 100%"
placeholder="请选择"
@change="keyChange(item.field_key)"
>
<el-option
v-for="item1 in item.field_choice"
:key="item1"
:label="item1"
:value="item1"
/>
<!--</el-option>-->
</el-select>
</el-form-item>
<el-form-item
v-else-if="item.field_type === 'selects'"
:label="item.field_name"
>
<el-select
v-model="checkForm[item.field_key]"
style="width: 100%"
multiple
placeholder="请选择"
>
<el-option
v-for="item1 in item.field_choice"
:key="item1"
:label="item1"
:value="item1"
>
</el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row v-show="hasPicture">
<el-form-item label="图表">
<div>
<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">
您的浏览器不支持绘图请升级或更换浏览器
</canvas>
<input type="text" value="" class="hide" id="txt" placeholder="请输入文字">
<textarea class="hide" id="word" cols="15" rows="5" placeholder="请输入文字" autofocus></textarea>
<input id="inputV" type="hidden" value="1">
<div class="canvasBtnWrap">
<div class="canvasBtn" @click="error1">标记</div>
<div class="canvasBtn" @click="word1()">文字</div>
<div class="canvasBtn" @click="back()">回退</div>
<div class="canvasBtn" @click="saveTu()">保存</div>
</div>
<div id="res"></div>
</div>
</div>
</el-form-item>
</el-row>
<div style="text-align: right">
<el-button type="primary" @click="judgeForm">
合格验证
</el-button>
</div>
</el-form>
</div>
</template>
<script>
import { upFile } from "@/api/file";
let preDrawAry = [];
export default {
name: "index",
props: {
results:{
type:Array,
default: () => {
return []
}} ,
formID: {
type:Number,
default:0
},
hasPicture:{
type:Boolean,
default:false
}
},
mounted() {
let that = this;
this.form = this.formID;
// debugger;
// console.log(this.results);
this.formData=this.results;
this.formData=[...this.formData];
for(let i=0;i<this.results.length;i++){
let key = this.results[i].field_key;
that.checkForm[key]='';
that.$set(that.checkForm,key,'')
}
// debugger;
// console.log(that.checkForm);
let listJudge = this.formData.filter(item => {
return item.need_judge === true;
});
listJudge.forEach(item => {
let obj = new Object();
obj = item;
obj.judge = false;
that.judgeList.push(obj)
});
setTimeout(function(){
that.canvasInit();
},500);
},
data(){
return{
checkForm:{},
canvas:null,
ctx:null,
myCanvas_rect:null,
Txt:null,
word:null,
widths:0,
heights:0,
lineW:3,
colorF:"#e42343",
imgData:'',
canvasImg:'',
formData:[],//表单数组
judgeList:[],//判定数组
}
},
methods:{
filterBlock(parent,rule){
// debugger;
let that = this;
if(parent!==''&&parent!==null&&parent!==undefined){
if(rule!==''&&rule!==null&&rule!==undefined){
let reg = /\{(.+?)\}/g;
//let str = rule.replace(temp,'').replace('$','');//==''
//let y = that.checkForm[key];
if(rule.indexOf('||')>-1||rule.indexOf('&&')>-1){
let tam = '', arr = [];
if(rule.indexOf('||')>-1){
arr = rule.split('||');
}else{
arr = rule.split('&&');
}
for (let i = 0;i<arr.length;i++){
//获取判断依据
let a = '';
a = arr[i].replace(/`/g,'');
a = '`'+ a+'`';
let tem =a.match(reg)[0];
let ky = tem.replace(/\{|\}/g, '');//qipao
if(that.checkForm[ky]){
//替换变量
a = a.replace(ky, 'yyy');
let yyy = "'"+that.checkForm[ky]+"'";
if(eval(eval( a))){
tam += 'true';
}else{
tam += 'false';
}
}else{
tam += 'false';
}
}
let rea = true;
if(rule.indexOf('||')>-1){
if( tam.indexOf('true')>-1){
rea = true;
}else{
rea = false;
}
}else{
if( tam.indexOf('false')>-1){
rea = false;
}else{
rea = true;
}
}
return rea;
}else{
let temp =rule.match(reg)[0];
let key = temp.replace(/\{|\}/g, '');//qipao
let a = rule.replace(key, 'yy');
a = a.replace(key, 'yy');
let yy = "'"+that.checkForm[key]+"'";
return eval(eval(a));
// return eval("'"+y+"'"+str);
}
}else{
return false;
}
}else{
return true;
}
},
keyChange(key){
key;
this.$forceUpdate();
/* debugger;
let y = this.checkForm[key];
this.$set(this.checkForm,key,y);*/
// this.filterBlock();
},
/* base64ToFile */
canvasInit(){
let that = this;
preDrawAry = [];
that.canvas = document.getElementById('canvas');
that.ctx = that.canvas.getContext('2d');
that.myCanvas_rect = that.canvas.getBoundingClientRect();
that.Txt = document.getElementById('txt');
that.word = document.getElementById('word');
that.widths = that.myCanvas_rect.width;
that.heights = that.myCanvas_rect.height;
setTimeout(function(){
that.draw();
},500);
},
draw(){
let canvasImg = document.getElementById("canvasImg");
canvasImg.style.width = '500px';
canvasImg.style.height = '300px';
this.ctx.drawImage(canvasImg,0,0,500,300);
},
// 叉号
error1(){
let canvas1 = document.getElementById('canvas');
let ctx1 = canvas1.getContext('2d');
this.Txt.style.display="none";
document.getElementById('word').style.display="none";
ctx1.closePath();
canvas1.onmousedown=function () {
this.imgData= ctx1.getImageData(0,0,canvas1.width,canvas1.height);
preDrawAry.push(this.imgData);
ctx1.beginPath();
ctx1.strokeStyle = "#e42343";
ctx1.lineWidth = "3";
ctx1.lineJoin="round";
};
//鼠标按下的位置
canvas1.onmouseup=function (ev) {
let oldX = ev.offsetX;
let oldY = ev.offsetY;
ctx1.moveTo(oldX,oldY);
ctx1.lineTo(ev.offsetX+10,ev.offsetY+10);
ctx1.moveTo(ev.offsetX+10,ev.offsetY);
ctx1.lineTo(ev.offsetX,ev.offsetY+10);
ctx1.stroke();
};
this.ctx.closePath();
},
// 文字先写字
text(){
let canvas2 = document.getElementById('canvas');
let ctx2 = canvas2.getContext('2d');
let Txt2 = document.getElementById('txt');
Txt2.style.display="block";
document.getElementById('word').style.display="none";
ctx2.font="16px Microsoft Yahei";
canvas2.onmousedown=function (ev) {
this.imgData=ctx2.getImageData(0,0,canvas2.width,canvas2.height);
preDrawAry.push(this.imgData);
var v = Txt2.value;
// console.log(v);
if (v != '') {
var oldX = ev.offsetX;
var oldY = ev.offsetY;
// console.log(oldX,oldY);
ctx2.moveTo(oldX,oldY);
canvas2.onmouseup=function () {
ctx2.fillStyle=this.colorF;
ctx2.fillText(v,oldX,oldY);
canvas2.TextAutoLine(v,canvas2,oldX,oldY,20);
// Txt.value = "";
Txt2.style.display="none";
}
}
}
},
// 文字
word1(){
let canvas3 = document.getElementById('canvas');
let ctx3 = canvas3.getContext('2d');
let Txt3 = document.getElementById('txt');
let word3 = document.getElementById('word');
Txt3.style.display="none";
ctx3.font="16px Microsoft Yahei";
canvas3.onmousedown=function () {
ctx3.closePath();
}
canvas3.onmouseup=function (ev) {
var inputV= document.getElementById('inputV').value;
if(inputV == 1){
document.getElementById('word').focus();
// console.log(ev.offsetX,ev.offsetY);
var oldX = ev.offsetX;
var oldY = ev.offsetY;
word3.style.display="block";
word3.style.left=oldX+'px';
word3.style.top =oldY+'px';
word3.onblur=function () {
let v = word3.value;
if(v != '' && v != ' '){
this.imgData=ctx3.getImageData(0,0,canvas3.width,canvas3.height);
let img = ctx3.getImageData(0,0,canvas3.width,canvas3.height);
// debugger;
preDrawAry.push(img);
ctx3.moveTo(oldX,oldY);
ctx3.fillStyle="#e42343";
let lineWidth = 0;
let canvasWidth = canvas3.width;
let lastSubStrIndex= 0;
for(let i=0;i<v.length;i++){
lineWidth+=ctx3.measureText(v[i]).width;
if(lineWidth>canvasWidth-oldX){
ctx3.fillText(v.substring(lastSubStrIndex,i),oldX,(oldY+10));
oldY+=20;
lineWidth=0;
lastSubStrIndex=i;
}
if(i==v.length-1){
ctx3.fillText(v.substring(lastSubStrIndex,i+1),oldX,(oldY+10));
}
}
inputV ="2";
word3.value = "";
}
}
}
}
},
// 文字过长超出换行toDataURL()
canvasTextAutoLine(str,canvas,initX,initY,lineHeight){
let ctx = canvas.getContext("2d");
let lineWidth = 0;
let canvasWidth = canvas.width;
let lastSubStrIndex= 0;
for(let i=0;i<str.length;i++){
lineWidth+=ctx.measureText(str[i]).width;
if(lineWidth>canvasWidth-initX){
ctx.fillText(str.substring(lastSubStrIndex,i),initX,initY);
initY+=lineHeight;
lineWidth=0;
lastSubStrIndex=i;
}
if(i==str.length-1){
ctx.fillText(str.substring(lastSubStrIndex,i+1),initX,initY);
}
}
},
// 撤销
back(){
this.word.style.display="none";
this.Txt.style.display="none";
if(preDrawAry.length>0) {
var popData = preDrawAry.pop();
this.ctx.putImageData(popData, 0, 0);
}
},
// 回退一次
put(){
this.ctx.putImageData(this.imgData,0,0);
},
//保存
saveTu(){
this.word.style.display="none";
this.Txt.style.display="none";
let canvas = document.getElementById('canvas');
let image = new Image();
image = canvas.toDataURL('image/png');
this.canvasImg = image;
let file = this.base64ToFile(image);
let formData = new FormData();
formData.append('file', file);
upFile(formData).then((res) => {
res;
// console.log(res);
})
},
base64ToFile(baseUrl) {
let arr = baseUrl.split(',');
// let type = arr[0].match(/:(.*?);/)[1]; // 解锁图片类型
let bytes = atob(arr[1]); // 解码base64
let n = bytes .length;
let bufferArray = new Uint8Array(n);
while (n--) {
bufferArray[n] = bytes.charCodeAt(n);
}
// let fileOfBlob = new File([bufferArray], new Date()+'.jpg');
return new File([bufferArray ],'draw.jpg');
},
/* base64ToFile */
judgeForm(){
let that = this ,
reg = /\{(.+?)\}/g,
judgeList = this.judgeList;
for(let i=0;i<judgeList.length;i++){
let arr = [],str = '';
let item = judgeList[i].rule_expression.replace(/`/g,'');
if(item.indexOf('||')>-1){
arr = item.split('||');
}else if(item.indexOf('&&')>-1){
arr = item.split('&&');
}else{
arr.push(item);
}
//对每个条件进行判定如果符合
for (let i = 0;i<arr.length;i++){
//获取判断依据
let a = '`'+ arr[i]+'`';
let tem =a.match(reg)[0];
let ky = tem.replace(/\{|\}/g, '');//qipao
//有值时进行判断
if(that.checkForm[ky]!==''&&that.checkForm[ky]!==null&&that.checkForm[ky]!==undefined){
//替换变量
a = a.replace(ky, 'yyy');
let yyy = "'"+that.checkForm[ky]+"'";
// debugger;
if(eval(eval( a))){
str += 'true';
}else{
str += 'false';
}
}
}
if(str.indexOf('true')>-1){
that.judgeList[i].judge = true;
}else{
that.judgeList[i].judge = false;
}
}
let real = that.judgeList.filter(item=>{
return item.judge==true;
});
if(real.length>0){
alert("检验不合格!")
}else{
alert("检验合格!")
}
},
}
}
</script>
<style scoped>
canvas {
width:500px!important;
/*border: 1px solid #000000;*/
cursor: crosshair;
margin: auto;
}
.canvasBtnWrap {
display: flex;
/*flex-direction: column;*/
/*width: 80px;*/
/*padding-left: 20px;*/
justify-content: flex-end;
}
.canvasBtn {
width: 70px;
height: 35px;
line-height: 35px;
border: 1px solid #aaaaaa;
text-align: center;
border-radius: 15px;
margin-right: 10px;
margin-bottom: 10px;
}
.hide {
display: none;
}
#txt {
position: absolute;
top: 1%;
left: 1%;
width: 150px;
height: 30px;
border: 1px solid #e42343;
}
#word {
position: absolute;
width: 200px;
height: 70px;
padding: 0 2px;
background: none;
color: #e42343;
border: 1px dashed #b9b9b9;
}
#word::-webkit-input-placeholder {
color: #e42343;
}
#word::-moz-placeholder {
color: #e42343;
}
#word::placeholder {
color: #e42343;
}
#res {
display: inline-block;
}
</style>

View File

@ -85,17 +85,26 @@
</el-dialog>
<!--表格展示-->
<el-dialog
class="bigDialog"
:visible.sync="dialogVisibleForm"
:close-on-click-modal="false"
:title="tableForm.name"
>
<el-form ref="Forms" label-width="80px" :model="checkForm">
<el-row
<customForm
:results="fieldList.results"
:hasPicture="hasPicture"
:formID="formID"
@formFunc="formFunc"
/>
<!--<el-form ref="Forms" label-width="100px" :model="checkForm">
<el-row>
<el-col
:span="12"
v-for="(item, $index) in fieldList.results"
v-show="filterBlock(item.parent,item.rule_expression,item.need_judge)"
v-show="filterBlock(item.parent,item.display_expression)"
:key="$index"
>
<!--<div v-if="item.field_type === 'img'">-->
&lt;!&ndash;<div v-if="item.field_type === 'img'">&ndash;&gt;
<el-form-item v-if="item.field_type === 'string'" :label="item.field_name">
<el-input
v-model="checkForm[item.field_key]"
@ -191,7 +200,10 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item label="图表" v-else-if="item.field_type === 'draw'">
</el-col>
</el-row>
<el-row v-show="hasPicture">
<el-form-item label="图表">
<div>
<img id="canvasImg" src="./../../assets/glass.png" style="width:300px;height: 200px;display: none">
<div style="position: relative;display: flex">
@ -204,7 +216,7 @@
<div class="canvasBtnWrap">
<div class="canvasBtn" @click="error1">标记</div>
<div class="canvasBtn" @click="word1()">文字</div>
<!--<button @click="restuya()">清除批注</button>-->
&lt;!&ndash;<button @click="restuya()">清除批注</button>&ndash;&gt;
<div class="canvasBtn" @click="back()">回退</div>
<div class="canvasBtn" @click="saveTu()">保存</div>
</div>
@ -213,7 +225,12 @@
</div>
</el-form-item>
</el-row>
</el-form>
</el-form>-->
<!--<div style="text-align: right">-->
<!--<el-button type="primary" @click="judgeForm">-->
<!--合格验证-->
<!--</el-button>-->
<!--</div>-->
</el-dialog>
</el-card>
</el-col>
@ -454,10 +471,13 @@
</el-form-item>
!-->
<el-form-item label="表达" v-if="field.need_judge === true">
<el-form-item label="判定" v-if="field.need_judge === true">
<el-input v-model="field.rule_expression" type="textarea"/>
</el-form-item>
<el-form-item label="模板图片" v-if="field.field_type === 'img'">
<el-form-item label="展示表达式">
<el-input v-model="field.display_expression" type="textarea"/>
</el-form-item>
<el-form-item label="模板图片" v-if="field.field_type === 'draw'">
<el-upload
class="avatar-uploader"
:action="upUrl"
@ -498,6 +518,7 @@
</template>
<script>
import customForm from '@/components/customForm/index'
import checkPermission from "@/utils/permission";
import {getEquipmentAll} from "@/api/equipment";
import vueJsonEditor from "vue-json-editor";
@ -527,7 +548,7 @@
const defaultfield = {};
let preDrawAry = [];
export default {
components: {Pagination, vueJsonEditor, Treeselect},
components: {Pagination, vueJsonEditor, Treeselect, customForm},
data() {
return {
step: defaultstep,
@ -535,6 +556,7 @@
upHeaders: upHeaders(),
upUrl: upUrl(),
fileList: [],
hasPicture: true,
listLoading: true,
need_judge: false,
dialogVisibles: false,
@ -655,12 +677,14 @@
myCanvas_rect: null,
Txt: null,
word: null,
formID: 0,
widths: 0,
heights: 0,
lineW: 3,
colorF: "#e42343",
imgData: '',
canvasImg: '',
judgeList: [],
};
},
computed: {},
@ -670,6 +694,9 @@
this.recordformLists();
},
methods: {
formFunc(value) {
this.dialogVisibleForm = value;
},
handleAvatarSuccess(res, file) {
this.field.draw_template = res.data.path;
},
@ -686,10 +713,9 @@
this.fieldList.results = [...this.fieldList.results];
this.filterBlock();
},
filterBlock(parent,rule,judge){
filterBlock(parent, rule) {
let that = this;
if (parent !== '' && parent !== null && parent !== undefined) {
if(judge){
if (rule !== '' && rule !== null && rule !== undefined) {
let reg = /\{(.+?)\}/g;
//let str = rule.replace(temp,'').replace('$','');//==''
@ -752,9 +778,6 @@
} else {
return true;
}
}else{
return true;
}
},
checkValue() {
this.field.field_key = this.field.field_key.replace(/[^a-zA-Z]/g, '');
@ -774,23 +797,39 @@
that.listQueryfield.form = that.formID;
getrffieldList(that.listQueryfield).then((response) => {
if (response.data) {
debugger;
this.hasPicture = false;
that.fieldList = response.data;
let list = response.data.results;
let arr = list.filter(item => {
return item.field_type === 'draw'
});
that.judgeList = [];
let listJudge = list.filter(item => {
return item.need_judge === true;
});
listJudge.forEach(item => {
let obj = new Object();
obj = item;
obj.judge = false;
that.judgeList.push(obj)
});
if (arr.length > 0) {
this.hasPicture = true;
}
for (let i = 0; i < list.length; i++) {
let key = list[i].field_key;
that.checkForm[key] = '';
that.$set(that.checkForm, key, '')
}
that.dialogVisibleForm = true;
setTimeout(function(){
/* setTimeout(function () {
that.canvasInit();
},500);
}, 500);*/
}
});
that.fieldLists();
},
canvasInit(){
/*canvasInit() {
let that = this;
preDrawAry = [];
that.canvas = document.getElementById('canvas');
@ -803,7 +842,7 @@
setTimeout(function () {
that.draw();
}, 500);
},
},*/
checkPermission,
handleCurrentChange(row) {
@ -845,7 +884,7 @@
}
});
},
draw(){
/*draw() {
let canvasImg = document.getElementById("canvasImg");
canvasImg.style.width = '300px';
canvasImg.style.height = '200px';
@ -982,13 +1021,13 @@
}
},
/* // 删除批注
/!* // 删除批注
restuya(){
this.word.style.display="none";
this.Txt.style.display="none";
this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height);
window.location.reload();
},*/
},*!/
// 撤销
back() {
@ -1032,7 +1071,65 @@
// let fileOfBlob = new File([bufferArray], new Date()+'.jpg');
return new File([bufferArray], 'draw.jpg');
},
judgeForm() {
let that = this,
reg = /\{(.+?)\}/g,
judgeList = this.judgeList;
debugger;
console.log(judgeList);
for (let i = 0; i < judgeList.length; i++) {
let arr = [], str = '';
let item = judgeList[i].rule_expression.replace(/`/g, '');
if (item.indexOf('||') > -1) {
arr = item.split('||');
} else if (item.indexOf('&&') > -1) {
arr = item.split('&&');
} else {
arr.push(item);
}
debugger;
console.log(arr);
//对每个条件进行判定如果符合
for (let i = 0; i < arr.length; i++) {
//获取判断依据
let a = '`' + arr[i] + '`';
let tem = a.match(reg)[0];
let ky = tem.replace(/\{|\}/g, '');//qipao
debugger;
console.log(that.checkForm[ky]);
//有值时进行判断
if (that.checkForm[ky] !== '' && that.checkForm[ky] !== null && that.checkForm[ky] !== undefined) {
//替换变量
a = a.replace(ky, 'yyy');
let yyy = "'" + that.checkForm[ky] + "'";
debugger;
if (eval(eval(a))) {
str += 'true';
} else {
str += 'false';
}
}
}
debugger;
console.log(str);
if (str.indexOf('true') > -1) {
that.judgeList[i].judge = true;
} else {
that.judgeList[i].judge = false;
}
}
let real = that.judgeList.filter(item => {
return item.judge == true;
});
debugger;
console.log(that.judgeList);
console.log(real);
if (real.length > 0) {
alert("检验不合格!")
} else {
alert("检验合格!")
}
},*/
//新增记录表
handleCreate() {
this.recordform = Object.assign({}, defaultrecordform);
@ -1177,55 +1274,5 @@
.my-content {
background: #fde2e2;
}
canvas{
border:1px solid #000000;
cursor: crosshair;
}
.canvasBtnWrap{
display: flex;
flex-direction: column;
width: 80px;
padding-left: 20px;
justify-content: space-between;
}
.canvasBtn{
width: 70px;
height: 35px;
line-height: 35px;
border: 1px solid #aaaaaa;
text-align: center;
border-radius: 15px;
}
.hide{
display: none;
}
#txt{
position: absolute;
top: 1%;
left: 1%;
width: 150px;
height: 30px;
border:1px solid #e42343;
}
#word{
position: absolute;
width: 150px;
height: 70px;
padding: 0 2px;
background:none;
color:#e42343;
border: 1px dashed #b9b9b9;
}
#word::-webkit-input-placeholder{
color:#e42343;
}
#word::-moz-placeholder{
color:#e42343;
}
#word::placeholder{
color:#e42343;
}
#res{
display: inline-block;
}
</style>

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2021-12-03 01:09
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mtm', '0040_material_piece_count'),
]
operations = [
migrations.AlterField(
model_name='material',
name='type',
field=models.PositiveSmallIntegerField(choices=[(1, '成品'), (2, '半成品'), (3, '主要原料'), (4, '辅助材料'), (5, '加工工具'), (6, '辅助工装')], default=1, verbose_name='物料类型'),
),
]

View File

@ -36,7 +36,7 @@ class Material(CommonAModel):
name = models.CharField('物料名称', max_length=100)
number = models.CharField('编号', max_length=100, unique=True)
specification = models.CharField('型号', max_length=100, null=True, blank=True)
type = models.CharField('物料类型', choices= type_choices, max_length=20, default=1)
type = models.PositiveSmallIntegerField('物料类型', choices= type_choices, default=1)
sort_str = models.CharField('排序字符', max_length=100, null=True, blank=True)
unit = models.CharField('基准计量单位', choices=unit_choices, default='', max_length=10)
count = models.IntegerField('物料总数', default=0)

View File

@ -42,7 +42,7 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel
queryset = ProductionPlan.objects.select_related('order', 'order__contract', 'product')
serializer_class = ProductionPlanSerializer
search_fields = ['number']
filterset_fields = []
filterset_fields = ['product', 'order']
ordering_fields = ['id']
ordering = ['-id']

View File

@ -0,0 +1,30 @@
# Generated by Django 3.2.9 on 2021-12-02 08:20
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('mtm', '0040_material_piece_count'),
('qm', '0012_alter_testrecorditem_field_type'),
]
operations = [
migrations.AddField(
model_name='testrecord',
name='step',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.step', verbose_name='关联的工序步骤'),
),
migrations.AddField(
model_name='testrecord',
name='test_record',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='qm.testrecord', verbose_name='关联检验记录'),
),
migrations.AddField(
model_name='testrecord',
name='type',
field=models.PositiveSmallIntegerField(choices=[(10, '子工序检验'), (20, '工序检验'), (30, '工序复检'), (40, '成品检验')], default=20),
),
]

View File

@ -47,14 +47,27 @@ class TestRecord(CommonAModel):
"""
检验记录
"""
TEST_STEP = 10
TEST_PROCESS = 20
TEST_PROCESS_RE = 30
TEST_FINAL = 40
type_choice = (
(TEST_STEP, '子工序检验'),
(TEST_PROCESS, '工序检验'),
(TEST_PROCESS_RE, '工序复检'),
(TEST_FINAL, '成品检验')
)
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)
material = models.ForeignKey('mtm.material', verbose_name='关联的物料状态', on_delete=models.CASCADE, null=True, blank=True)
step = models.ForeignKey('mtm.step', verbose_name='关联的工序步骤', on_delete=models.CASCADE, null=True, blank=True)
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)
test_record = models.ForeignKey('self', verbose_name='关联检验记录', on_delete=models.CASCADE, null=True, blank=True)
remark = models.TextField('备注', default='')

View File

@ -50,7 +50,7 @@ class OrderViewSet(CreateUpdateCustomMixin, ModelViewSet):
queryset = Order.objects.select_related('contract', 'customer').all()
serializer_class = OrderSerializer
search_fields = ['number', 'product']
filterset_fields = []
filterset_fields = ['product', 'contract', 'customer']
ordering_fields = ['create_time']
ordering = ['-create_time']

View File

@ -210,6 +210,18 @@ class Ticket(CommonBModel):
act_state = models.IntegerField('进行状态', default=1, help_text='当前工单的进行状态', choices=act_state_choices)
multi_all_person = models.JSONField('全部处理的结果', default=dict, blank=True, help_text='需要当前状态处理人全部处理时实际的处理结果json格式')
class TicketData():
"""
工单数据,自定义字段值
"""
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

@ -0,0 +1,54 @@
# Generated by Django 3.2.9 on 2021-12-02 01:54
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('inm', '0019_auto_20211201_1011'),
('pm', '0015_auto_20211122_1556'),
('mtm', '0040_material_piece_count'),
('wpm', '0026_auto_20211201_1608'),
]
operations = [
migrations.CreateModel(
name='Pick',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('type', models.PositiveSmallIntegerField(choices=[(10, '仓库领取'), (20, '半成品领取')], default=10)),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='pick_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('fifo', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='inm.fifo', verbose_name='关联的出入库记录')),
('subproduction_plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pm.subproductionplan', verbose_name='关联子生产计划')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='pick_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='PickWproduct',
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, verbose_name='物品编号')),
('material', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='领取时的物料状态')),
('pick', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.pick', verbose_name='关联领料')),
('subproduction_plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pm.subproductionplan', verbose_name='领取时所属子生产计划')),
('wproduct', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pw_wproduct', to='wpm.wproduct', verbose_name='关联半成品')),
],
options={
'abstract': False,
},
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.9 on 2021-12-02 08:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wpm', '0027_pick_pickwproduct'),
]
operations = [
migrations.AlterField(
model_name='wproduct',
name='act_state',
field=models.IntegerField(choices=[(6, '待复检'), (8, '操作准备中'), (10, '操作进行中'), (20, '待检验'), (30, '已合格'), (40, '库存中'), (50, '不合格'), (60, '待成品检验')], default=0, verbose_name='进行状态'),
),
migrations.AlterField(
model_name='wproduct',
name='is_executed',
field=models.BooleanField(default=False, verbose_name='是否执行'),
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.9 on 2021-12-02 08:30
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('wpm', '0028_auto_20211202_1620'),
]
operations = [
migrations.RemoveField(
model_name='wproduct',
name='is_executed',
),
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='关联操作'),
),
]

View File

@ -3,7 +3,7 @@ from django.db import models
from django.db.models.base import Model
import django.utils.timezone as timezone
from django.db.models.query import QuerySet
from apps.inm.models import WareHouse
from apps.inm.models import FIFO, WareHouse
from apps.pm.models import ProductionPlan, SubProductionPlan, SubProductionProgress
from apps.system.models import CommonADModel, CommonAModel, CommonBModel, Organization, User, Dict, File
from utils.model import SoftModel, BaseModel
@ -20,12 +20,12 @@ class WMaterial(BaseModel):
batch = models.CharField('批次号', max_length=100, null=True, blank=True)
count = models.PositiveIntegerField('当前数量', default=0)
class WProduct(CommonAModel):
"""
动态半成品/成品表
"""
WPR_ACT_STATE_TORETEST = 6
WPR_ACT_STATE_DOWAIT = 8
WPR_ACT_STATE_DOING = 10
WPR_ACT_STATE_TOTEST = 20
WPR_ACT_STATE_OK = 30
@ -34,7 +34,8 @@ class WProduct(CommonAModel):
WPR_ACT_STATE_TOFINALTEST = 60
act_state_choices=(
(WPR_ACT_STATE_TORETEST, '待复检'),
(WPR_ACT_STATE_DOING, '生产中'),
(WPR_ACT_STATE_DOWAIT, '操作准备中'),
(WPR_ACT_STATE_DOING, '操作进行中'),
(WPR_ACT_STATE_TOTEST, '待检验'),
(WPR_ACT_STATE_OK, '已合格'),
(WPR_ACT_STATE_INM, '库存中'),
@ -46,14 +47,38 @@ class WProduct(CommonAModel):
pre_step = models.ForeignKey(Step, verbose_name='已执行到', help_text='已执行完的步骤', null=True, blank=True, on_delete=models.CASCADE, related_name='w_pre_step')
step = models.ForeignKey(Step, verbose_name='所在步骤', on_delete=models.CASCADE, null=True, blank=True, related_name='w_step')
act_state = models.IntegerField('进行状态', default=0, choices=act_state_choices)
is_executed = models.BooleanField('子工序是否已执行', default=False)
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, 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='关联操作',
on_delete=models.SET_NULL, null=True, blank=True, related_name='current_operation')
on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_operation')
class Pick(CommonADModel):
"""
领料记录
"""
PICK_FROM_WAREHOUSE = 10
PICK_FROM_WPRODUCT = 20
type_choice = (
(PICK_FROM_WAREHOUSE, '仓库领取'),
(PICK_FROM_WPRODUCT, '半成品领取'),
)
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.CASCADE)
type = models.PositiveSmallIntegerField(choices=type_choice, default=PICK_FROM_WAREHOUSE)
fifo = models.ForeignKey(FIFO, verbose_name='关联的出入库记录', on_delete=models.CASCADE, null=True, blank=True)
class PickWproduct(BaseModel):
"""
领取半成品时详情
"""
pick = models.ForeignKey(Pick, verbose_name='关联领料', on_delete=models.CASCADE)
wproduct = models.ForeignKey(WProduct, verbose_name='关联半成品', on_delete=models.CASCADE, related_name='pw_wproduct')
number = models.CharField('物品编号', null=True, blank=True, max_length=50)
material = models.ForeignKey(Material, verbose_name='领取时的物料状态', on_delete=models.CASCADE)
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='领取时所属子生产计划', on_delete=models.CASCADE)
class Operation(CommonADModel):
"""

View File

@ -13,27 +13,27 @@ from django.utils.translation import gettext_lazy as _
from apps.pm.serializers import SubproductionPlanSimpleSerializer
from apps.qm.models import TestRecord, TestRecordItem
from apps.system.serializers import UserSimpleSerializer
from apps.wpm.models import Operation, OperationEquip, OperationMaterial, OperationWproduct, WMaterial, WProduct, OperationRecord, OperationRecordItem
from apps.wpm.models import Operation, OperationEquip, OperationMaterial, OperationWproduct, Pick, WMaterial, WProduct, OperationRecord, OperationRecordItem
from django.db import transaction
class PickHalfSerializer(serializers.Serializer):
id = serializers.PrimaryKeyRelatedField(queryset=SubProductionProgress.objects.all(), label='子计划进度ID')
wproducts = serializers.ListField(child=serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all(), label='半成品ID'),
required=False) # 从半成品表里直接修改状态
wproducts = serializers.ListField(child=serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all())
, label='半成品ID', required=False) # 从半成品表里直接修改状态
class PickHalfsSerializer(serializers.ListSerializer):
child = PickHalfSerializer()
class PickDetailSerializer(serializers.Serializer):
material = serializers.PrimaryKeyRelatedField(queryset=Material.objects.all(), label="物料ID")
batch = serializers.CharField(label='物料批次', allow_blank=True)
warehouse = serializers.PrimaryKeyRelatedField(queryset=WareHouse.objects.all(), label="仓库ID")
pick_count = serializers.IntegerField(label="领料数量", required=False)
iproducts = serializers.ListField(child=serializers.PrimaryKeyRelatedField(queryset=IProduct.objects.all(), label='库存半成品ID'),
required=False)
iproducts = serializers.PrimaryKeyRelatedField(queryset=IProduct.objects.all(), label='库存半成品ID',required=False, many=True)
class PickSerializer(serializers.Serializer):
subproduction_plan=serializers.PrimaryKeyRelatedField(queryset=SubProductionPlan.objects.all(), label="子计划ID")
picks = PickDetailSerializer(many=True) # 从库存里拿
def create(self, validated_data):
picks = validated_data.pop('picks')
sp = validated_data.pop('subproduction_plan')
@ -101,13 +101,20 @@ class PickSerializer(serializers.Serializer):
wids = IProduct.objects.filter(pk__in=[x.id for x in iproducts]).values_list('wproduct', flat=True)
wproducts = WProduct.objects.filter(pk__in=wids)
first_step = Step.objects.get(pk=sp.steps[0]['id'])
wproducts.update(step=first_step, is_executed=False,
wproducts.update(step=first_step,
act_state=WProduct.WPR_ACT_STATE_TORETEST, is_hidden=False, warehouse=None,
subproduction_plan=sp)
sp.is_picked=True
sp.state = SubProductionPlan.SUBPLAN_STATE_WORKING #生产中
sp.state_date_real = timezone.now() #实际开工日期
sp.save()
# 创建领料记录
pick = Pick()
pick.subproduction_plan = sp
pick.type = Pick.PICK_FROM_WAREHOUSE
pick.fifo = fifo
pick.create_by = self.context['request'].user
pick.save()
# 更新库存
fifo.is_audited = True
fifo.save()
@ -166,8 +173,7 @@ class OperationCreateSerializer(serializers.Serializer):
"""
step = serializers.PrimaryKeyRelatedField(queryset=Step.objects.all(), label="子工序ID")
# subproduction_plan = serializers.PrimaryKeyRelatedField(queryset=SubProductionPlan.objects.all(), label="子计划ID", required=False)
wproducts = serializers.ListField(child=
serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all()), label="半成品ID列表", required=False)
wproducts = serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all(), label="半成品ID列表", required=False, many=True)
def validate(self, data):
# subproduction_plan = data['subproduction_plan']
@ -181,10 +187,10 @@ class OperationCreateSerializer(serializers.Serializer):
if step.type == Step.STEP_TYPE_DIV:
raise exceptions.APIException(_('不可进行此操作'))
for i in data['wproducts']:
if i.act_state != WProduct.WPR_ACT_STATE_DOING:
raise exceptions.APIException('半成品不在生产状态')
if i.is_executed:
raise exceptions.APIException('不可进行操作')
if i.act_state != WProduct.WPR_ACT_STATE_DOWAIT:
raise exceptions.APIException('半成品不在待操作状态')
# if i.is_executed:
# raise exceptions.APIException('不可进行操作')
# if i.subproduction_plan != subproduction_plan:
# raise exceptions.APIException('半成品所属子计划不一致')
if i.step != step:
@ -203,8 +209,7 @@ class OperationUpdateSerializer(serializers.ModelSerializer):
class OperationInitSerializer(serializers.Serializer):
step = serializers.PrimaryKeyRelatedField(queryset=Step.objects.all(), label="子工序ID")
subproduction_plan = serializers.PrimaryKeyRelatedField(queryset=SubProductionPlan.objects.all(), label="子计划ID", required=False)
wproducts = serializers.ListField(child=
serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all()), label="半成品ID列表", required=False)
wproducts = serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all(), label="半成品ID列表", required=False, many=True)
def validate(self, data):
# subproduction_plan = data['subproduction_plan']
@ -218,8 +223,8 @@ class OperationInitSerializer(serializers.Serializer):
if step.type == Step.STEP_TYPE_DIV:
raise exceptions.APIException(_('不可进行此操作'))
for i in data['wproducts']:
if i.is_executed:
raise exceptions.APIException('不可进行操作')
if i.act_state != WProduct.WPR_ACT_STATE_DOWAIT:
raise exceptions.APIException('半成品不在待操作状态')
# if i.subproduction_plan != subproduction_plan:
# raise exceptions.APIException('半成品所属子计划不一致')
if i.step != step:
@ -267,8 +272,7 @@ class OperationWproductListSerializer(serializers.ModelSerializer):
class OperationSubmitSerializer(serializers.Serializer):
step = serializers.PrimaryKeyRelatedField(queryset=Step.objects.all(), label="子工序ID")
subproduction_plan = serializers.PrimaryKeyRelatedField(queryset=SubProductionPlan.objects.all(), label="子计划ID", required=False)
wproducts = serializers.ListField(child=
serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all()), label="半成品ID列表", required=False)
wproducts = serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all(), label="半成品ID列表", required=False, many=True)
input = DoInputSerializer(many=True, required=False)
output = DoOutputSerializer(many=True, required=False)
forms = OperationRecordSerializer(many=True, required=False)
@ -288,11 +292,6 @@ class WpmTestRecordCreateSerializer(serializers.ModelSerializer):
model = TestRecord
fields = ['form', 'record_data', 'is_testok', 'wproduct']
class WproductPutInSerializer(serializers.Serializer):
"""
半成品入库序列化
"""
class WplanPutInSerializer(serializers.Serializer):
warehouse = serializers.PrimaryKeyRelatedField(queryset=WareHouse.objects.all(), label="仓库ID")
remark = serializers.CharField(label="入库备注", required =False)
@ -301,6 +300,12 @@ class WproductPutInSerializer(serializers.Serializer):
warehouse = serializers.PrimaryKeyRelatedField(queryset=WareHouse.objects.all(), label="仓库ID")
remark = serializers.CharField(label="入库备注", required =False)
class WproductPutInsSerializer(serializers.Serializer):
warehouse = serializers.PrimaryKeyRelatedField(queryset=WareHouse.objects.all(), label="仓库ID")
wproducts = serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all(), label='半成品ID', many=True)
remark = serializers.CharField(label="入库备注", required =False)
class OperationEquipListSerializer(serializers.Serializer):
equip_ = EquipmentSimpleSerializer(source='equip', read_only=True)
class Meta:

View File

@ -11,14 +11,14 @@ from apps.mtm.models import Material, RecordForm, RecordFormField, Step, Subprod
from apps.mtm.serializers import RecordFormDetailSerializer, SubprodctionMaterialListSerializer, TechDocListSerializer
from apps.pm.models import SubProductionPlan, SubProductionProgress
from apps.pm.serializers import SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer, SubProductionProgressSerializer
from apps.qm.models import TestRecordItem
from apps.qm.models import TestRecord, TestRecordItem
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
from rest_framework.decorators import action
from apps.wpm.filters import WMaterialFilterSet
from apps.wpm.models import OperationEquip, OperationWproduct, WMaterial, WProduct, Operation, OperationMaterial, OperationRecord, OperationRecordItem
from apps.wpm.models import OperationEquip, OperationWproduct, Pick, PickWproduct, WMaterial, WProduct, Operation, OperationMaterial, OperationRecord, OperationRecordItem
from apps.wpm.serializers import OperationEquipListSerializer, OperationEquipUpdateSerializer, OperationMaterialCreate1Serailizer, OperationMaterialCreate2Serailizer, OperationMaterialCreate3Serializer, OperationMaterialListSerializer, OperationRecordListSerializer, OperationRecordSubmitSerializer, OperationUpdateSerializer, OperationWproductListSerializer, OperationCreateSerializer, OperationDetailSerializer, OperationListSerializer, PickHalfSerializer, PickSerializer, OperationInitSerializer, OperationSubmitSerializer, WMaterialListSerializer, WProductListSerializer, WplanPutInSerializer, WpmTestRecordCreateSerializer, WproductPutInSerializer
from apps.wpm.serializers import OperationEquipListSerializer, OperationEquipUpdateSerializer, OperationMaterialCreate1Serailizer, OperationMaterialCreate2Serailizer, OperationMaterialCreate3Serializer, OperationMaterialListSerializer, OperationRecordListSerializer, OperationRecordSubmitSerializer, OperationUpdateSerializer, OperationWproductListSerializer, OperationCreateSerializer, OperationDetailSerializer, OperationListSerializer, PickHalfSerializer, PickHalfsSerializer, PickSerializer, OperationInitSerializer, OperationSubmitSerializer, WMaterialListSerializer, WProductListSerializer, WplanPutInSerializer, WpmTestRecordCreateSerializer, WproductPutInSerializer, WproductPutInsSerializer
from rest_framework.response import Response
from django.db import transaction
from rest_framework import exceptions, serializers
@ -27,6 +27,7 @@ from apps.wpm.services import WpmServies
from django.utils import timezone
from utils.tools import ranstr
from rest_framework import status
from django.db.models import Count
# Create your views here.
class WPlanViewSet(ListModelMixin, GenericViewSet):
"""
@ -40,7 +41,7 @@ class WPlanViewSet(ListModelMixin, GenericViewSet):
ordering_fields = []
ordering = ['-update_time']
@action(methods=['post', 'get'], detail=True, perms_map={'post':'*', 'get':'*'}, serializer_class=PickHalfSerializer)
@action(methods=['post', 'get'], detail=True, perms_map={'post':'*', 'get':'*'}, serializer_class=PickHalfsSerializer)
@transaction.atomic
def pick_half(self, request, pk=None):
"""
@ -55,10 +56,16 @@ class WPlanViewSet(ListModelMixin, GenericViewSet):
material__type=Material.MA_TYPE_HALFGOOD, subproduction_plan=sp).select_related('material')
return Response(SubProductionProgressSerializer(instance=spps, many=True).data)
elif request.method=='POST':
serializer= PickHalfSerializer(data=request.data, many=True)
serializer= PickHalfsSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
vdata = serializer.data
first_step = Step.objects.get(pk=sp.steps[0]['id'])
# 创建领料记录
pick = Pick()
pick.subproduction_plan = sp
pick.type = Pick.PICK_FROM_WPRODUCT
pick.create_by = request.user
pick.save()
for i in vdata:
if 'wproducts' in i and len(i['wproducts'])>0:
spp = SubProductionProgress.objects.get(pk=i['id'])
@ -66,12 +73,21 @@ class WPlanViewSet(ListModelMixin, GenericViewSet):
# if spp.count_pick > spp.count:
# raise exceptions.APIException('超过计划数')
spp.save()
wps = WProduct.objects.filter(pk__in=[x for x in i['wproducts']])
wps.update(step=first_step, is_executed=False,
wps = WProduct.objects.filter(pk__in=[x for x in i['wproducts']], act_state=WProduct.WPR_ACT_STATE_OK)
wps.update(step=first_step,
act_state=WProduct.WPR_ACT_STATE_TORETEST, is_hidden=False, warehouse=None,
subproduction_plan=sp, update_by=request.user, update_time=timezone.now())
for i in wps:
pw = PickWproduct()
pw.pick =pick
pw.wproduct = i
pw.number = i.number
pw.material = i.material
pw.subproduction_plan = i.suproduction_plan
pw.save()
sp.is_picked = True
sp.save()
return Response()
@ -160,9 +176,9 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
半成品
"""
perms_map={'*':'*'}
queryset = WProduct.objects.select_related('step', 'material').filter(is_hidden=False, operation=None)
queryset = WProduct.objects.select_related('step', 'material').filter(is_hidden=False)
serializer_class = WProductListSerializer
filterset_fields = ['step', 'subproduction_plan', 'material', 'step__process', 'act_state']
filterset_fields = ['step', 'subproduction_plan', 'material', 'step__process', 'act_state', 'material__type']
search_fields = ['number']
ordering_fields = ['id']
ordering = ['id']
@ -178,13 +194,19 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
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]:
if wproduct.act_state not in [WProduct.WPR_ACT_STATE_TOTEST,
WProduct.WPR_ACT_STATE_TORETEST, WProduct.WPR_ACT_STATE_TOFINALTEST]:
raise exceptions.APIException('该产品当前状态不可检验')
if 'is_testok' not in vdata:
raise exceptions.APIException('未填写检测结论')
obj = serializer.save(create_by = self.request.user,
material=wproduct.material, number=wproduct.number, subproduction_plan=wproduct.subproduction_plan)
savedict = dict(create_by = self.request.user,
material=wproduct.material, number=wproduct.number, subproduction_plan=wproduct.subproduction_plan, step=wproduct.step)
if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST:
savedict['type'] = TestRecord.TEST_PROCESS_RE
elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOFINALTEST:
savedict['type'] = TestRecord.TEST_FINAL
obj = serializer.save(**savedict)
tris = []
for m in record_data: # 保存记录详情
form_field = m['form_field']
@ -202,6 +224,11 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
# 如果检测合格, 变更动态产品进行状态
if obj.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)
@ -218,46 +245,54 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
return Response()
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=WpmTestRecordCreateSerializer)
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=WproductPutInsSerializer)
@transaction.atomic
def retest(self, request, pk=None):
def putins(self, request, pk=None):
"""
复检
半成品批量入库
"""
serializer = WpmTestRecordCreateSerializer(data=request.data)
serializer= WproductPutInsSerializer(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 != WProduct.WPR_ACT_STATE_TORETEST:
raise exceptions.APIException('该产品当前状态不可检验')
if 'is_testok' not in vdata:
raise exceptions.APIException('未填写检测结论')
vdata = serializer.data
wproducts = WProduct.objects.filter(pk__in=[x for x in vdata['wproducts']])
warehouse = WareHouse.objects.get(id=vdata['warehouse'])
for i in wproducts:
if i.act_state is not WProduct.WPR_ACT_STATE_OK:
raise exceptions.APIException('存在不可入库半成品')
# 聚合一下
wproducts_a = wproducts.values('subproduction_plan', 'material', 'subproduction_plan__number').annotate(total=Count('id'))
# 创建入库记录
remark = vdata.get('remark', '')
fifo = FIFO.objects.create(type=FIFO.FIFO_TYPE_DO_IN,
is_audited=True, auditor=request.user, inout_date=timezone.now(), create_by=request.user, remark=remark)
# 创建入库明细
for i in wproducts_a:
spi = SubProductionPlan.objects.get(pk=i['subproduction_plan'])
fifoitem = FIFOItem()
fifoitem.is_tested = True
fifoitem.is_testok = True
fifoitem.warehouse = warehouse
fifoitem.material = Material.objects.get(pk=i['material'])
fifoitem.count = i['total']
fifoitem.batch = spi.number
fifoitem.fifo = fifo
fifoitem.subproduction_plan = spi
fifoitem.save()
obj = serializer.save(create_by = self.request.user,
material=wproduct.material, number=wproduct.number, subproduction_plan=wproduct.subproduction_plan)
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 None
m['test_record'] = obj
tris.append(TestRecordItem(**m))
TestRecordItem.objects.bulk_create(tris)
# 如果检测合格, 变更动态产品进行状态
if obj.is_testok:
wproduct.act_state = WProduct.WPR_ACT_STATE_DOING
wproduct.save()
else:# 如果不合格
wproduct.act_state = WProduct.WPR_ACT_STATE_NOTOK
wproduct.save()
wproducts_items = wproducts.filter(subproduction_plan=i['subproduction_plan'], material=i['material'])
ips = []
for i in wproducts_items:
# 创建入库明细半成品
ip = {}
ip['fifoitem'] = fifoitem
ip['wproduct'] = i
ip['number'] = i.number
ip['material'] = i.material
ips.append(FIFOItemProduct(**ip))
FIFOItemProduct.objects.bulk_create(ips)
# 更新库存并修改半成品进行状态
update_inm(fifo)
wproducts.update(act_state=WProduct.WPR_ACT_STATE_INM, warehouse=warehouse, update_by=request.user)
return Response()
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=WproductPutInSerializer)
@ -340,6 +375,8 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd
instance = self.get_object()
if instance.is_submited:
raise exceptions.APIException('该操作已提交')
# 恢复半成品可操作
instance.wp_operation.all().update(act_state=WProduct.WPR_ACT_STATE_DOWAIT)
self.perform_destroy(instance)
return Response(status=status.HTTP_204_NO_CONTENT)
@ -359,7 +396,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd
# 创建操作所用半成品关联记录
if 'wproducts' in vdata:
owps = []
WProduct.objects.filter(pk__in=[x.id for x in vdata['wproducts']]).update(operation=op)
WProduct.objects.filter(pk__in=[x.id for x in vdata['wproducts']]).update(operation=op, act_state=WProduct.WPR_ACT_STATE_DOING)
splans = WpmServies.get_subplans_queryset_from_wproducts(vdata['wproducts'])
for wpd in vdata['wproducts']:
owp = {}
@ -439,9 +476,8 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd
wp.step = newstep
wp.pre_step = step
if hasNext:
wp.is_executed= False
wp.act_state= WProduct.WPR_ACT_STATE_DOWAIT
else:
wp.is_executed = True
wp.act_state = WProduct.WPR_ACT_STATE_TOTEST
wp.material = wsp.main_product
# 更新子计划进度
@ -457,7 +493,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd
if i.subproduction_progress.is_main:
newstep, _ = WpmServies.get_next_step(i.subproduction_plan, step)
wpr = dict(material=i.material, step=newstep,
act_state=WProduct.WPR_ACT_STATE_DOING, is_executed=False, remark='',
act_state=WProduct.WPR_ACT_STATE_DOWAIT, remark='',
subproduction_plan=i.subproduction_plan)
for x in range(i.count):
WProduct.objects.create(**wpr)
@ -469,11 +505,9 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd
wproduct.step = newstep
wproduct.subproduction_plan = i.subproduction_plan
if hasNext:
wproduct.act_state = WProduct.WPR_ACT_STATE_DOING
wproduct.is_executed = False
wproduct.act_state = WProduct.WPR_ACT_STATE_DOWAIT
else:
wproduct.act_state = WProduct.WPR_ACT_STATE_TOTEST
wproduct.is_executed = True
# 更新子计划进度
instance = SubProductionProgress.objects.get(subproduction_plan=i.subproduction_plan,
is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT)
@ -782,7 +816,7 @@ class DoFormSubmit(CreateAPIView, GenericAPIView):
if vdata['step'].type == Step.STEP_TYPE_DIV:
newstep, _ = WpmServies.get_next_step(i['subproduction_plan'], vdata['step'])
wpr = dict(material=ma, step=newstep,
act_state=WProduct.WPR_ACT_STATE_DOING, is_executed=False, remark='',
act_state=WProduct.WPR_ACT_STATE_DOWAIT, remark='',
subproduction_plan=i['subproduction_plan'])
for x in range(i['count_output']):
WProduct.objects.create(**wpr)
@ -814,11 +848,9 @@ class DoFormSubmit(CreateAPIView, GenericAPIView):
wproduct.subproduction_plan=vdata['subproduction_plan']
wproduct.parent = data['wproducts']
if hasNext:
wproduct.act_state=WProduct.WPR_ACT_STATE_DOING
wproduct.is_executed=False
wproduct.act_state=WProduct.WPR_ACT_STATE_DOWAIT
else:
wproduct.act_state=WProduct.WPR_ACT_STATE_TOTEST
wproduct.is_executed=True
wproduct.save()
else:
raise exceptions.APIException('请指定子计划')
@ -829,9 +861,8 @@ class DoFormSubmit(CreateAPIView, GenericAPIView):
wproduct.step = newstep
wproduct.pre_step=vdata['step']
if hasNext:
wproduct.is_executed= False
wproduct.act_state=WProduct.WPR_ACT_STATE_DOWAIT
else:
wproduct.is_executed= True
wproduct.act_state=WProduct.WPR_ACT_STATE_TOTEST
wproduct.material=wproduct.subproduction_plan.main_product
wproduct.save()