Merge branch 'develop' of https://e.coding.net/ctcdevteam/hberp/hberp into develop
This commit is contained in:
commit
1f83b26fa5
|
|
@ -22,6 +22,7 @@
|
||||||
"compression-webpack-plugin": "^5.0.1",
|
"compression-webpack-plugin": "^5.0.1",
|
||||||
"d3": "^5.14.2",
|
"d3": "^5.14.2",
|
||||||
"dagre-d3": "^0.6.4",
|
"dagre-d3": "^0.6.4",
|
||||||
|
"dhtmlx-gantt": "^6.2.1",
|
||||||
"element-ui": "^2.15.5",
|
"element-ui": "^2.15.5",
|
||||||
"file-saver": "^2.0.2",
|
"file-saver": "^2.0.2",
|
||||||
"fuse.js": "^6.4.6",
|
"fuse.js": "^6.4.6",
|
||||||
|
|
@ -30,6 +31,7 @@
|
||||||
"nprogress": "0.2.0",
|
"nprogress": "0.2.0",
|
||||||
"path-to-regexp": "^6.2.0",
|
"path-to-regexp": "^6.2.0",
|
||||||
"vue": "^2.6.14",
|
"vue": "^2.6.14",
|
||||||
|
"vue-function-api": "^2.1.2",
|
||||||
"vue-json-editor": "^1.4.3",
|
"vue-json-editor": "^1.4.3",
|
||||||
"vue-quill-editor": "^3.0.6",
|
"vue-quill-editor": "^3.0.6",
|
||||||
"vue-router": "^3.5.2",
|
"vue-router": "^3.5.2",
|
||||||
|
|
|
||||||
|
|
@ -227,7 +227,7 @@ export const asyncRoutes = [
|
||||||
path: 'need',
|
path: 'need',
|
||||||
name: 'need',
|
name: 'need',
|
||||||
component: () => import('@/views/wpm/need'),
|
component: () => import('@/views/wpm/need'),
|
||||||
meta: { title: '半成品检验', icon: 'example', perms: ['index_manage'] }
|
meta: { title: '过程检验', icon: 'example', perms: ['index_manage'] }
|
||||||
}
|
}
|
||||||
,
|
,
|
||||||
{
|
{
|
||||||
|
|
@ -320,15 +320,15 @@ export const asyncRoutes = [
|
||||||
{
|
{
|
||||||
path: '/qm',
|
path: '/qm',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: '/qm/standard',
|
redirect: '/qm/product',
|
||||||
name: 'qm',
|
name: 'qm',
|
||||||
meta: { title: '质量管理', icon: 'example', perms: ['equipment_set'] },
|
meta: { title: '质量管理', icon: 'example', perms: ['equipment_set'] },
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'standard',
|
path: 'product',
|
||||||
name: 'standard',
|
name: 'product',
|
||||||
component: () => import('@/views/qm/standard'),
|
component: () => import('@/views/qm/product'),
|
||||||
meta: { title: '标准', icon: 'example', perms: ['index_manage'] }
|
meta: { title: '军检', icon: 'example', perms: ['index_manage'] }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'testitem',
|
path: 'testitem',
|
||||||
|
|
|
||||||
|
|
@ -45,21 +45,7 @@
|
||||||
<el-tag v-else-if="scope.row.is_mtestok == true">合格</el-tag></template>
|
<el-tag v-else-if="scope.row.is_mtestok == true">合格</el-tag></template>
|
||||||
|
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="操作"
|
|
||||||
width="220px"
|
|
||||||
>
|
|
||||||
<template slot-scope="scope">
|
|
||||||
|
|
||||||
<el-link
|
|
||||||
v-if="scope.row.is_mtested == false"
|
|
||||||
@click="handleMtest(scope)"
|
|
||||||
>军检</el-link
|
|
||||||
>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
</el-table>
|
||||||
<pagination
|
<pagination
|
||||||
v-show="iproductData.count > 0"
|
v-show="iproductData.count > 0"
|
||||||
|
|
@ -68,34 +54,7 @@
|
||||||
:limit.sync="listQuery.page_size"
|
:limit.sync="listQuery.page_size"
|
||||||
@pagination="getList"
|
@pagination="getList"
|
||||||
/>
|
/>
|
||||||
<el-dialog
|
|
||||||
:visible.sync="dialogVisible"
|
|
||||||
:close-on-click-modal="false"
|
|
||||||
title="军检"
|
|
||||||
>
|
|
||||||
<el-form
|
|
||||||
ref="Form"
|
|
||||||
:model="mtest"
|
|
||||||
label-width="150px"
|
|
||||||
label-position="right"
|
|
||||||
|
|
||||||
>
|
|
||||||
<el-form-item label="军检是否合格">
|
|
||||||
|
|
||||||
<el-radio v-model="mtest.is_mtestok" label=True >合格</el-radio>
|
|
||||||
<el-radio v-model="mtest.is_mtestok" label=False >不合格</el-radio>
|
|
||||||
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="备注" prop="remark">
|
|
||||||
<el-input v-model="mtest.remark" placeholder="备注" />
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
</el-form>
|
|
||||||
<div style="text-align: right">
|
|
||||||
<el-button type="danger" @click="dialogVisible = false">取消</el-button>
|
|
||||||
<el-button type="primary" @click="smtconfirm('Form')">确认</el-button>
|
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -136,6 +95,7 @@ export default {
|
||||||
//半成品列表
|
//半成品列表
|
||||||
getList() {
|
getList() {
|
||||||
this.listLoading = true;
|
this.listLoading = true;
|
||||||
|
this.listQuery.material__type=1;
|
||||||
getiproductList(this.listQuery).then((response) => {
|
getiproductList(this.listQuery).then((response) => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
this.iproductData= response.data;
|
this.iproductData= response.data;
|
||||||
|
|
@ -147,18 +107,7 @@ export default {
|
||||||
this.saleproduct=scope.row.id;
|
this.saleproduct=scope.row.id;
|
||||||
this.dialogVisible=true;
|
this.dialogVisible=true;
|
||||||
},
|
},
|
||||||
smtconfirm(){
|
|
||||||
|
|
||||||
saleMtest(this.saleproduct,this.mtest).then((res) => {
|
|
||||||
|
|
||||||
if (res.code >= 200) {
|
|
||||||
this.getList();
|
|
||||||
this.dialogVisible = false;
|
|
||||||
this.$message.success("成功");
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ export default {
|
||||||
//半成品列表
|
//半成品列表
|
||||||
getList() {
|
getList() {
|
||||||
this.listLoading = true;
|
this.listLoading = true;
|
||||||
|
this.listQuery.material__type=2;
|
||||||
getiproductList(this.listQuery).then((response) => {
|
getiproductList(this.listQuery).then((response) => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
this.iproductData= response.data;
|
this.iproductData= response.data;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,165 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
|
||||||
|
<el-card style="margin-top: 2px">
|
||||||
|
<el-table
|
||||||
|
v-loading="listLoading"
|
||||||
|
:data="iproductData.results"
|
||||||
|
border
|
||||||
|
fit
|
||||||
|
stripe
|
||||||
|
highlight-current-row
|
||||||
|
max-height="700"
|
||||||
|
height="100"
|
||||||
|
v-el-height-adaptive-table="{bottomOffset: 50}"
|
||||||
|
>
|
||||||
|
<el-table-column type="index" width="50" />
|
||||||
|
<el-table-column label="成品编号">
|
||||||
|
<template slot-scope="scope">{{ scope.row.number }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="成品批次">
|
||||||
|
<template slot-scope="scope">{{ scope.row.batch }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="成品名称">
|
||||||
|
<template slot-scope="scope">{{ scope.row.material_.name }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="所在仓库">
|
||||||
|
<template slot-scope="scope">{{ scope.row.warehouse_.name }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="是否已军检">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
|
||||||
|
|
||||||
|
<el-tag v-if="scope.row.is_mtested == false">未军检</el-tag>
|
||||||
|
<el-tag v-else>已军检</el-tag></template>
|
||||||
|
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="军检">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
|
||||||
|
|
||||||
|
<el-tag v-if="scope.row.is_mtestok == false">不合格</el-tag>
|
||||||
|
<el-tag v-else-if="scope.row.is_mtestok == true">合格</el-tag></template>
|
||||||
|
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
align="center"
|
||||||
|
label="操作"
|
||||||
|
width="220px"
|
||||||
|
>
|
||||||
|
<template slot-scope="scope">
|
||||||
|
|
||||||
|
<el-link
|
||||||
|
v-if="scope.row.is_mtested == false"
|
||||||
|
@click="handleMtest(scope)"
|
||||||
|
>军检</el-link
|
||||||
|
>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<pagination
|
||||||
|
v-show="iproductData.count > 0"
|
||||||
|
:total="iproductData.count"
|
||||||
|
:page.sync="listQuery.page"
|
||||||
|
:limit.sync="listQuery.page_size"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
<el-dialog
|
||||||
|
:visible.sync="dialogVisible"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
title="军检"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="Form"
|
||||||
|
:model="mtest"
|
||||||
|
label-width="150px"
|
||||||
|
label-position="right"
|
||||||
|
|
||||||
|
>
|
||||||
|
<el-form-item label="军检是否合格">
|
||||||
|
|
||||||
|
<el-radio v-model="mtest.is_mtestok" label=True >合格</el-radio>
|
||||||
|
<el-radio v-model="mtest.is_mtestok" label=False >不合格</el-radio>
|
||||||
|
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注" prop="remark">
|
||||||
|
<el-input v-model="mtest.remark" placeholder="备注" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
</el-form>
|
||||||
|
<div style="text-align: right">
|
||||||
|
<el-button type="danger" @click="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="smtconfirm('Form')">确认</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { getiproductList,saleMtest} from "@/api/inm";
|
||||||
|
import checkPermission from "@/utils/permission";
|
||||||
|
|
||||||
|
import { genTree } from "@/utils";
|
||||||
|
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { Pagination },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
|
||||||
|
iproductData: {
|
||||||
|
count: 0,
|
||||||
|
},
|
||||||
|
listQuery: {
|
||||||
|
page: 1,
|
||||||
|
page_size: 20,
|
||||||
|
},
|
||||||
|
mtest: {},
|
||||||
|
salesdetail:"",
|
||||||
|
saleproduct:"",
|
||||||
|
dialogVisible:false,
|
||||||
|
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {},
|
||||||
|
watch: {},
|
||||||
|
created() {
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
checkPermission,
|
||||||
|
//半成品列表
|
||||||
|
getList() {
|
||||||
|
this.listLoading = true;
|
||||||
|
this.listQuery.type=1;
|
||||||
|
getiproductList(this.listQuery).then((response) => {
|
||||||
|
if (response.data) {
|
||||||
|
this.iproductData= response.data;
|
||||||
|
}
|
||||||
|
this.listLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleMtest(scope){
|
||||||
|
this.saleproduct=scope.row.id;
|
||||||
|
this.dialogVisible=true;
|
||||||
|
},
|
||||||
|
smtconfirm(){
|
||||||
|
|
||||||
|
saleMtest(this.saleproduct,this.mtest).then((res) => {
|
||||||
|
|
||||||
|
if (res.code >= 200) {
|
||||||
|
this.getList();
|
||||||
|
this.dialogVisible = false;
|
||||||
|
this.$message.success("成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
@ -143,10 +143,48 @@
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="选择产品" prop="iproducts">
|
<el-form-item label="选择产品" prop="iproducts">
|
||||||
<div class="trdiv">
|
|
||||||
<el-transfer v-model="sale.iproducts" :data="iproductoptions" :titles="['未选产品', '已选产品']" right-check-change="change"></el-transfer>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<el-table
|
||||||
|
v-loading="listLoading"
|
||||||
|
:data="iproductoptions"
|
||||||
|
border
|
||||||
|
fit
|
||||||
|
stripe
|
||||||
|
highlight-current-row
|
||||||
|
ref="multipleTable"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
type="selection"
|
||||||
|
width="55">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column type="index" width="50" />
|
||||||
|
<el-table-column label="成品编号">
|
||||||
|
<template slot-scope="scope">{{ scope.row.number }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="成品批次">
|
||||||
|
<template slot-scope="scope">{{ scope.row.batch }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="成品名称">
|
||||||
|
<template slot-scope="scope">{{ scope.row.material_.name }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="所在仓库">
|
||||||
|
<template slot-scope="scope">{{ scope.row.warehouse_.name }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="是否已军检">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
|
||||||
|
|
||||||
|
<el-tag v-if="scope.row.is_mtested == false">未军检</el-tag>
|
||||||
|
<el-tag v-else>已军检</el-tag></template>
|
||||||
|
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</el-table>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
|
|
@ -183,10 +221,11 @@ export default {
|
||||||
page: 1,
|
page: 1,
|
||||||
page_size: 20,
|
page_size: 20,
|
||||||
},
|
},
|
||||||
|
iproducts:[],
|
||||||
orderoptions:[],
|
orderoptions:[],
|
||||||
customeroptions:[],
|
customeroptions:[],
|
||||||
materialoptions:[],
|
materialoptions:[],
|
||||||
iproductoptions:[],
|
iproductoptions:"",
|
||||||
dialogVisible: false,
|
dialogVisible: false,
|
||||||
dialogType: "new",
|
dialogType: "new",
|
||||||
|
|
||||||
|
|
@ -221,20 +260,13 @@ export default {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
this.sale.customer = response.data.customer;
|
this.sale.customer = response.data.customer;
|
||||||
this.sale.product = response.data.product;
|
this.sale.product = response.data.product;
|
||||||
this.iproductoptions=[];
|
getiproductList({page:0,material__type:1,material:this.sale.product}).then((response) => {
|
||||||
getiproductList({material: this.sale.product,page:0}).then((response) => {
|
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
response.data.forEach((item) => {
|
this.iproductoptions=response.data;
|
||||||
this.iproductoptions.push({
|
|
||||||
label: item.number+"__"+item.material_.name+"__"+(item.is_mtested==true?"已军检":"未军检") ,
|
|
||||||
key: item.id
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
@ -270,39 +302,27 @@ export default {
|
||||||
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
selectproduct(selval)
|
|
||||||
{
|
|
||||||
this.iproductoptions=[];
|
|
||||||
getiproductList({material:selval,page:0}).then((response) => {
|
|
||||||
if (response.data) {
|
|
||||||
response.data.forEach((item) => {
|
|
||||||
this.iproductoptions.push({
|
|
||||||
label: item.number+"__"+item.material_.name+"__"+(item.is_mtested==true?"已军检":"未军检") ,
|
|
||||||
key: item.id
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
//成品
|
//成品
|
||||||
getproductList() {
|
getproductList() {
|
||||||
this.iproductoptions=[],
|
|
||||||
getiproductList({page:0}).then((response) => {
|
getiproductList({page:0,material__type:1}).then((response) => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
response.data.forEach((item) => {
|
this.iproductoptions=response.data;
|
||||||
this.iproductoptions.push({
|
|
||||||
label: item.number+"__"+item.material_.name+"__"+(item.is_mtested==true?"已军检":"未军检") ,
|
|
||||||
key: item.id
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
//根据选择的产品弹出对应库里的产品
|
||||||
|
selectproduct(){
|
||||||
|
getiproductList({page:0,material__type:1,material:this.sale.product}).then((response) => {
|
||||||
|
if (response.data) {
|
||||||
|
this.iproductoptions=response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
handleFilter() {
|
handleFilter() {
|
||||||
|
|
@ -320,7 +340,7 @@ export default {
|
||||||
this.sale = Object.assign({}, defaulteSale);
|
this.sale = Object.assign({}, defaulteSale);
|
||||||
this.dialogType = "new";
|
this.dialogType = "new";
|
||||||
this.dialogVisible = true;
|
this.dialogVisible = true;
|
||||||
this.iproductoptions=[];
|
this.iproducts=[];
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.$refs["Form"].clearValidate();
|
this.$refs["Form"].clearValidate();
|
||||||
});
|
});
|
||||||
|
|
@ -364,6 +384,17 @@ export default {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
this.$refs.multipleTable.selection.forEach((item) => {
|
||||||
|
this.iproducts.push(
|
||||||
|
item.id
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.sale.iproducts=this.iproducts;
|
||||||
createSale(this.sale).then((res) => {
|
createSale(this.sale).then((res) => {
|
||||||
if (res.code >= 200) {
|
if (res.code >= 200) {
|
||||||
this.getList();
|
this.getList();
|
||||||
|
|
|
||||||
|
|
@ -14,4 +14,4 @@ class IProductFilterSet(filters.FilterSet):
|
||||||
order = filters.NumberFilter(field_name="wproduct__subproduction_plan__production_plan__order")
|
order = filters.NumberFilter(field_name="wproduct__subproduction_plan__production_plan__order")
|
||||||
class Meta:
|
class Meta:
|
||||||
model = IProduct
|
model = IProduct
|
||||||
fields = ['material', 'warehouse', 'batch', 'order', 'is_mtested', 'is_mtestok']
|
fields = ['material', 'warehouse', 'batch', 'order', 'is_mtested', 'is_mtestok', 'material__type']
|
||||||
|
|
@ -75,8 +75,8 @@ class FIFOItem(BaseModel):
|
||||||
"""
|
"""
|
||||||
出入库详细条目
|
出入库详细条目
|
||||||
"""
|
"""
|
||||||
is_tested = models.BooleanField('是否已检测', default=False)
|
is_tested = models.BooleanField('是否已检验', default=False)
|
||||||
is_testok = models.BooleanField('是否检测合格', default=False)
|
is_testok = models.BooleanField('是否检验合格', default=False)
|
||||||
warehouse = models.ForeignKey(WareHouse, on_delete=models.CASCADE, verbose_name='仓库')
|
warehouse = models.ForeignKey(WareHouse, on_delete=models.CASCADE, verbose_name='仓库')
|
||||||
material = models.ForeignKey(Material, verbose_name='物料类型', on_delete=models.CASCADE)
|
material = models.ForeignKey(Material, verbose_name='物料类型', on_delete=models.CASCADE)
|
||||||
count = models.PositiveIntegerField('数量', default=0)
|
count = models.PositiveIntegerField('数量', default=0)
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ class InventoryViewSet(ListModelMixin, GenericViewSet):
|
||||||
仓库物料表
|
仓库物料表
|
||||||
"""
|
"""
|
||||||
perms_map = {'*': '*'}
|
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
|
serializer_class = InventorySerializer
|
||||||
filterset_fields = ['material', 'warehouse']
|
filterset_fields = ['material', 'warehouse']
|
||||||
search_fields = []
|
search_fields = []
|
||||||
|
|
@ -48,7 +48,7 @@ class InventoryViewSet(ListModelMixin, GenericViewSet):
|
||||||
|
|
||||||
class MaterialBatchViewSet(ListModelMixin, GenericViewSet):
|
class MaterialBatchViewSet(ListModelMixin, GenericViewSet):
|
||||||
perms_map = {'*': '*'}
|
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
|
serializer_class = MaterialBatchSerializer
|
||||||
# filterset_fields = ['material', 'warehouse']
|
# filterset_fields = ['material', 'warehouse']
|
||||||
filterset_class = MbFilterSet
|
filterset_class = MbFilterSet
|
||||||
|
|
@ -87,14 +87,14 @@ class FIFOItemViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
|
||||||
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=InmTestRecordCreateSerializer)
|
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=InmTestRecordCreateSerializer)
|
||||||
def test(self, request, pk=None):
|
def test(self, request, pk=None):
|
||||||
"""
|
"""
|
||||||
检测
|
检验
|
||||||
"""
|
"""
|
||||||
serializer = InmTestRecordCreateSerializer(data=request.data)
|
serializer = InmTestRecordCreateSerializer(data=request.data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
vdata = serializer.validated_data
|
vdata = serializer.validated_data
|
||||||
record_data = vdata.pop('record_data')
|
record_data = vdata.pop('record_data')
|
||||||
if 'is_testok' not in vdata:
|
if 'is_testok' not in vdata:
|
||||||
raise APIException('未填写检测结论')
|
raise APIException('未填写检验结论')
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
obj = serializer.save(create_by = self.request.user)
|
obj = serializer.save(create_by = self.request.user)
|
||||||
tris = []
|
tris = []
|
||||||
|
|
@ -111,7 +111,7 @@ class FIFOItemViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
|
||||||
tris.append(TestRecordItem(**m))
|
tris.append(TestRecordItem(**m))
|
||||||
TestRecordItem.objects.bulk_create(tris)
|
TestRecordItem.objects.bulk_create(tris)
|
||||||
|
|
||||||
# 如果检测合格
|
# 如果检验合格
|
||||||
if obj.fifo_item:
|
if obj.fifo_item:
|
||||||
obj.fifo_item.is_testok = True if obj.is_testok else False
|
obj.fifo_item.is_testok = True if obj.is_testok else False
|
||||||
obj.fifo_item.is_tested = True
|
obj.fifo_item.is_tested = True
|
||||||
|
|
|
||||||
|
|
@ -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='类型'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -143,6 +143,7 @@ class RecordFormField(CommonAModel):
|
||||||
FIELD_SELECTS = 'selects'
|
FIELD_SELECTS = 'selects'
|
||||||
FIELD_TEXTAREA = 'textarea'
|
FIELD_TEXTAREA = 'textarea'
|
||||||
FIELD_DRAW = 'draw'
|
FIELD_DRAW = 'draw'
|
||||||
|
FIELD_TABLE = 'table'
|
||||||
FIELD_FROMSYSTEM = 'fromsystem'
|
FIELD_FROMSYSTEM = 'fromsystem'
|
||||||
field_type_choices = (
|
field_type_choices = (
|
||||||
('string', '字符串'),
|
('string', '字符串'),
|
||||||
|
|
@ -157,6 +158,7 @@ class RecordFormField(CommonAModel):
|
||||||
('select', '单选下拉'),
|
('select', '单选下拉'),
|
||||||
('selects', '多选下拉'),
|
('selects', '多选下拉'),
|
||||||
('textarea', '文本域'),
|
('textarea', '文本域'),
|
||||||
|
('table', '表格'),
|
||||||
('draw', '绘图'),
|
('draw', '绘图'),
|
||||||
)
|
)
|
||||||
high_rule_choices = (
|
high_rule_choices = (
|
||||||
|
|
|
||||||
|
|
@ -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='关联主生产计划'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -14,12 +14,19 @@ class ProductionPlan(CommonAModel):
|
||||||
"""
|
"""
|
||||||
生产计划
|
生产计划
|
||||||
"""
|
"""
|
||||||
|
# PLAN_STATE_WAIT = 6
|
||||||
|
# PLAN_STATE_WORKING = 10
|
||||||
|
# PLAN_STATE_DONE = 20
|
||||||
|
# state_choices = (
|
||||||
|
# (PLAN_STATE_WORKING, '进行中'),
|
||||||
|
# (PLAN_STATE_DONE, '已完成')
|
||||||
|
# )
|
||||||
number = models.CharField('编号', max_length=50, unique=True)
|
number = models.CharField('编号', max_length=50, unique=True)
|
||||||
order = models.ForeignKey(Order, verbose_name='关联订单', null=True, blank=True, on_delete=models.SET_NULL)
|
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)
|
product = models.ForeignKey(Material, verbose_name='生产产品', on_delete=models.CASCADE)
|
||||||
count = models.IntegerField('生产数量', default=1)
|
count = models.PositiveIntegerField('生产数量', default=1)
|
||||||
count_real = models.IntegerField('实际产出数', default=0)
|
count_real = models.PositiveIntegerField('实际产出数', default=0)
|
||||||
count_ok = models.IntegerField('合格数', default=0)
|
count_ok = models.PositiveIntegerField('合格数', default=0)
|
||||||
start_date = models.DateField('计划开工日期')
|
start_date = models.DateField('计划开工日期')
|
||||||
end_date = models.DateField('计划完工日期')
|
end_date = models.DateField('计划完工日期')
|
||||||
is_planed = models.BooleanField('是否已排产', default=False)
|
is_planed = models.BooleanField('是否已排产', default=False)
|
||||||
|
|
@ -47,7 +54,7 @@ class SubProductionPlan(CommonAModel):
|
||||||
(SUBPLAN_STATE_DONE, '已完成')
|
(SUBPLAN_STATE_DONE, '已完成')
|
||||||
)
|
)
|
||||||
number = models.CharField('子计划编号', max_length=50, unique=True, null=True, blank=True)
|
number = models.CharField('子计划编号', max_length=50, unique=True, null=True, blank=True)
|
||||||
production_plan = models.ForeignKey(ProductionPlan, verbose_name='关联主生产计划', on_delete=models.CASCADE)
|
production_plan = models.ForeignKey(ProductionPlan, verbose_name='关联主生产计划', on_delete=models.CASCADE, related_name='subplan_plan')
|
||||||
subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE, related_name='subplan_subprod')
|
subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE, related_name='subplan_subprod')
|
||||||
start_date = models.DateField('计划开工日期')
|
start_date = models.DateField('计划开工日期')
|
||||||
end_date = models.DateField('计划完工日期')
|
end_date = models.DateField('计划完工日期')
|
||||||
|
|
@ -56,9 +63,9 @@ class SubProductionPlan(CommonAModel):
|
||||||
process = models.ForeignKey(Process, verbose_name='关联大工序', on_delete=models.CASCADE)
|
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_product = models.ForeignKey(Material, verbose_name='主要产品', on_delete=models.CASCADE, null=True, blank=True)
|
||||||
main_count = models.IntegerField('应产出数', default=0)
|
main_count = models.PositiveIntegerField('应产出数', default=0)
|
||||||
main_count_real = models.IntegerField('实际产出数', default=0)
|
main_count_real = models.PositiveIntegerField('实际产出数', default=0)
|
||||||
main_count_ok = models.IntegerField('合格数', default=0)
|
main_count_ok = models.PositiveIntegerField('合格数', default=0)
|
||||||
|
|
||||||
steps = models.JSONField('工艺步骤', default=list)
|
steps = models.JSONField('工艺步骤', default=list)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ def update_subplan_main(sender, instance, created, **kwargs):
|
||||||
if subplan.main_product.type == Material.MA_TYPE_GOOD:
|
if subplan.main_product.type == Material.MA_TYPE_GOOD:
|
||||||
# 如果是产品,更新主计划进度
|
# 如果是产品,更新主计划进度
|
||||||
plan = subplan.production_plan
|
plan = subplan.production_plan
|
||||||
plan.count_real = subplan.count_real
|
plan.count_real = subplan.main_count_real
|
||||||
plan.count_ok = subplan.count_ok
|
plan.count_ok = subplan.main_count_ok
|
||||||
plan.save()
|
plan.save()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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='关联的动态产品'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -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='是否提交'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -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='是否隐藏'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.enums import Choices
|
from django.db.models.enums import Choices
|
||||||
from apps.mtm.models import RecordForm, RecordFormField
|
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
|
from utils.model import BaseModel
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
class Standard(CommonAModel):
|
class Standard(CommonAModel):
|
||||||
|
|
@ -43,7 +43,7 @@ class AnalysisItem(CommonAModel):
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = '检验分析项'
|
verbose_name = '检验分析项'
|
||||||
|
|
||||||
class TestRecord(CommonAModel):
|
class TestRecord(CommonADModel):
|
||||||
"""
|
"""
|
||||||
检验记录
|
检验记录
|
||||||
"""
|
"""
|
||||||
|
|
@ -70,6 +70,7 @@ class TestRecord(CommonAModel):
|
||||||
subproduction_plan = models.ForeignKey('pm.subproductionplan', 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)
|
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)
|
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='')
|
remark = models.TextField('备注', default='')
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -77,13 +78,12 @@ class TestRecordItem(BaseModel):
|
||||||
"""
|
"""
|
||||||
记录表格字段值
|
记录表格字段值
|
||||||
"""
|
"""
|
||||||
form_field = models.ForeignKey(RecordFormField, verbose_name='关联字段', on_delete=models.CASCADE, db_constraint=False)
|
form_field = models.ForeignKey(RecordFormField, verbose_name='关联自定义表格字段', on_delete=models.CASCADE, db_constraint=False)
|
||||||
field_name = models.CharField('字段名', max_length=50)
|
field_name = models.CharField('字段名', max_length=50)
|
||||||
field_key = models.CharField('字段标识', max_length=50)
|
field_key = models.CharField('字段标识', max_length=50)
|
||||||
field_type = models.CharField('字段类型', choices=RecordForm.type_choices, max_length=50)
|
field_type = models.CharField('字段类型', choices=RecordForm.type_choices, max_length=50)
|
||||||
field_value = models.JSONField('录入值', default=dict, blank=True)
|
field_value = models.JSONField('录入值', default=dict, blank=True)
|
||||||
need_judge = models.BooleanField('是否需要判定', default=False)
|
is_hidden = models.BooleanField('是否隐藏', default=False)
|
||||||
sort = models.IntegerField('排序号', default=1)
|
|
||||||
is_testok = models.BooleanField('是否合格', null=True, blank=True)
|
is_testok = models.BooleanField('是否合格', null=True, blank=True)
|
||||||
is_testok_robot = 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')
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from apps.mtm.models import RecordForm, RecordFormField
|
from apps.mtm.models import RecordForm, RecordFormField
|
||||||
from apps.mtm.serializers import RecordFormFieldSerializer, RecordFormSimpleSerializer
|
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 .models import Standard, TestItem, TestRecord, TestRecordItem
|
||||||
|
from django.db import transaction
|
||||||
|
|
||||||
class StandardCreateUpdateSerializer(serializers.ModelSerializer):
|
class StandardCreateUpdateSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -38,9 +39,21 @@ class AnalysisItemSerializer(serializers.ModelSerializer):
|
||||||
class TestRecordItemCreateSerializer(serializers.ModelSerializer):
|
class TestRecordItemCreateSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = TestRecordItem
|
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):
|
class TestRecordItemSerializer(serializers.ModelSerializer):
|
||||||
|
need_judge = serializers.BooleanField(source='form_field.need_judge', 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:
|
class Meta:
|
||||||
model = TestRecordItem
|
model = TestRecordItem
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
@ -52,28 +65,67 @@ class TestRecordCreateSerializer(serializers.ModelSerializer):
|
||||||
fields = ['form', 'record_data', 'is_testok', 'fifo_item']
|
fields = ['form', 'record_data', 'is_testok', 'fifo_item']
|
||||||
|
|
||||||
class TestRecordListSerializer(serializers.ModelSerializer):
|
class TestRecordListSerializer(serializers.ModelSerializer):
|
||||||
|
form_ = RecordFormSimpleSerializer(source='form', read_only=True)
|
||||||
|
create_by_ = UserSimpleSerializer(source='create_by', read_only=True)
|
||||||
class Meta:
|
class Meta:
|
||||||
model = TestRecord
|
model = TestRecord
|
||||||
fields = '__all__'
|
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):
|
class TestRecordDetailSerializer(serializers.ModelSerializer):
|
||||||
form_ = RecordFormSimpleSerializer(source='form', read_only=True)
|
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:
|
class Meta:
|
||||||
model = TestRecord
|
model = TestRecord
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
@staticmethod
|
def get_record_data(self, obj):
|
||||||
def setup_eager_loading(queryset):
|
return TestRecordItemSerializer(instance=obj.item_test_record.order_by('form_field__sort'), many=True).data
|
||||||
queryset = queryset.select_related('form','fifo_item')
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
# def get_record_data_(self, obj):
|
def to_representation(self, instance):
|
||||||
# record_data = obj.record_data
|
ret = super().to_representation(instance)
|
||||||
# all_fields = RecordFormField.objects.filter(form=obj.form, is_deletd=False).order_by('sort')
|
if instance.origin_test and instance.type == TestRecord.TEST_PROCESS_RE:
|
||||||
# all_fields_l = RecordFormFieldSerializer(instance=all_fields, many=True).data
|
origin_test = ret['origin_test']
|
||||||
# for i in all_fields_l:
|
o_dict = {}
|
||||||
# key = i['field_key']
|
for i in origin_test['record_data']:
|
||||||
# i['field_value'] = record_data.get(key, None)
|
o_dict[i['field_key']] = i['field_value']
|
||||||
# return all_fields_l
|
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
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
|
from rest_framework import exceptions, serializers
|
||||||
from apps.qm.serializers import StandardCreateUpdateSerializer, StandardSerializer, TestItemCreateUpdateSerializer, TestItemSerializer, TestRecordCreateSerializer, TestRecordDetailSerializer, TestRecordListSerializer
|
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 apps.qm.models import Standard, TestItem, TestRecord, TestRecordItem
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from rest_framework.viewsets import GenericViewSet, ModelViewSet
|
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.response import Response
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from django.db import transaction
|
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.
|
# Create your views here.
|
||||||
class StandardViewSet(CreateUpdateModelAMixin, ModelViewSet):
|
class StandardViewSet(CreateUpdateModelAMixin, ModelViewSet):
|
||||||
"""
|
"""
|
||||||
|
|
@ -43,12 +48,12 @@ class TestItemViewSet(CreateUpdateModelAMixin, ModelViewSet):
|
||||||
return TestItemCreateUpdateSerializer
|
return TestItemCreateUpdateSerializer
|
||||||
return TestItemSerializer
|
return TestItemSerializer
|
||||||
|
|
||||||
class TestRecordViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
|
class TestRecordViewSet(ListModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin, GenericViewSet):
|
||||||
"""
|
"""
|
||||||
检测记录
|
检验记录
|
||||||
"""
|
"""
|
||||||
perms_map = {'*': '*'}
|
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
|
serializer_class = TestRecordListSerializer
|
||||||
filterset_fields = ['wproduct', 'material', 'step', 'subproduction_plan', 'fifo_item', 'origin_test', 'type']
|
filterset_fields = ['wproduct', 'material', 'step', 'subproduction_plan', 'fifo_item', 'origin_test', 'type']
|
||||||
ordering = ['-id']
|
ordering = ['-id']
|
||||||
|
|
@ -58,15 +63,40 @@ class TestRecordViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
|
||||||
return TestRecordListSerializer
|
return TestRecordListSerializer
|
||||||
elif self.action == 'retrieve':
|
elif self.action == 'retrieve':
|
||||||
return TestRecordDetailSerializer
|
return TestRecordDetailSerializer
|
||||||
|
elif self.action == 'update':
|
||||||
|
return TestRecordUpdateSerializer
|
||||||
return super().get_serializer_class()
|
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).exists():
|
||||||
|
raise exceptions.APIException('存在未填写项目')
|
||||||
|
with transaction.atomic():
|
||||||
|
WpmServies.update_wproduct_by_test(obj, request.user)
|
||||||
|
return Response()
|
||||||
|
|
||||||
# def create(self, request, *args, **kwargs):
|
# def create(self, request, *args, **kwargs):
|
||||||
# serializer = self.get_serializer(data=request.data)
|
# serializer = self.get_serializer(data=request.data)
|
||||||
# serializer.is_valid(raise_exception=True)
|
# serializer.is_valid(raise_exception=True)
|
||||||
# vdata = serializer.validated_data
|
# vdata = serializer.validated_data
|
||||||
# record_data = vdata.pop('record_data')
|
# record_data = vdata.pop('record_data')
|
||||||
# if 'is_testok' not in vdata:
|
# if 'is_testok' not in vdata:
|
||||||
# raise APIException('未填写检测结论')
|
# raise APIException('未填写检验结论')
|
||||||
# with transaction.atomic():
|
# with transaction.atomic():
|
||||||
# obj = serializer.save(create_by = self.request.user)
|
# obj = serializer.save(create_by = self.request.user)
|
||||||
# tris = []
|
# tris = []
|
||||||
|
|
@ -83,7 +113,7 @@ class TestRecordViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
|
||||||
# tris.append(TestRecordItem(**m))
|
# tris.append(TestRecordItem(**m))
|
||||||
# TestRecordItem.objects.bulk_create(tris)
|
# TestRecordItem.objects.bulk_create(tris)
|
||||||
|
|
||||||
# # 如果检测合格
|
# # 如果检验合格
|
||||||
# if obj.fifo_item:
|
# if obj.fifo_item:
|
||||||
# obj.fifo_item.is_testok = True if obj.is_testok else False
|
# obj.fifo_item.is_testok = True if obj.is_testok else False
|
||||||
# obj.fifo_item.is_tested = True
|
# obj.fifo_item.is_tested = True
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
from rest_framework import serializers
|
||||||
|
from apps.pm.models import ProductionPlan, SubProductionPlan
|
||||||
|
from apps.mtm.serializers import ProcessSimpleSerializer
|
||||||
|
|
||||||
|
class SubplanGanttSerializer(serializers.ModelSerializer):
|
||||||
|
count = serializers.IntegerField(source='main_count')
|
||||||
|
count_real = serializers.IntegerField(source='main_count_real')
|
||||||
|
count_ok = serializers.IntegerField(source='main_count_ok')
|
||||||
|
process_ = ProcessSimpleSerializer(source='process', read_only=True)
|
||||||
|
class Meta:
|
||||||
|
model = SubProductionPlan
|
||||||
|
fields = ['id', 'number', 'start_date', 'end_date', 'count', 'count_real', 'count_ok', 'start_date_real', 'end_date_real', 'process_']
|
||||||
|
|
||||||
|
class PlanGanttSerializer(serializers.ModelSerializer):
|
||||||
|
children = serializers.SerializerMethodField()
|
||||||
|
class Meta:
|
||||||
|
model = ProductionPlan
|
||||||
|
fields = ['id', 'number', 'start_date', 'end_date', 'children', 'count', 'count_real', 'count_ok']
|
||||||
|
|
||||||
|
def get_children(self, obj):
|
||||||
|
subplans = SubProductionPlan.objects.filter(production_plan=obj).order_by('process__number')
|
||||||
|
return SubplanGanttSerializer(instance=subplans, many=True).data
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
from django.db.models import base
|
||||||
|
from rest_framework import urlpatterns
|
||||||
|
from django.urls import path, include
|
||||||
|
from rest_framework.routers import DefaultRouter
|
||||||
|
|
||||||
|
from apps.srm.views import GanttPlan
|
||||||
|
|
||||||
|
router = DefaultRouter()
|
||||||
|
urlpatterns = [
|
||||||
|
path('gantt/plan/', GanttPlan.as_view()),
|
||||||
|
path('', include(router.urls)),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
@ -1,8 +1,17 @@
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
from rest_framework import serializers
|
||||||
from rest_framework.generics import ListAPIView
|
from rest_framework.generics import ListAPIView
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from apps.pm.models import ProductionPlan, SubProductionPlan
|
||||||
|
from apps.srm.serializers import PlanGanttSerializer
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
||||||
class GanttOrder(ListAPIView):
|
class GanttPlan(ListAPIView):
|
||||||
"""
|
"""
|
||||||
订单-计划-子计划甘特图
|
计划-子计划甘特图
|
||||||
"""
|
"""
|
||||||
|
perms_map = {'get':'*'}
|
||||||
|
serializer_class = PlanGanttSerializer
|
||||||
|
queryset = ProductionPlan.objects.filter(is_deleted=False, is_planed=True).prefetch_related('subplan_plan', 'subplan_plan__process')
|
||||||
|
ordering = ['-id']
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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='工作流标识'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -13,7 +13,7 @@ class Workflow(CommonAModel):
|
||||||
工作流
|
工作流
|
||||||
"""
|
"""
|
||||||
name = models.CharField('名称', max_length=50)
|
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')
|
sn_prefix = models.CharField('流水号前缀', max_length=50, default='hb')
|
||||||
description = models.CharField('描述', max_length=200)
|
description = models.CharField('描述', max_length=200)
|
||||||
view_permission_check = models.BooleanField('查看权限校验', default=True, help_text='开启后,只允许工单的关联人(创建人、曾经的处理人)有权限查看工单')
|
view_permission_check = models.BooleanField('查看权限校验', default=True, help_text='开启后,只允许工单的关联人(创建人、曾经的处理人)有权限查看工单')
|
||||||
|
|
@ -213,18 +213,6 @@ class Ticket(CommonBModel):
|
||||||
multi_all_person = models.JSONField('全部处理的结果', default=dict, blank=True, help_text='需要当前状态处理人全部处理时实际的处理结果,json格式')
|
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):
|
class TicketFlow(BaseModel):
|
||||||
"""
|
"""
|
||||||
工单流转日志
|
工单流转日志
|
||||||
|
|
|
||||||
|
|
@ -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='关联工单'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import re
|
import re
|
||||||
|
from rest_framework import exceptions
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.base import Model
|
from django.db.models.base import Model
|
||||||
import django.utils.timezone as timezone
|
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.pm.models import ProductionPlan, SubProductionPlan, SubProductionProgress
|
||||||
from apps.qm.models import TestRecord
|
from apps.qm.models import TestRecord
|
||||||
from apps.system.models import CommonADModel, CommonAModel, CommonBModel, Organization, User, Dict, File
|
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 utils.model import SoftModel, BaseModel
|
||||||
from simple_history.models import HistoricalRecords
|
from simple_history.models import HistoricalRecords
|
||||||
from apps.mtm.models import Material, Process, RecordFormField, Step, RecordForm, SubprodctionMaterial
|
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)
|
remark = models.CharField('备注', max_length=200, null=True, blank=True)
|
||||||
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='当前子生产计划', on_delete=models.CASCADE, related_name='wproduct_subplan')
|
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='当前子生产计划', on_delete=models.CASCADE, related_name='wproduct_subplan')
|
||||||
warehouse = models.ForeignKey(WareHouse, verbose_name='所在仓库', on_delete=models.SET_NULL, null=True, blank=True)
|
warehouse = models.ForeignKey(WareHouse, verbose_name='所在仓库', on_delete=models.SET_NULL, null=True, blank=True)
|
||||||
operation = models.ForeignKey('wpm.operation', verbose_name='关联操作',
|
operation = models.ForeignKey('wpm.operation', verbose_name='当前操作',
|
||||||
on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_operation')
|
on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_operation')
|
||||||
|
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
|
@property
|
||||||
def last_process_test(self):
|
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):
|
class WprouctTicket(CommonAModel):
|
||||||
"""
|
"""
|
||||||
玻璃审批工单
|
玻璃审批工单
|
||||||
|
|
@ -72,7 +113,7 @@ class WprouctTicket(CommonAModel):
|
||||||
material = models.ForeignKey(Material, verbose_name='所在物料状态', on_delete=models.CASCADE)
|
material = models.ForeignKey(Material, verbose_name='所在物料状态', on_delete=models.CASCADE)
|
||||||
step = models.ForeignKey(Step, verbose_name='所在步骤', on_delete=models.CASCADE)
|
step = models.ForeignKey(Step, verbose_name='所在步骤', on_delete=models.CASCADE)
|
||||||
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='所在子生产计划', on_delete=models.CASCADE)
|
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):
|
class Pick(CommonADModel):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ class PickSerializer(serializers.Serializer):
|
||||||
if isLowLevel:
|
if isLowLevel:
|
||||||
iproducts = i.pop('iproducts')
|
iproducts = i.pop('iproducts')
|
||||||
i['fifo'] = fifo
|
i['fifo'] = fifo
|
||||||
i['is_testok'] = True # 默认检测合格
|
i['is_testok'] = True # 默认检验合格
|
||||||
i['subproduction_plan'] = sp
|
i['subproduction_plan'] = sp
|
||||||
fifoitem = FIFOItem.objects.create(**i)
|
fifoitem = FIFOItem.objects.create(**i)
|
||||||
# 创建再下一个层级
|
# 创建再下一个层级
|
||||||
|
|
@ -282,27 +282,29 @@ class OperationSubmitSerializer(serializers.Serializer):
|
||||||
class WpmTestRecordItemCreateSerializer(serializers.ModelSerializer):
|
class WpmTestRecordItemCreateSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = TestRecordItem
|
model = TestRecordItem
|
||||||
fields = ['form_field', 'field_value', 'is_testok']
|
fields = ['form_field', 'field_value', 'is_testok', 'is_hidden']
|
||||||
|
|
||||||
class WpmTestRecordCreateSerializer(serializers.ModelSerializer):
|
class WpmTestRecordCreateSerializer(serializers.ModelSerializer):
|
||||||
record_data = WpmTestRecordItemCreateSerializer(many=True)
|
record_data = WpmTestRecordItemCreateSerializer(many=True)
|
||||||
wproduct = serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all(), required=True)
|
wproduct = serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all(), required=True)
|
||||||
is_testok = serializers.BooleanField(required=False)
|
is_testok = serializers.BooleanField(required=False)
|
||||||
|
origin_test = serializers.PrimaryKeyRelatedField(queryset=TestRecord.objects.all(), default=None)
|
||||||
class Meta:
|
class Meta:
|
||||||
model = TestRecord
|
model = TestRecord
|
||||||
fields = ['form', 'record_data', 'is_testok', 'wproduct']
|
fields = ['form', 'record_data', 'is_testok', 'wproduct', 'is_submited', 'origin_test']
|
||||||
|
|
||||||
class WpmTestFormInitSerializer(serializers.Serializer):
|
class WpmTestFormInitSerializer(serializers.Serializer):
|
||||||
wproduct = serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all(), required=True)
|
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):
|
class WplanPutInSerializer(serializers.Serializer):
|
||||||
warehouse = serializers.PrimaryKeyRelatedField(queryset=WareHouse.objects.all(), label="仓库ID")
|
warehouse = serializers.PrimaryKeyRelatedField(queryset=WareHouse.objects.all(), label="仓库ID")
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
from typing import List
|
from typing import List
|
||||||
from apps.pm.models import SubProductionPlan
|
from apps.pm.models import SubProductionPlan, SubProductionProgress
|
||||||
from apps.mtm.models import Step, SubprodctionMaterial
|
from apps.mtm.models import Material, Step, SubprodctionMaterial
|
||||||
|
from apps.qm.models import TestRecord
|
||||||
|
from apps.system.models import User
|
||||||
from apps.wpm.models import WProduct
|
from apps.wpm.models import WProduct
|
||||||
|
from utils.tools import ranstr
|
||||||
class WpmServies(object):
|
class WpmServies(object):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -32,3 +35,32 @@ class WpmServies(object):
|
||||||
splans = SubProductionPlan.objects.filter(is_deleted=False,
|
splans = SubProductionPlan.objects.filter(is_deleted=False,
|
||||||
subproduction__usedstep_subproduction__step=step, state=SubProductionPlan.SUBPLAN_STATE_WORKING)
|
subproduction__usedstep_subproduction__step=step, state=SubProductionPlan.SUBPLAN_STATE_WORKING)
|
||||||
return splans
|
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()
|
||||||
|
|
@ -16,6 +16,7 @@ from apps.qm.serializers import TestRecordDetailSerializer
|
||||||
|
|
||||||
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
|
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
|
from apps.wf.models import Workflow
|
||||||
from apps.wpm.filters import WMaterialFilterSet
|
from apps.wpm.filters import WMaterialFilterSet
|
||||||
from apps.wpm.models import OperationEquip, OperationWproduct, Pick, PickWproduct, WMaterial, WProduct, Operation, OperationMaterial, OperationRecord, OperationRecordItem
|
from apps.wpm.models import OperationEquip, OperationWproduct, Pick, PickWproduct, WMaterial, WProduct, Operation, OperationMaterial, OperationRecord, OperationRecordItem
|
||||||
|
|
||||||
|
|
@ -193,26 +194,29 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
vdata = serializer.validated_data
|
vdata = serializer.validated_data
|
||||||
wproduct = vdata['wproduct']
|
wproduct = vdata['wproduct']
|
||||||
form = vdata.get('form', None)
|
form = vdata['form']
|
||||||
|
if wproduct.test:
|
||||||
|
raise exceptions.APIException('存在进行中检验')
|
||||||
|
|
||||||
|
data = RecordFormDetailSerializer(instance=form).data
|
||||||
# 如果是复检记录, 需要带入原数据
|
data['origin_test'] = None
|
||||||
|
data['form'] = form.id
|
||||||
|
# 如果是复检, 需要带入原数据
|
||||||
if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST:
|
if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST:
|
||||||
# 查找最近一条检验记录
|
# 查找最近一条检验记录
|
||||||
trs = wproduct.last_process_test
|
trs = wproduct.last_process_test
|
||||||
if trs:
|
if trs:
|
||||||
origin_test = TestRecordDetailSerializer(instance=trs).data
|
origin_test = TestRecordDetailSerializer(instance=trs).data
|
||||||
data = RecordFormDetailSerializer(instance=trs.form).data
|
data['origin_test_'] = origin_test
|
||||||
data['origin_test'] = origin_test
|
data['origin_test'] = origin_test.get('id', None)
|
||||||
o_dict = {}
|
o_dict = {}
|
||||||
for i in origin_test['record_data_']:
|
for i in origin_test['record_data']:
|
||||||
o_dict[i['field_key']] = i['field_value']
|
o_dict[i['field_key']] = i['field_value']
|
||||||
for i in data['form_fields']:
|
for i in data['form_fields']:
|
||||||
i['origin_value'] = o_dict[i['field_key']] if i['field_key'] in o_dict else None
|
i['origin_value'] = o_dict[i['field_key']] if i['field_key'] in o_dict else None
|
||||||
|
i['is_hidden'] = o_dict[i['is_hidden']] if i['is_hidden'] in o_dict else False
|
||||||
else:
|
else:
|
||||||
raise exceptions.APIException('原工序检验记录不存在')
|
raise exceptions.APIException('原工序检验记录不存在')
|
||||||
else:
|
|
||||||
data = RecordFormDetailSerializer(instance=form).data
|
|
||||||
|
|
||||||
# 后续加入系统自带数据
|
# 后续加入系统自带数据
|
||||||
return Response(data)
|
return Response(data)
|
||||||
|
|
@ -221,7 +225,7 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def test(self, request, pk=None):
|
def test(self, request, pk=None):
|
||||||
"""
|
"""
|
||||||
检测
|
检验记录提交
|
||||||
"""
|
"""
|
||||||
serializer = WpmTestRecordCreateSerializer(data=request.data)
|
serializer = WpmTestRecordCreateSerializer(data=request.data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
@ -232,11 +236,13 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
|
||||||
WProduct.WPR_ACT_STATE_TORETEST, WProduct.WPR_ACT_STATE_TOFINALTEST, WProduct.WPR_ACT_STATE_TOCOMBTEST]:
|
WProduct.WPR_ACT_STATE_TORETEST, WProduct.WPR_ACT_STATE_TOFINALTEST, WProduct.WPR_ACT_STATE_TOCOMBTEST]:
|
||||||
raise exceptions.APIException('该产品当前状态不可检验')
|
raise exceptions.APIException('该产品当前状态不可检验')
|
||||||
if 'is_testok' not in vdata:
|
if 'is_testok' not in vdata:
|
||||||
raise exceptions.APIException('未填写检测结论')
|
raise exceptions.APIException('未填写检验结论')
|
||||||
|
|
||||||
savedict = dict(create_by = self.request.user,
|
savedict = dict(create_by = self.request.user,
|
||||||
material=wproduct.material, number=wproduct.number, subproduction_plan=wproduct.subproduction_plan, step=wproduct.step)
|
material=wproduct.material, number=wproduct.number, subproduction_plan=wproduct.subproduction_plan, step=wproduct.step)
|
||||||
if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST:
|
if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST:
|
||||||
|
if not vdata['origin_test']:
|
||||||
|
raise exceptions.APIException('自检记录不存在')
|
||||||
savedict['type'] = TestRecord.TEST_PROCESS_RE
|
savedict['type'] = TestRecord.TEST_PROCESS_RE
|
||||||
elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOFINALTEST:
|
elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOFINALTEST:
|
||||||
savedict['type'] = TestRecord.TEST_FINAL
|
savedict['type'] = TestRecord.TEST_FINAL
|
||||||
|
|
@ -249,35 +255,18 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
|
||||||
m['field_name'] = form_field.field_name
|
m['field_name'] = form_field.field_name
|
||||||
m['field_key'] = form_field.field_key
|
m['field_key'] = form_field.field_key
|
||||||
m['field_type'] = form_field.field_type
|
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['is_testok'] = m['is_testok'] if 'is_testok' in m else None
|
||||||
|
m['is_hidden'] = m['is_hidden'] if 'is_hidden' in m else None
|
||||||
m['test_record'] = obj
|
m['test_record'] = obj
|
||||||
tris.append(TestRecordItem(**m))
|
tris.append(TestRecordItem(**m))
|
||||||
TestRecordItem.objects.bulk_create(tris)
|
TestRecordItem.objects.bulk_create(tris)
|
||||||
|
|
||||||
# 如果检测合格, 变更动态产品进行状态
|
# 如果提交检验
|
||||||
|
if obj.is_submited:
|
||||||
if obj.is_testok:
|
WpmServies.update_wproduct_by_test(obj, request.user)
|
||||||
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:
|
else:
|
||||||
wproduct.act_state = WProduct.WPR_ACT_STATE_OK
|
# 保存当前检验
|
||||||
if wproduct.number is None: # 产生半成品编号
|
wproduct.test = obj
|
||||||
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
|
wproduct.update_by = request.user
|
||||||
wproduct.save()
|
wproduct.save()
|
||||||
return Response()
|
return Response()
|
||||||
|
|
@ -379,6 +368,12 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
|
||||||
wproduct.save()
|
wproduct.save()
|
||||||
return Response()
|
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):
|
class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin, GenericViewSet):
|
||||||
"""
|
"""
|
||||||
生产操作记录
|
生产操作记录
|
||||||
|
|
@ -619,7 +614,7 @@ class OperationEquipViewSet(ListModelMixin, DestroyModelMixin, UpdateModelMixin,
|
||||||
instance.delete()
|
instance.delete()
|
||||||
return Response()
|
return Response()
|
||||||
|
|
||||||
class OperationRecordViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
|
class OperationRecordViewSet(ListModelMixin, DestroyModelMixin, UpdateModelMixin, GenericViewSet):
|
||||||
"""
|
"""
|
||||||
操作使用的自定义表格
|
操作使用的自定义表格
|
||||||
"""
|
"""
|
||||||
|
|
@ -630,6 +625,10 @@ class OperationRecordViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
|
||||||
ordering_fields = ['id']
|
ordering_fields = ['id']
|
||||||
ordering = ['-id']
|
ordering = ['-id']
|
||||||
|
|
||||||
|
def get_serializer_class(self):
|
||||||
|
if self.action == 'update':
|
||||||
|
return OperationRecordSubmitSerializer
|
||||||
|
return super().get_serializer_class()
|
||||||
@transaction.atomic()
|
@transaction.atomic()
|
||||||
def destroy(self, request, *args, **kwargs):
|
def destroy(self, request, *args, **kwargs):
|
||||||
instance = self.get_object()
|
instance = self.get_object()
|
||||||
|
|
@ -648,12 +647,14 @@ class OperationRecordViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
|
||||||
# 后续加入系统带入数据
|
# 后续加入系统带入数据
|
||||||
return Response(data)
|
return Response(data)
|
||||||
|
|
||||||
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=OperationRecordSubmitSerializer)
|
|
||||||
def submit(self, request, pk=None):
|
def update(self, request, *args, **kwargs):
|
||||||
serializer = OperationRecordSubmitSerializer(data=request.data, context={'request':self.request})
|
serializer = OperationRecordSubmitSerializer(data=request.data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
vdata = serializer.validated_data
|
vdata = serializer.validated_data
|
||||||
opr = self.get_object()
|
opr = self.get_object()
|
||||||
|
if opr.operation.is_submited:
|
||||||
|
raise exceptions.APIException('操作已提交不可修改')
|
||||||
wrds = []
|
wrds = []
|
||||||
for m in vdata['record_data']: # 保存记录详情
|
for m in vdata['record_data']: # 保存记录详情
|
||||||
form_field = m['form_field']
|
form_field = m['form_field']
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ urlpatterns = [
|
||||||
path('api/qm/', include('apps.qm.urls')),
|
path('api/qm/', include('apps.qm.urls')),
|
||||||
path('api/pm/', include('apps.pm.urls')),
|
path('api/pm/', include('apps.pm.urls')),
|
||||||
path('api/wpm/', include('apps.wpm.urls')),
|
path('api/wpm/', include('apps.wpm.urls')),
|
||||||
|
path('api/srm/', include('apps.srm.urls')),
|
||||||
# 工具
|
# 工具
|
||||||
path('api/utils/signature/', GenSignature.as_view()),
|
path('api/utils/signature/', GenSignature.as_view()),
|
||||||
path('api/utils/develop/', UpdateDevelop.as_view()),
|
path('api/utils/develop/', UpdateDevelop.as_view()),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue