ticketDelete

This commit is contained in:
shijing 2021-10-15 16:08:25 +08:00
commit 5ac6a2b1bf
26 changed files with 795 additions and 199 deletions

View File

@ -3,16 +3,55 @@ import request from '@/utils/request'
//生产排程 //生产排程
export function getProductionplanList(query) { export function getProductionplanList(query) {
return request({ return request({
url: 'pm/productionplan/', url: 'pm/production_plan/',
method: 'get', method: 'get',
params: query params: query
}) })
} }
export function createProductionplan(data) { export function createProductionplan(data) {
return request({ return request({
url: 'pm/productionplan/', url: 'pm/production_plan/',
method: 'post', method: 'post',
data data
}) })
} }
//计算物料配置
export function createresource(data) {
return request({
url: '/pm/resource/cal/',
method: 'post',
data
})
}
//产品对应的设备
export function createequip(data) {
return request({
url: '/pm/resource/cal_equip/',
method: 'post',
data
})
}
//生成子计划
export function createsubplan(id, data) {
return request({
url: `/pm/production_plan/${id}/gen_subplan/`,
method: 'post',
data
})
}
//子计划列表
export function getsubproductionplanList(query) {
return request({
url: '/pm/subproduction_plan/',
method: 'get',
params: query
})
}
//子计划编辑开始结束时间
export function updatesubproductionplan(id, data) {
return request({
url: `/pm/subproduction_plan/${id}/`,
method: 'put',
data
})
}

View File

@ -143,7 +143,15 @@ export const asyncRoutes = [
path: 'plan', path: 'plan',
name: 'plan', name: 'plan',
component: () => import('@/views/pm/plan'), component: () => import('@/views/pm/plan'),
meta: { title: '生产计划管理', icon: 'example', perms: ['pm_plan'] } meta: { title: '生产计划管理', icon: 'example', perms: ['index_manage'] }
}
,
{
path: 'work/:id',
name: 'work',
component: () => import('@/views/pm/work'),
meta: { title: '生产子计划', perms: ['vendor_manage'] },
hidden: true
}, },
{ {
path: 'resources', path: 'resources',

View File

@ -144,14 +144,7 @@
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="绑定工序" prop="processes" v-if="material.type==1">
<el-transfer
v-model="material.processes"
:data="processOptions"
:titles="['工序清单', '选择的工序清单']"
:props="{ key: 'id', label: 'name' }"
/>
</el-form-item>
<el-form-item label="排序" prop="sort_str"> <el-form-item label="排序" prop="sort_str">
<el-input v-model="material.sort_str" placeholder="排序" /> <el-input v-model="material.sort_str" placeholder="排序" />
</el-form-item> </el-form-item>

View File

@ -80,13 +80,17 @@
border border
fit fit
stripe stripe
highlight-current-row
@current-change="handlespChange" @current-change="handlespChange"
> >
<el-table-column type="index" width="50" /> <el-table-column type="index" width="50" />
<el-table-column label="物料编号"> <el-table-column label="分解产品名称">
<template slot-scope="scope">{{scope.row.name}}</template> <template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column> </el-table-column>
<el-table-column label="单位消耗量"> <el-table-column label="工序">
<template slot-scope="scope">{{ scope.row.process_.name }}</template>
</el-table-column>
<el-table-column label="排序">
<template slot-scope="scope">{{ scope.row.sort }}</template> <template slot-scope="scope">{{ scope.row.sort }}</template>
</el-table-column> </el-table-column>
@ -128,6 +132,17 @@
<el-form-item label="名称" prop="name"> <el-form-item label="名称" prop="name">
<el-input v-model="subproducation.name" /> <el-input v-model="subproducation.name" />
</el-form-item> </el-form-item>
<el-form-item label="绑定工序" prop="process" >
<el-select v-model="subproducation.process" placeholder="请选择" >
<el-option
v-for="item in processOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="排序" prop="sort"> <el-form-item label="排序" prop="sort">
<el-input-number <el-input-number
v-model="subproducation.sort" v-model="subproducation.sort"
@ -213,17 +228,11 @@
> >
<el-form-item label="消耗量" prop="count"> <el-form-item label="消耗量" prop="count">
<el-input v-model="inputmaterial.count" placeholder="输入整数或小数" /> <el-input-number v-model="inputmaterial.count" :min="0" placeholder="输入整数或小数" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number
v-model="inputmaterial.sort"
:min="-2147483648"
:max="2147483647"
></el-input-number>
</el-form-item> </el-form-item>
<el-form-item label="输入物料" prop="unit"> <el-form-item label="输入物料" prop="unit">
<el-select style="width: 100%" v-model="inputmaterial.material" placeholder="请选择"> <el-select filterable style="width: 100%" v-model="inputmaterial.material" placeholder="请选择">
<el-option <el-option
v-for="item in materialoptions" v-for="item in materialoptions"
:key="item.value" :key="item.value"
@ -232,7 +241,13 @@
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number
v-model="inputmaterial.sort"
:min="-2147483648"
:max="2147483647"
></el-input-number>
</el-form-item>
</el-form> </el-form>
@ -305,15 +320,9 @@
> >
<el-form-item label="产出量" prop="count"> <el-form-item label="产出量" prop="count">
<el-input v-model="outputmaterial.count" placeholder="输入整数或小数" /> <el-input-number v-model="outputmaterial.count" :min="0" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number
v-model="outputmaterial.sort"
:min="-2147483648"
:max="2147483647"
></el-input-number>
</el-form-item> </el-form-item>
<el-form-item label="输出物料" prop="unit"> <el-form-item label="输出物料" prop="unit">
<el-select style="width: 100%" v-model="outputmaterial.material" placeholder="请选择"> <el-select style="width: 100%" v-model="outputmaterial.material" placeholder="请选择">
<el-option <el-option
@ -325,7 +334,13 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number
v-model="outputmaterial.sort"
:min="-2147483648"
:max="2147483647"
></el-input-number>
</el-form-item>
</el-form> </el-form>
<div style="text-align: right"> <div style="text-align: right">
@ -507,11 +522,12 @@
import { getMaterialList,getMaterial,getInputmaterialList,createInputmaterial,updateInputmaterial import { getMaterialList,getMaterial,getInputmaterialList,createInputmaterial,updateInputmaterial
,deleteInputmaterial,getOutputmaterialList,createOutputmaterial,updateOutputmaterial,deleteOutputmaterial, ,deleteInputmaterial,getOutputmaterialList,createOutputmaterial,updateOutputmaterial,deleteOutputmaterial,
getUsedstepList,createUsedstep,deleteUsedstep,getStepList,gettechdocList,createtechdoc,updatetechdoc,deletetechdoc getUsedstepList,createUsedstep,deleteUsedstep,getStepList,gettechdocList,createtechdoc,updatetechdoc,deletetechdoc
,getsubproducationList,createsubproducation,updatesubproducation,deletesubproducation } from "@/api/mtm"; ,getsubproducationList,createsubproducation,updatesubproducation,deletesubproducation,getProcessList } from "@/api/mtm";
import { quillEditor } from 'vue-quill-editor' import { quillEditor } from 'vue-quill-editor'
import 'quill/dist/quill.core.css' import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css' import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css' import 'quill/dist/quill.bubble.css'
import { upUrl, upHeaders } from "@/api/file"; import { upUrl, upHeaders } from "@/api/file";
import checkPermission from "@/utils/permission"; import checkPermission from "@/utils/permission";
import { genTree } from "@/utils"; import { genTree } from "@/utils";
@ -529,7 +545,7 @@ const defaulttechdoc = {
}; };
const defaultsubproducation = { const defaultsubproducation = {
}; };
export default { export default {
@ -541,6 +557,7 @@ export default {
subproducationData:"", subproducationData:"",
inputtableData:"", inputtableData:"",
editorOption: {} , editorOption: {} ,
processOptions:[],
techdoc: defaulttechdoc, techdoc: defaulttechdoc,
subproducation:defaultsubproducation, subproducation:defaultsubproducation,
inputmaterial: defaultinputmaterial, inputmaterial: defaultinputmaterial,
@ -625,7 +642,12 @@ export default {
this.listLoading = false; this.listLoading = false;
}); });
}, },
//工序清单
getProcessList() {
getProcessList().then((res) => {
this.processOptions = genTree(res.data.results);
});
},
//获取产品工艺 //获取产品工艺
getMaterial(){ getMaterial(){
getMaterial(this.product).then((response) => { getMaterial(this.product).then((response) => {
@ -639,11 +661,14 @@ export default {
handleCurrentChange(row){ handleCurrentChange(row){
this.product=row.id; this.product=row.id;
this.getMaterial(); this.getMaterial();
this.getProcessList();
this.getsubproducationList(); this.getsubproducationList();
}, },
//点击产品分解弹出输入输出物料子工序技术文件 //点击产品分解弹出输入输出物料子工序技术文件
handlespChange(row){ handlespChange(row){
this.subproduction = row.id; this.subproduction = row.id;
this.processes = row.process;
this.getmaterialList();//物料列表 this.getmaterialList();//物料列表
this.getInputmaterialLists();//输入物料 this.getInputmaterialLists();//输入物料
@ -911,7 +936,7 @@ export default {
}, },
getstepList() { getstepList() {
this.listQuerystep.process=this.processes;
getStepList(this.listQuerystep).then((response) => { getStepList(this.listQuerystep).then((response) => {
if (response.data) { if (response.data) {
this.stepoptions = genTree(response.data); this.stepoptions = genTree(response.data);
@ -1071,3 +1096,4 @@ export default {
}, },
}; };
</script> </script>

View File

@ -51,7 +51,12 @@
<el-table-column label="交付截止时间"> <el-table-column label="交付截止时间">
<template slot-scope="scope">{{ scope.row.order_.delivery_date }}</template> <template slot-scope="scope">{{ scope.row.order_.delivery_date }}</template>
</el-table-column> </el-table-column>
<el-table-column label="是否生成子计划">
<template slot-scope="scope" >
<el-tag v-if="scope.row.is_planed==false"></el-tag>
<el-tag v-if="scope.row.is_planed==true"></el-tag>
</template>
</el-table-column>
<el-table-column label="创建时间"> <el-table-column label="创建时间">
<template slot-scope="scope">{{ scope.row.create_time }}</template> <template slot-scope="scope">{{ scope.row.create_time }}</template>
</el-table-column> </el-table-column>
@ -61,13 +66,14 @@
width="100px" width="100px"
> >
<template slot-scope="scope"> <template slot-scope="scope">
<el-link type="primary" <el-link type="primary"
v-if="checkPermission(['warehouse_update'])" v-if="scope.row.is_planed"
@click="handleclick(scope)" @click="handleselectplan(scope)"
>车间排产</el-link >查看子计划</el-link>
> <el-link type="primary"
v-else
@click="handleWork(scope)"
>生产子计划</el-link>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -118,6 +124,8 @@
<el-table-column label="交货日期"> <el-table-column label="交货日期">
<template slot-scope="scope">{{ scope.row.delivery_date }}</template> <template slot-scope="scope">{{ scope.row.delivery_date }}</template>
</el-table-column> </el-table-column>
<el-table-column label="创建时间"> <el-table-column label="创建时间">
<template slot-scope="scope">{{ scope.row.create_time }}</template> <template slot-scope="scope">{{ scope.row.create_time }}</template>
</el-table-column> </el-table-column>
@ -159,7 +167,7 @@
<el-input v-model="orderplan.number" placeholder="生产计划编号" /> <el-input v-model="orderplan.number" placeholder="生产计划编号" />
</el-form-item> </el-form-item>
<el-form-item label="排产数量" prop="count"> <el-form-item label="排产数量" prop="count">
<el-input type="number" v-model.number="orderplan.count"/> <el-input-number v-model="orderplan.count" :min="0"></el-input-number>
</el-form-item> </el-form-item>
@ -190,7 +198,7 @@
</template> </template>
<script> <script>
import { getordertoplan } from "@/api/sam"; import { getordertoplan } from "@/api/sam";
import { createProductionplan,getProductionplanList} from "@/api/pm"; import { createProductionplan,getProductionplanList,createsubplan} from "@/api/pm";
import { getMaterialList } from "@/api/mtm"; import { getMaterialList } from "@/api/mtm";
import checkPermission from "@/utils/permission"; import checkPermission from "@/utils/permission";
@ -282,6 +290,31 @@ export default {
} }
}); });
}, },
handleWork(scope)
{
this.$confirm("确认生成子计划?", "提醒", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
await createsubplan(scope.row.id).then((res) => {
if (res.code >= 200) {
this.$message.success("生成子计划成功!");
}
});
})
.catch((err) => {
console.error(err);
});
},
//查看子计划
handleselectplan(scope)
{
this.$router.push({name: "work", params: { id: scope.row.id }, })
}
}, },
}; };

View File

@ -7,60 +7,60 @@
<span>合同订单列表</span> <span>合同订单列表</span>
</div> </div>
<el-button type="primary" @click="handlecount"
>计算物料</el-button>
<el-table <el-table
:data="orderList.results" :data="orderList.results"
border border
fit fit
stripe stripe
style="width: 100%" style="width: 100%"
@selection-change="handleSelectionChange">
> <el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column type="index" width="50" /> <el-table-column type="index" width="50" />
<el-table-column label="订单编号"> <el-table-column label="订单编号">
<template slot-scope="scope">{{ scope.row.number }}</template> <template slot-scope="scope">{{ scope.row.number }}</template>
</el-table-column> </el-table-column>
<el-table-column label="所需产品">
<template slot-scope="scope">{{ scope.row.product_.name }}</template>
</el-table-column>
<el-table-column label="所需数量">
<template slot-scope="scope">{{ scope.row.count }}</template>
</el-table-column>
<el-table-column label="已派数量">
<template slot-scope="scope">{{ scope.row.planed_count }}</template>
</el-table-column>
<el-table-column label="计划生产数" width="140px">
<template slot-scope="scope" >
<el-form :model="scope.row">
<el-form-item size="mini">
<el-input-number v-model="scope.row.pgcount" :min="0"></el-input-number>
</el-form-item>
</el-form>
</template>
</el-table-column>
<el-table-column label="产品型号">
<template slot-scope="scope">{{ scope.row.product_.specification }}</template>
</el-table-column>
<el-table-column label="客户"> <el-table-column label="客户">
<template slot-scope="scope">{{ scope.row.customer_.name }}</template> <template slot-scope="scope">{{ scope.row.customer_.name }}</template>
</el-table-column> </el-table-column>
<el-table-column label="所属合同"> <el-table-column label="所属合同">
<template slot-scope="scope">{{ scope.row.contract_.name }}</template> <template slot-scope="scope">{{ scope.row.contract_.name }}</template>
</el-table-column> </el-table-column>
<el-table-column label="所需产品">
<template slot-scope="scope">{{ scope.row.product_.name }}</template>
</el-table-column>
<el-table-column label="产品型号">
<template slot-scope="scope">{{ scope.row.product_.specification }}</template>
</el-table-column>
<el-table-column label="所需数量">
<template slot-scope="scope">{{ scope.row.count }}</template>
</el-table-column>
<el-table-column label="已派数量">
<template slot-scope="scope">{{ scope.row.planed_count }}</template>
</el-table-column>
<el-table-column label="交货日期"> <el-table-column label="交货日期">
<template slot-scope="scope">{{ scope.row.delivery_date }}</template> <template slot-scope="scope">{{ scope.row.delivery_date }}</template>
</el-table-column> </el-table-column>
<el-table-column label="创建时间"> <el-table-column label="创建时间">
<template slot-scope="scope">{{ scope.row.create_time }}</template> <template slot-scope="scope">{{ scope.row.create_time }}</template>
</el-table-column> </el-table-column>
<el-table-column
align="center"
label="操作"
width="120px"
fixed="right"
>
<template slot-scope="scope">
<el-link type="primary"
v-if="checkPermission(['warehouse_update'])"
@click="handleclick(scope)"
>排产</el-link
>
</template>
</el-table-column>
</el-table> </el-table>
<pagination <pagination
v-show="orderList.count > 0" v-show="orderList.count > 0"
@ -81,7 +81,7 @@
</div> </div>
<el-table <el-table
:data="orderList.results" :data="materialpzTable"
border border
fit fit
stripe stripe
@ -90,58 +90,25 @@
> >
<el-table-column type="index" width="50" /> <el-table-column type="index" width="50" />
<el-table-column label="订单编号"> <el-table-column label="物料名称">
<template slot-scope="scope">{{ scope.row.name }}</template>
</el-table-column>
<el-table-column label="物料类型">
<template slot-scope="scope"> {{options_[scope.row.type]}}</template>
</el-table-column>
<el-table-column label="物料编号">
<template slot-scope="scope">{{ scope.row.number }}</template> <template slot-scope="scope">{{ scope.row.number }}</template>
</el-table-column> </el-table-column>
<el-table-column label="客户"> <el-table-column label="生产所需数量">
<template slot-scope="scope">{{ scope.row.customer_.name }}</template>
</el-table-column>
<el-table-column label="所属合同">
<template slot-scope="scope">{{ scope.row.contract_.name }}</template>
</el-table-column>
<el-table-column label="所需产品">
<template slot-scope="scope">{{ scope.row.product_.name }}</template>
</el-table-column>
<el-table-column label="产品型号">
<template slot-scope="scope">{{ scope.row.product_.specification }}</template>
</el-table-column>
<el-table-column label="所需数量">
<template slot-scope="scope">{{ scope.row.count }}</template> <template slot-scope="scope">{{ scope.row.count }}</template>
</el-table-column> </el-table-column>
<el-table-column label="已派数"> <el-table-column label="库存量">
<template slot-scope="scope">{{ scope.row.planed_count }}</template> <template slot-scope="scope">{{ scope.row.inv_count }}</template>
</el-table-column> </el-table-column>
<el-table-column label="交货日期">
<template slot-scope="scope">{{ scope.row.delivery_date }}</template>
</el-table-column>
<el-table-column label="创建时间">
<template slot-scope="scope">{{ scope.row.create_time }}</template>
</el-table-column>
<el-table-column
align="center"
label="操作"
width="120px"
fixed="right"
>
<template slot-scope="scope">
<el-link type="primary"
v-if="checkPermission(['warehouse_update'])"
@click="handleclick(scope)"
>排产</el-link
>
</template>
</el-table-column>
</el-table> </el-table>
<pagination
v-show="orderList.count > 0"
:total="orderList.count"
:page.sync="listQuery.page"
:limit.sync="listQuery.page_size"
@pagination="getorderList"
/>
</el-card> </el-card>
</el-col> </el-col>
@ -154,59 +121,30 @@
</div> </div>
<el-table <el-table
:data="orderList.results" :data="equipmentTable"
border border
fit fit
stripe stripe
style="width: 100%" style="width: 100%"
> >
<el-table-column type="index" width="50" /> <el-table-column type="index" width="50" />
<el-table-column label="订单编号"> <el-table-column label="设备名称">
<template slot-scope="scope">{{ scope.row.number }}</template> <template slot-scope="scope">{{ scope.row.name }}</template>
</el-table-column> </el-table-column>
<el-table-column label="客户"> <el-table-column label="设备状态">
<template slot-scope="scope">{{ scope.row.customer_.name }}</template> <template slot-scope="scope"> {{state_[scope.row.state]}}</template>
</el-table-column> </el-table-column>
<el-table-column label="所属合同"> <el-table-column label="设备编号">
<template slot-scope="scope">{{ scope.row.contract_.name }}</template> <template slot-scope="scope"> {{scope.row.number}}</template>
</el-table-column> </el-table-column>
<el-table-column label="所需产品"> <el-table-column label="设备型号">
<template slot-scope="scope">{{ scope.row.product_.name }}</template> <template slot-scope="scope"> {{scope.row.model}}</template>
</el-table-column>
<el-table-column label="产品型号">
<template slot-scope="scope">{{ scope.row.product_.specification }}</template>
</el-table-column>
<el-table-column label="所需数量">
<template slot-scope="scope">{{ scope.row.count }}</template>
</el-table-column>
<el-table-column label="已派数量">
<template slot-scope="scope">{{ scope.row.planed_count }}</template>
</el-table-column> </el-table-column>
<el-table-column label="交货日期">
<template slot-scope="scope">{{ scope.row.delivery_date }}</template>
</el-table-column>
<el-table-column label="创建时间">
<template slot-scope="scope">{{ scope.row.create_time }}</template>
</el-table-column>
<el-table-column
align="center"
label="操作"
width="120px"
fixed="right"
>
<template slot-scope="scope">
<el-link type="primary"
v-if="checkPermission(['warehouse_update'])"
@click="handleclick(scope)"
>排产</el-link
>
</template>
</el-table-column>
</el-table> </el-table>
<pagination <pagination
v-show="orderList.count > 0" v-show="orderList.count > 0"
@ -226,7 +164,7 @@
</template> </template>
<script> <script>
import { getordertoplan } from "@/api/sam"; import { getordertoplan } from "@/api/sam";
import { createProductionplan,getProductionplanList} from "@/api/pm"; import { createProductionplan,getProductionplanList,createresource,createequip} from "@/api/pm";
import { getMaterialList } from "@/api/mtm"; import { getMaterialList } from "@/api/mtm";
import checkPermission from "@/utils/permission"; import checkPermission from "@/utils/permission";
@ -243,13 +181,30 @@ export default {
orderplan: defaulteorderplan, orderplan: defaulteorderplan,
orderList: { orderList: {
count: 0, count: 0,
},
options_:{
"1":'成品',
"2":'半成品',
"3":'主要原料',
"4":'辅助原料',
"5":'加工工具',
"6":'辅助工具',
},
state_:{
0:'完好',
1:'限用',
2:'在修',
3:'禁用',
}, },
listQuery: { listQuery: {
page: 1, page: 1,
page_size: 20, page_size: 20,
}, },
materialpzTable:"",
mutipID:[],
equipmentTable:[],
listLoading: true, listLoading: true,
dialogVisible: false, dialogVisible: false,
dialogType: "new", dialogType: "new",
@ -278,7 +233,35 @@ export default {
this.listLoading = false; this.listLoading = false;
}); });
}, },
handleSelectionChange(row) {
let _this=this
_this.mutipID=[]
row.forEach((item) => {
_this.mutipID.push({
"id":item.product_.id,
"count": item.pgcount
});
});
},
//物料计算
handlecount()
{
createresource(this.mutipID).then((res) => {
if (res.code >= 200) {
this.materialpzTable=res.data;
this.$message.success("物料计算成功");
}
});
createequip(this.mutipID).then((res) => {
if (res.code >= 200) {
this.equipmentTable=res.data;
this.$message.success("成功");
}
});
},
handleclick(scope){ handleclick(scope){
this.orderID = scope.row.id; this.orderID = scope.row.id;

View File

@ -0,0 +1,181 @@
<template>
<div class="app-container">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>生产任务列表</span>
</div>
<el-table
:data="subproductionplanList.results"
border
fit
stripe
style="width: 100%"
max-height="400"
>
<el-table-column type="index" width="50" />
<el-table-column label="生产子计划名">
<template slot-scope="scope">{{ scope.row.workshop_.name }}</template>
</el-table-column>
<el-table-column label="工序名">
<template slot-scope="scope">{{ scope.row.process_.name }}</template>
</el-table-column>
<el-table-column label="工序编号">
<template slot-scope="scope">{{ scope.row.process_.number }}</template>
</el-table-column>
<el-table-column label="开工时间">
<template slot-scope="scope">{{ scope.row.start_date }}</template>
</el-table-column>
<el-table-column label="完工时间">
<template slot-scope="scope">{{ scope.row.end_date }}</template>
</el-table-column>
<el-table-column label="创建时间">
<template slot-scope="scope">{{ scope.row.create_time }}</template>
</el-table-column>
<el-table-column
align="center"
label="操作"
width="100px"
>
<template slot-scope="scope">
<el-link type="primary"
v-if="checkPermission(['warehouse_update'])"
@click="handleclick(scope)"
>编辑</el-link
>
</template>
</el-table-column>
</el-table>
<pagination
v-show="subproductionplanList.count > 0"
:total="subproductionplanList.count"
:page.sync="listQuery.page"
:limit.sync="listQuery.page_size"
@pagination="getspList"
/>
</el-card>
<el-dialog
:visible.sync="dialogVisible"
>
<el-form
ref="Form"
:model="subproductionplan"
label-width="180px"
label-position="right"
:rules="rule1"
>
<el-form-item label="子计划开工时间" prop="start_date">
<el-date-picker
v-model="subproductionplan.start_date"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
style="width:100%"
>
</el-date-picker>
</el-form-item>
<el-form-item label="子计划完工时间" prop="end_date">
<el-date-picker
v-model="subproductionplan.end_date"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
style="width:100%"
>
</el-date-picker>
</el-form-item>
</el-form>
<div style="text-align: right">
<el-button type="danger" @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirm('Form')">确认</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getsubproductionplanList,updatesubproductionplan } from "@/api/pm";
import checkPermission from "@/utils/permission";
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
const defaultesubproductionplan = {
};
export default {
components: { Pagination },
data() {
return {
subproductionplan: defaultesubproductionplan,
subproductionplanList: {
count: 0,
},
listQuery: {
page: 1,
page_size: 20,
},
listLoading: true,
dialogVisible: false,
dialogType: "new",
rule1: {
number: [{ required: true, message: "请输入", trigger: "blur" }],
},
};
},
computed: {},
watch: {},
created() {
this.id=this.$route.params.id;
this.getspList();
},
methods: {
checkPermission,
//订单列表
getspList() {
this.listLoading = true;
this.listQuery.production_plan = this.id;
console.log(this.listQuery)
getsubproductionplanList(this.listQuery).then((response) => {
if (response.data) {
this.subproductionplanList = response.data;
}
this.listLoading = false;
});
},
handleclick(scope)
{
this.subproductionplan = Object.assign({}, scope.row); // copy obj
this.dialogVisible = true;
this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});
},
confirm()
{
updatesubproductionplan(this.subproductionplan.id,this.subproductionplan).then((res) => {
if (res.code >= 200) {
this.getspList();
this.dialogVisible = false;
this.$message.success("成功");
}
});
}
},
};
</script>

View File

@ -74,7 +74,7 @@
<template slot-scope="scope"> <template slot-scope="scope">
<el-link <el-link
v-if="checkPermission(['warehouse_update'])" v-if="scope.row.planed_count==0&&checkPermission(['warehouse_update'])"
@click="handleEdit(scope)" @click="handleEdit(scope)"
>编辑</el-link >编辑</el-link
> >

View File

@ -23,6 +23,7 @@ class EquipmentSerializer(ModelSerializer):
def get_step_(self, obj): def get_step_(self, obj):
return Step.objects.filter(equipments=obj).values('id', 'name', 'number') return Step.objects.filter(equipments=obj).values('id', 'name', 'number')
class EquipmentSimpleSerializer(ModelSerializer): class EquipmentSimpleSerializer(ModelSerializer):
class Meta: class Meta:
model = Equipment model = Equipment

View File

@ -0,0 +1,26 @@
# Generated by Django 3.2.6 on 2021-10-14 01:44
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('em', '0009_auto_20210916_1108'),
('mtm', '0021_auto_20211013_0856'),
]
operations = [
migrations.AddField(
model_name='subproduction',
name='process',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='subproduction_process', to='mtm.process', verbose_name='隶属大工序'),
preserve_default=False,
),
migrations.AlterField(
model_name='step',
name='equipments',
field=models.ManyToManyField(related_name='step_equips', to='em.Equipment', verbose_name='使用设备'),
),
]

View File

@ -64,7 +64,7 @@ class Step(CommonAModel):
number = models.CharField('步骤编号', max_length=100, null=True, blank=True) number = models.CharField('步骤编号', max_length=100, null=True, blank=True)
instruction_content = models.TextField('相应操作指导', null=True, blank=True) instruction_content = models.TextField('相应操作指导', null=True, blank=True)
sort = models.IntegerField('排序号', default=1) sort = models.IntegerField('排序号', default=1)
equipments = models.ManyToManyField(Equipment, verbose_name='使用设备') equipments = models.ManyToManyField(Equipment, verbose_name='使用设备', related_name='step_equips')
class Meta: class Meta:
verbose_name = '工序步骤' verbose_name = '工序步骤'
@ -134,6 +134,7 @@ class SubProduction(CommonAModel):
""" """
name = models.CharField('命名', max_length=50, null=True, blank=True) name = models.CharField('命名', max_length=50, null=True, blank=True)
product = models.ForeignKey(Material, verbose_name='产品', on_delete=models.CASCADE) product = models.ForeignKey(Material, verbose_name='产品', on_delete=models.CASCADE)
process = models.ForeignKey(Process, verbose_name='隶属大工序', on_delete=models.CASCADE, related_name='subproduction_process')
sort = models.IntegerField('排序号', default=1) sort = models.IntegerField('排序号', default=1)
class Meta: class Meta:
@ -173,7 +174,7 @@ class UsedStep(CommonAModel):
""" """
涉及的生产子工序 涉及的生产子工序
""" """
step = models.ForeignKey(Step, verbose_name='子工序', on_delete=models.CASCADE, related_name='usedsteps') step = models.ForeignKey(Step, verbose_name='子工序', on_delete=models.CASCADE, related_name='usedstep')
remark = models.TextField('生产备注', null=True, blank=True) remark = models.TextField('生产备注', null=True, blank=True)
subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE) subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE)

View File

@ -17,8 +17,9 @@ class MaterialDetailSerializer(serializers.ModelSerializer):
fields = '__all__' fields = '__all__'
def get_processes_(self, obj): def get_processes_(self, obj):
steps = UsedStep.objects.filter(subproduction__product=obj).values_list('step', flat=True) # steps = UsedStep.objects.filter(subproduction__product=obj).values_list('step', flat=True)
objs = Process.objects.filter(step_process__id__in=steps).distinct().order_by('number') # objs = Process.objects.filter(step_process__id__in=steps).distinct().order_by('number')
objs = Process.objects.filter(subproduction_process__product=obj, subproduction_process__is_deleted=False, is_deleted=False).order_by('number')
return ProcessSimpleSerializer(instance=objs, many=True).data return ProcessSimpleSerializer(instance=objs, many=True).data
@ -61,6 +62,7 @@ class StepDetailSerializer(serializers.ModelSerializer):
return queryset return queryset
class SubProductionSerializer(serializers.ModelSerializer): class SubProductionSerializer(serializers.ModelSerializer):
process_ = ProcessSimpleSerializer(source='process', read_only=True)
class Meta: class Meta:
model = SubProduction model = SubProduction
fields = '__all__' fields = '__all__'

View File

@ -74,8 +74,8 @@ class SubProductionViewSet(CreateUpdateModelAMixin, ModelViewSet):
产品生产分解增删改查 产品生产分解增删改查
""" """
perms_map={'*':'*'} perms_map={'*':'*'}
queryset = SubProduction.objects.all() queryset = SubProduction.objects.select_related('process').all()
filterset_fields = ['product'] filterset_fields = ['product', 'process']
search_fields = ['name'] search_fields = ['name']
serializer_class = SubProductionSerializer serializer_class = SubProductionSerializer
ordering = ['sort'] ordering = ['sort']

View File

@ -0,0 +1,45 @@
# Generated by Django 3.2.6 on 2021-10-14 07:03
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 = [
('system', '0003_auto_20210812_0909'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('mtm', '0022_auto_20211014_0944'),
('pm', '0002_alter_productionplan_count'),
]
operations = [
migrations.AddField(
model_name='productionplan',
name='is_planed',
field=models.BooleanField(default=False, verbose_name='是否已排产'),
),
migrations.CreateModel(
name='SubProductionPlan',
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='删除标记')),
('start_date', models.DateField(verbose_name='计划开工日期')),
('end_date', models.DateField(verbose_name='计划完工日期')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='subproductionplan_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('process', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.process', verbose_name='关联大工序')),
('production_plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pm.productionplan', verbose_name='关联主生产计划')),
('subproduction', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.subproduction', verbose_name='关联生产分解')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='subproductionplan_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
('workshop', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='system.organization', verbose_name='生产车间')),
],
options={
'verbose_name': '子生产计划',
'verbose_name_plural': '子生产计划',
},
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-10-15 02:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pm', '0003_auto_20211014_1503'),
]
operations = [
migrations.AddField(
model_name='subproductionplan',
name='steps',
field=models.JSONField(default=list, verbose_name='工艺步骤'),
),
]

View File

@ -1,4 +1,4 @@
from apps.system.models import CommonAModel from apps.system.models import CommonAModel, Organization
from django.db import models from django.db import models
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
from django.db.models.base import Model from django.db.models.base import Model
@ -6,10 +6,9 @@ import django.utils.timezone as timezone
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from utils.model import SoftModel, BaseModel from utils.model import SoftModel, BaseModel
from apps.mtm.models import Material from apps.mtm.models import Material, Process, SubProduction
from apps.sam.models import Order from apps.sam.models import Order
class ProductionPlan(CommonAModel): class ProductionPlan(CommonAModel):
""" """
生产计划 生产计划
@ -20,7 +19,7 @@ class ProductionPlan(CommonAModel):
count = models.IntegerField('生产数量', default=1) count = models.IntegerField('生产数量', default=1)
start_date = models.DateField('计划开工日期') start_date = models.DateField('计划开工日期')
end_date = models.DateField('计划完工日期') end_date = models.DateField('计划完工日期')
is_planed = models.BooleanField('是否已排产', default=False)
class Meta: class Meta:
verbose_name = '生产计划' verbose_name = '生产计划'
verbose_name_plural = verbose_name verbose_name_plural = verbose_name
@ -28,3 +27,24 @@ class ProductionPlan(CommonAModel):
def __str__(self): def __str__(self):
return self.number return self.number
class SubProductionPlan(CommonAModel):
"""
子生产计划
"""
production_plan = models.ForeignKey(ProductionPlan, verbose_name='关联主生产计划', on_delete=models.CASCADE)
subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE)
start_date = models.DateField('计划开工日期')
end_date = models.DateField('计划完工日期')
workshop = models.ForeignKey(Organization, verbose_name='生产车间', on_delete=models.CASCADE)
process = models.ForeignKey(Process, verbose_name='关联大工序', on_delete=models.CASCADE)
steps = models.JSONField('工艺步骤', default=list)
class Meta:
verbose_name = '子生产计划'
verbose_name_plural = verbose_name
# class ProductionProgress(BaseModel):
# """
# 子计划生产进度
# """
# subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.CASCADE)
# material = models.

View File

@ -1,7 +1,8 @@
from apps.pm.models import ProductionPlan from apps.pm.models import ProductionPlan, SubProductionPlan
from rest_framework import serializers from rest_framework import serializers
from apps.sam.serializers import OrderSerializer from apps.sam.serializers import OrderSerializer
from apps.mtm.serializers import MaterialSimpleSerializer from apps.mtm.serializers import MaterialSimpleSerializer, ProcessSimpleSerializer
from apps.system.serializers import OrganizationSimpleSerializer
class ProductionPlanCreateFromOrderSerializer(serializers.ModelSerializer): class ProductionPlanCreateFromOrderSerializer(serializers.ModelSerializer):
@ -15,3 +16,25 @@ class ProductionPlanSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = ProductionPlan model = ProductionPlan
fields ='__all__' fields ='__all__'
class ResourceCalSerializer(serializers.Serializer):
id = serializers.IntegerField(label='产品ID')
count = serializers.IntegerField(label='生产数量')
class ResourceCalListSerializer(serializers.ListSerializer):
child = ResourceCalSerializer()
class SubProductionPlanListSerializer(serializers.ModelSerializer):
workshop_ = OrganizationSimpleSerializer(source='workshop', read_only=True)
process_ = ProcessSimpleSerializer(source='process', read_only=True)
class Meta:
model=SubProductionPlan
fields = '__all__'
class SubProductionPlanUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = SubProductionPlan
fields = ['start_date', 'end_date']
class GenSubPlanSerializer(serializers.Serializer):
pass

View File

@ -1,12 +1,13 @@
from apps.pm.views import ProductionPlanViewSet from apps.pm.views import ProductionPlanViewSet, ResourceViewSet, SubProductionPlanViewSet
from django.db.models import base from django.db.models import base
from rest_framework import urlpatterns from rest_framework import urlpatterns
from django.urls import path, include from django.urls import path, include
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
router = DefaultRouter() router = DefaultRouter()
router.register('productionplan', ProductionPlanViewSet, basename='productionplan') router.register('production_plan', ProductionPlanViewSet, basename='production_plan')
router.register('subproduction_plan', SubProductionPlanViewSet, basename='subproduction_plan')
router.register('resource', ResourceViewSet, basename='resource')
urlpatterns = [ urlpatterns = [
path('', include(router.urls)), path('', include(router.urls)),
] ]

View File

@ -1,13 +1,19 @@
from rest_framework import serializers
from rest_framework.views import APIView from rest_framework.views import APIView
from apps.em.models import Equipment
from apps.em.serializers import EquipmentSerializer
from apps.mtm.models import InputMaterial, Step, SubProduction, UsedStep
from apps.system.mixins import CreateUpdateModelAMixin from apps.system.mixins import CreateUpdateModelAMixin
from apps.pm.serializers import ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer from apps.pm.serializers import GenSubPlanSerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer
from rest_framework.mixins import CreateModelMixin, ListModelMixin from rest_framework.mixins import CreateModelMixin, ListModelMixin, UpdateModelMixin
from apps.pm.models import ProductionPlan from apps.pm.models import ProductionPlan, SubProductionPlan
from rest_framework.viewsets import GenericViewSet, ModelViewSet from rest_framework.viewsets import GenericViewSet, ModelViewSet
from django.shortcuts import render from django.shortcuts import render
from apps.sam.models import Order from apps.sam.models import Order
from rest_framework.exceptions import APIException from rest_framework.exceptions import APIException
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.decorators import action
from django.db.models import F
# Create your views here. # Create your views here.
def updateOrderPlanedCount(order): def updateOrderPlanedCount(order):
@ -36,7 +42,9 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel
def get_serializer_class(self): def get_serializer_class(self):
if self.action in ['create']: if self.action in ['create']:
return ProductionPlanCreateFromOrderSerializer return ProductionPlanCreateFromOrderSerializer
return ProductionPlanSerializer elif self.action == 'list':
return ProductionPlanSerializer
return super().get_serializer_class()
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
data = request.data data = request.data
@ -51,6 +59,90 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel
instance = serializer.save(create_by=request.user, product=order.product) instance = serializer.save(create_by=request.user, product=order.product)
updateOrderPlanedCount(instance.order) updateOrderPlanedCount(instance.order)
return Response() return Response()
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=GenSubPlanSerializer)
def gen_subplan(self, request, pk=None):
"""
生成子计划
"""
production_plan=self.get_object()
if production_plan.is_planed:
raise APIException('已生成子计划')
subps = SubProduction.objects.filter(product=production_plan.product).order_by('process__number')
for i in subps:
steps = Step.objects.filter(usedstep__subproduction=i, usedstep__subproduction__is_deleted=False,
usedstep__is_deleted=False, is_deleted=False).values('id', 'number', 'name', 'usedstep__remark')
SubProductionPlan.objects.create(production_plan=production_plan, subproduction=i,
start_date=production_plan.start_date, end_date=production_plan.end_date,
workshop=i.process.workshop, process=i.process, create_by=request.user,
steps = list(steps))
production_plan.is_planed=True
production_plan.save()
return Response()
class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateModelMixin, GenericViewSet):
"""
子生产计划-列表/修改
"""
perms_map = {'*': '*'}
queryset = SubProductionPlan.objects.select_related('process', 'workshop')
search_fields = []
filterset_fields = ['production_plan']
ordering_fields = ['process__number']
ordering = ['process__number']
def get_serializer_class(self):
if self.action == 'list':
return SubProductionPlanListSerializer
elif self.action == 'update':
return SubProductionPlanUpdateSerializer
return SubProductionPlanListSerializer
class ResourceViewSet(GenericViewSet):
perms_map = {'*': '*'}
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=ResourceCalListSerializer)
def cal(self, request, pk=None):
"""
物料消耗计算
"""
rdata = request.data
serializer = self.get_serializer(data=rdata)
serializer.is_valid(raise_exception=True)
res_d_list = []
res = []
for i in rdata:
materials = InputMaterial.objects.filter(subproduction__product__id=i['id'],
subproduction__is_deleted=False, is_deleted=False, material__type__in=[3,4]).order_by('material__number')\
.values('material__id', 'material__name', 'material__number', 'material__type', 'count', 'material__count')
l_m = list(materials)
for m in l_m:
if m['material__id'] in res_d_list:
index = res_d_list.index(m['material__id'])
res[index]['count'] = res[index]['count'] + m['count']*i['count']
else:
res_d_list.append(m['material__id'])
res.append({'id':m['material__id'], 'name':m['material__name'],
'type':m['material__type'], 'number':m['material__number'],
'count':m['count']*i['count'], 'inv_count':m['material__count']})
return Response(res)
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=ResourceCalListSerializer)
def cal_equip(self, request, pk=None):
"""
设备状态查看
"""
rdata = request.data
serializer = self.get_serializer(data=rdata)
serializer.is_valid(raise_exception=True)
rdata_l = []
for i in rdata:
rdata_l.append(i['id'])
subproductions = SubProduction.objects.filter(product__id__in=rdata_l, is_deleted=False)
steps = Step.objects.filter(usedstep__is_deleted=False, usedstep__subproduction__in=subproductions)
equips = Equipment.objects.filter(step_equips__in=steps, is_deleted=False)
serializer = EquipmentSerializer(instance=equips, many=True)
return Response(serializer.data)
class ResourceCalculate(APIView):
pass

View File

@ -16,7 +16,7 @@ class TicketFilterSet(filters.FilterSet):
elif value == 'duty': elif value == 'duty':
queryset = queryset.filter(participant__contains=user.id).exclude(act_state__in=[Ticket.TICKET_ACT_STATE_FINISH, Ticket.TICKET_ACT_STATE_CLOSED]) queryset = queryset.filter(participant__contains=user.id).exclude(act_state__in=[Ticket.TICKET_ACT_STATE_FINISH, Ticket.TICKET_ACT_STATE_CLOSED])
elif value == 'worked': elif value == 'worked':
queryset = queryset.filter(ticketflow_ticket__participant=user).exclude(create_by=user) queryset = queryset.filter(ticketflow_ticket__participant=user).exclude(create_by=user).order_by('-update_time').distinct()
elif value == 'all': elif value == 'all':
pass pass
else: else:

View File

@ -0,0 +1,19 @@
# Generated by Django 3.2.6 on 2021-10-14 01:44
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('wf', '0012_ticketflow_intervene_type'),
]
operations = [
migrations.AlterField(
model_name='ticketflow',
name='transition',
field=models.ForeignKey(blank=True, help_text='与worklow.Transition关联 为空时表示认为干预的操作', null=True, on_delete=django.db.models.deletion.CASCADE, to='wf.transition', verbose_name='流转id'),
),
]

View File

@ -201,7 +201,7 @@ class TicketFlow(BaseModel):
工单流转日志 工单流转日志
""" """
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, verbose_name='关联工单', related_name='ticketflow_ticket') ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, verbose_name='关联工单', related_name='ticketflow_ticket')
transition = models.ForeignKey(Transition, verbose_name='流转id', help_text='与worklow.Transition关联0时表示认为干预的操作', on_delete=models.CASCADE, null=True, blank=True) transition = models.ForeignKey(Transition, verbose_name='流转id', help_text='与worklow.Transition关联时表示认为干预的操作', on_delete=models.CASCADE, null=True, blank=True)
suggestion = models.CharField('处理意见', max_length=10000, default='', blank=True) suggestion = models.CharField('处理意见', max_length=10000, default='', blank=True)
participant_type = models.IntegerField('处理人类型', default=0, help_text='0.无处理人,1.个人,2.多人', choices=State.state_participanttype_choices) participant_type = models.IntegerField('处理人类型', default=0, help_text='0.无处理人,1.个人,2.多人', choices=State.state_participanttype_choices)
participant = models.ForeignKey(User, verbose_name='处理人', on_delete=models.SET_NULL, null=True, blank=True, related_name='ticketflow_participant') participant = models.ForeignKey(User, verbose_name='处理人', on_delete=models.SET_NULL, null=True, blank=True, related_name='ticketflow_participant')

View File

@ -129,4 +129,17 @@ class TicketHandleSerializer(serializers.Serializer):
suggestion = serializers.CharField(label="处理意见", required = False) suggestion = serializers.CharField(label="处理意见", required = False)
class TicketRetreatSerializer(serializers.Serializer): class TicketRetreatSerializer(serializers.Serializer):
suggestion = serializers.CharField(label="撤回原因", required = False) suggestion = serializers.CharField(label="撤回原因", required = False)
class TicketCloseSerializer(serializers.Serializer):
suggestion = serializers.CharField(label="关闭原因", required = False)
class TicketAddNodeSerializer(serializers.Serializer):
suggestion = serializers.CharField(label="加签说明", required = False)
toadd_user = serializers.IntegerField(label='发送给谁去加签')
class TicketAddNodeEndSerializer(serializers.Serializer):
suggestion = serializers.CharField(label="加签意见", required = False)
class TicketDestorySerializer(serializers.Serializer):
ids = serializers.ListField(child=serializers.IntegerField(), label='工单ID列表')

View File

@ -32,7 +32,18 @@ class WfService(object):
return wf_state_obj return wf_state_obj
except: except:
raise Exception('工作流状态配置错误') raise Exception('工作流状态配置错误')
@staticmethod
def get_workflow_end_state(workflow:Workflow):
"""
获取工作流结束状态
"""
try:
wf_state_obj = State.objects.get(workflow=workflow, type=State.STATE_TYPE_END, is_deleted=False)
return wf_state_obj
except:
raise Exception('工作流状态配置错误')
@staticmethod @staticmethod
def get_workflow_custom_fields(workflow:Workflow): def get_workflow_custom_fields(workflow:Workflow):
""" """
@ -124,7 +135,7 @@ class WfService(object):
""" """
到达结束状态 到达结束状态
""" """
return dict(destination_participant_type=State.PARTICIPANT_TYPE_PERSONAL, return dict(destination_participant_type=0,
destination_participant=0, destination_participant=0,
multi_all_person={}) multi_all_person={})
multi_all_person_dict = {} multi_all_person_dict = {}

View File

@ -1,9 +1,10 @@
from apps.system.models import User
from apps.wf.filters import TicketFilterSet from apps.wf.filters import TicketFilterSet
from django.core.exceptions import AppRegistryNotReady from django.core.exceptions import AppRegistryNotReady
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import serializers from rest_framework import serializers
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin
from apps.wf.serializers import CustomFieldSerializer, StateSerializer, TicketCreateSerializer, TicketFlowSerializer, TicketFlowSimpleSerializer, TicketHandleSerializer, TicketRetreatSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer, TicketListSerializer, TicketDetailSerializer from apps.wf.serializers import CustomFieldSerializer, StateSerializer, TicketAddNodeEndSerializer, TicketAddNodeSerializer, TicketCloseSerializer, TicketCreateSerializer, TicketDestorySerializer, TicketFlowSerializer, TicketFlowSimpleSerializer, TicketHandleSerializer, TicketRetreatSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer, TicketListSerializer, TicketDetailSerializer
from django.shortcuts import get_object_or_404, render from django.shortcuts import get_object_or_404, render
from rest_framework.viewsets import GenericViewSet, ModelViewSet from rest_framework.viewsets import GenericViewSet, ModelViewSet
from rest_framework.decorators import action, api_view from rest_framework.decorators import action, api_view
@ -11,6 +12,7 @@ from apps.wf.models import CustomField, Ticket, Workflow, State, Transition, Tic
from apps.system.mixins import CreateUpdateCustomMixin, CreateUpdateModelAMixin, OptimizationMixin from apps.system.mixins import CreateUpdateCustomMixin, CreateUpdateModelAMixin, OptimizationMixin
from apps.wf.services import WfService from apps.wf.services import WfService
from rest_framework.exceptions import APIException, PermissionDenied from rest_framework.exceptions import APIException, PermissionDenied
from rest_framework import status
# Create your views here. # Create your views here.
class WorkflowViewSet(CreateUpdateModelAMixin, ModelViewSet): class WorkflowViewSet(CreateUpdateModelAMixin, ModelViewSet):
@ -225,7 +227,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
ticket.act_state = Ticket.TICKET_ACT_STATE_ONGOING ticket.act_state = Ticket.TICKET_ACT_STATE_ONGOING
if transition.attribute_type == Transition.TRANSITION_ATTRIBUTE_TYPE_REFUSE: if transition.attribute_type == Transition.TRANSITION_ATTRIBUTE_TYPE_REFUSE:
transition.act_state = Ticket.TICKET_ACT_STATE_BACK ticket.act_state = Ticket.TICKET_ACT_STATE_BACK
# 只更新必填和可选的字段 # 只更新必填和可选的字段
for key, value in ticket.state.state_fields.items(): for key, value in ticket.state.state_fields.items():
@ -312,16 +314,75 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
participant=request.user, transition=None) participant=request.user, transition=None)
return Response() return Response()
@action(methods=['post'], detail=True, perms_map={'post':'*'}) @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=TicketAddNodeSerializer)
def add_node(self, request, pk=None): def add_node(self, request, pk=None):
""" """
加签 加签
""" """
ticket = self.get_object()
data = request.data
add_user = User.objects.get(pk=data['toadd_user'])
ticket.participant_type = State.PARTICIPANT_TYPE_PERSONAL
ticket.participant = add_user.id
ticket.in_add_node = True
ticket.add_node_man = request.user
ticket.save()
# 更新流转记录
suggestion = request.data.get('suggestion', '') # 加签说明
TicketFlow.objects.create(ticket=ticket, state=ticket.state, ticket_data=WfService.get_ticket_all_field_value(ticket),
suggestion=suggestion, participant_type=State.PARTICIPANT_TYPE_PERSONAL, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_ADD_NODE,
participant=request.user, transition=None)
return Response()
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=TicketAddNodeEndSerializer)
def add_node_end(self, request, pk=None):
"""
加签完成
"""
ticket = self.get_object()
ticket.participant_type = State.PARTICIPANT_TYPE_PERSONAL
ticket.in_add_node = False
ticket.participant = ticket.add_node_man.id
ticket.add_node_man = None
ticket.save()
# 更新流转记录
suggestion = request.data.get('suggestion', '') # 加签意见
TicketFlow.objects.create(ticket=ticket, state=ticket.state, ticket_data=WfService.get_ticket_all_field_value(ticket),
suggestion=suggestion, participant_type=State.PARTICIPANT_TYPE_PERSONAL, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_ADD_NODE_END,
participant=request.user, transition=None)
return Response()
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=TicketCloseSerializer)
def close(self, request, pk=None): def close(self, request, pk=None):
""" """
关闭工单(超级管理员或者创建人在初始状态) 关闭工单(创建人在初始状态)
""" """
ticket = self.get_object()
if ticket.state.type == State.STATE_TYPE_START and ticket.create_by==request.user:
end_state = WfService.get_workflow_end_state(ticket.workflow)
ticket.state = end_state
ticket.participant_type = 0
ticket.participant = 0
ticket.act_state = Ticket.TICKET_ACT_STATE_CLOSED
ticket.save()
# 更新流转记录
suggestion = request.data.get('suggestion', '') # 关闭原因
TicketFlow.objects.create(ticket=ticket, state=ticket.state, ticket_data=WfService.get_ticket_all_field_value(ticket),
suggestion=suggestion, participant_type=State.PARTICIPANT_TYPE_PERSONAL, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_CLOSE,
participant=request.user, transition=None)
return Response()
else:
return Response('工单不可关闭', status=status.HTTP_400_BAD_REQUEST)
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=TicketDestorySerializer)
def destory(self, request, pk=None):
"""
批量物理删除
"""
Ticket.objects.filter(id__in=request.data.get('ids', [])).delete(soft=False)
return Response()
class TicketFlowViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet): class TicketFlowViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):

View File

@ -56,7 +56,7 @@ class FitJSONRenderer(JSONRenderer):
data = data[prefix] data = data[prefix]
if isinstance(data, list): if isinstance(data, list):
data = data[0] data = data[0]
response_body.msg = prefix + ":" + data # 取一部分放入msg,方便前端alert response_body.msg = prefix + ":" + str(data) # 取一部分放入msg,方便前端alert
else: else:
response_body.data = data response_body.data = data
renderer_context.get("response").status_code = 200 # 统一成200响应,用code区分 renderer_context.get("response").status_code = 200 # 统一成200响应,用code区分