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

This commit is contained in:
shijing 2021-10-13 09:54:18 +08:00
commit 7cbbdfaf48
24 changed files with 1794 additions and 394 deletions

View File

@ -1,5 +1,6 @@
import request from '@/utils/request' import request from '@/utils/request'
//物料 //物料
export function getMaterialList(query) { export function getMaterialList(query) {
return request({ return request({
url: '/mtm/material/', url: '/mtm/material/',
@ -34,6 +35,36 @@ export function deleteMaterial(id, data) {
data data
}) })
} }
//产品分解
export function getsubproducationList(query) {
return request({
url: '/mtm/subproducation/',
method: 'get',
params: query
})
}
export function createsubproducation(data) {
return request({
url: '/mtm/subproducation/',
method: 'post',
data
})
}
export function updatesubproducation(id, data) {
return request({
url: `/mtm/subproducation/${id}/`,
method: 'put',
data
})
}
export function deletesubproducation(id, data) {
return request({
url: `/mtm/subproducation/${id}/`,
method: 'delete',
data
})
}
//工序 //工序
export function getProcessList(query) { export function getProcessList(query) {
return request({ return request({
@ -64,7 +95,16 @@ export function deleteProcess(id, data) {
}) })
} }
//子工序 //子工序
export function getStepList(id) { export function getStepList(query) {
return request({
url: `/mtm/step/`,
method: 'get',
params: query
})
}
export function getStepLists(id) {
return request({ return request({
url: `/mtm/process/${id}/steps/`, url: `/mtm/process/${id}/steps/`,
method: 'get' method: 'get'

18
hb_client/src/api/pm.js Normal file
View File

@ -0,0 +1,18 @@
import request from '@/utils/request'
//生产排程
export function getProductionplanList(query) {
return request({
url: 'pm/productionplan/',
method: 'get',
params: query
})
}
export function createProductionplan(data) {
return request({
url: 'pm/productionplan/',
method: 'post',
data
})
}

View File

@ -87,3 +87,10 @@ export function deleteOrder(id, data) {
data data
}) })
} }
export function getordertoplan() {
return request({
url: '/sam/order/toplan/',
method: 'get',
})
}

View File

@ -130,6 +130,34 @@ export const asyncRoutes = [
meta: { title: '产品管理', icon: 'example', perms: ['vendor_manage'] } meta: { title: '产品管理', icon: 'example', perms: ['vendor_manage'] }
}, },
] ]
}
,
{
path: '/pm',
component: Layout,
redirect: '/pm/plan',
name: 'pm',
meta: { title: '生产管理', icon: 'example', perms: ['equipment_set'] },
children: [
{
path: 'plan',
name: 'plan',
component: () => import('@/views/pm/plan'),
meta: { title: '生产计划管理', icon: 'example', perms: ['index_manage'] }
},
{
path: 'resources',
name: 'resources',
component: () => import('@/views/pm/resources'),
meta: { title: '生产资源配置', icon: 'example', perms: ['index_manage'] }
},
{
path: 'testitem',
name: 'testitem',
component: () => import('@/views/pm/plan'),
meta: { title: '生产作业管理', icon: 'example', perms: ['index_manage'] }
}
]
}, },
{ {
path: '/em', path: '/em',
@ -233,6 +261,13 @@ export const asyncRoutes = [
name: 'warehouse', name: 'warehouse',
component: () => import('@/views/inm/warehouse'), component: () => import('@/views/inm/warehouse'),
meta: { title: '仓库', icon: 'example', perms: ['index_manage'] } meta: { title: '仓库', icon: 'example', perms: ['index_manage'] }
},
{
path: 'materialbatch/:id',
name: 'MaterialBatch',
component: () => import('@/views/inm/materialbatch'),
meta: { title: '仓库物料', perms: ['vendor_manage'] },
hidden: true
} }
, ,
{ {

View File

@ -0,0 +1,178 @@
<template>
<div class="app-container">
<el-card>
<div>
<el-input
v-model="listQuery.search"
placeholder="仓库名称/仓库编号"
style="width: 300px"
class="filter-item"
@keyup.enter.native="handleFilter"
/>
<el-button
class="filter-item"
type="primary"
icon="el-icon-search"
@click="handleFilter"
>搜索</el-button
>
<el-button
class="filter-item"
type="primary"
icon="el-icon-refresh-left"
@click="resetFilter"
>重置</el-button
>
</div>
</el-card>
<el-card style="margin-top: 10px">
<el-table
v-loading="listLoading"
:data="warehouseList.results"
border
fit
stripe
highlight-current-row
max-height="600"
>
<el-table-column type="index" width="50" />
<el-table-column label="物料名称">
<template slot-scope="scope">{{ scope.row.name }}</template>
</el-table-column>
<el-table-column label="物料批次">
<template slot-scope="scope">{{ scope.row.number }}</template>
</el-table-column>
<el-table-column label="入库数量">
<template slot-scope="scope">{{ scope.row.place }}</template>
</el-table-column>
<el-table-column label="物料有效期">
<template slot-scope="scope">{{ scope.row.place }}</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="220px"
>
<template slot-scope="scope">
<el-link
v-if="checkPermission(['warehouse_delete'])"
type="danger"
@click="handleDelete(scope)"
>删除</el-link
>
</template>
</el-table-column>
</el-table>
<pagination
v-show="warehouseList.count > 0"
:total="warehouseList.count"
:page.sync="listQuery.page"
:limit.sync="listQuery.page_size"
@pagination="getList"
/>
</el-card>
</div>
</template>
<script>
import { getWarehouseList, deleteWarehouse } from "@/api/inm";
import checkPermission from "@/utils/permission";
import { genTree } from "@/utils";
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
const defaultewarehouse = {
};
export default {
components: { Pagination },
data() {
return {
warehouse: defaultewarehouse,
warehouseList: {
count: 0,
},
options: [{
value: 0,
label: '运转正常'
}, {
value: 1,
label: '停用'
}, {
value: 2,
label: '报废'
}],
listQuery: {
page: 1,
page_size: 20,
},
keeperOptions:[],
depOptions:[],
listLoading: true,
dialogVisible: false,
dialogType: "new",
rule1: {
name: [{ required: true, message: "请输入", trigger: "blur" }],
number: [{ required: true, message: "请输入", trigger: "blur" }],
place: [{ required: true, message: "请输入", trigger: "blur" }],
},
};
},
computed: {},
watch: {},
created() {
this.getList();
},
methods: {
checkPermission,
//设备列表
getList() {
this.listLoading = true;
getWarehouseList(this.listQuery).then((response) => {
if (response.data) {
this.warehouseList = response.data;
}
this.listLoading = false;
});
},
handleMaterial(scope){
alert(1)
},
handleFilter() {
this.listQuery.page = 1;
this.getList();
},
resetFilter() {
this.listQuery = {
page: 1,
page_size: 20,
}
this.getList();
},
handleDelete(scope) {
this.$confirm("确认删除?", "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
await deleteWarehouse(scope.row.id);
this.getList();
this.$message.success("成功");
})
.catch((err) => {
console.error(err);
});
},
},
};
</script>

View File

@ -61,7 +61,11 @@
width="220px" width="220px"
> >
<template slot-scope="scope"> <template slot-scope="scope">
<el-link
v-if="checkPermission(['warehouse_update'])"
@click="handleMaterial(scope)"
>查看物料</el-link
>
<el-link <el-link
v-if="checkPermission(['warehouse_update'])" v-if="checkPermission(['warehouse_update'])"
@click="handleEdit(scope)" @click="handleEdit(scope)"
@ -173,8 +177,10 @@ export default {
this.listLoading = false; this.listLoading = false;
}); });
}, },
//跳转到该仓库的物料表
handleMaterial(scope){
this.$router.push({name: "MaterialBatch", params: { id: scope.row.id }, })
},
handleFilter() { handleFilter() {
this.listQuery.page = 1; this.listQuery.page = 1;
this.getList(); this.getList();

View File

@ -1,17 +1,18 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<el-card>
<el-tabs type="border-card">
<el-tab-pane label="过程记录">
<el-row :gutter="24"> <el-row :gutter="24">
<el-col :span="6" > <el-col :span="6" >
<el-card style="margin-top: 10px"> <el-card >
<div slot="header" class="clearfix">
<span style="font-size: 16px;
font-weight: 700;
">过程记录表</span>
</div>
<el-button type="primary" icon="el-icon-plus" @click="handleCreate" <el-button type="primary" icon="el-icon-plus" @click="handleCreate"
>新增</el-button> >新增</el-button>
<el-table <el-table
:data="recordformList.results" :data="recordformList.results"
border border
fit fit
@ -21,34 +22,30 @@
v-el-height-adaptive-table="{bottomOffset: 50}" v-el-height-adaptive-table="{bottomOffset: 50}"
@current-change="handleCurrentChange" @current-change="handleCurrentChange"
> >
<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 <el-table-column
align="center" align="center"
label="操作" label="操作"
> >
<template slot-scope="scope"> <template slot-scope="scope">
<el-link
v-if="checkPermission(['material_update'])"
@click="handleLook(scope)"
>查看</el-link>
<el-link <el-link
v-if="checkPermission(['material_update'])" v-if="checkPermission(['material_update'])"
@click="handleEdit(scope)" @click="handleEdit(scope)"
>编辑</el-link >编辑</el-link>
>
<el-link <el-link
v-if="checkPermission(['material_delete'])" v-if="checkPermission(['material_delete'])"
type="danger" type="danger"
@click="handleDelete(scope)" @click="handleDelete(scope)"
>删除</el-link >删除</el-link>
>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<el-dialog <el-dialog
:visible.sync="dialogVisible" :visible.sync="dialogVisible"
@ -74,11 +71,80 @@
<el-button type="primary" @click="recordformconfirm('Forms')">确认</el-button> <el-button type="primary" @click="recordformconfirm('Forms')">确认</el-button>
</div> </div>
</el-dialog> </el-dialog>
<el-dialog
:model="tableForm"
:visible.sync="dialogVisibleForm"
:title="tableForm.name">
<el-form
label-width="80px"
label-position="right"
>
<el-row v-for="(item,$index) in fieldList.results" :key="$index">
<el-form-item v-if="item.field_type==='string'" :label="item.field_name" >
<el-input placeholder="请输入" v-model="item.sort"/>
</el-form-item>
<el-form-item v-else-if="item.field_type==='int'" :label="item.field_name">
<el-input type="number" placeholder="请输入" v-model="item.sort"/>
</el-form-item>
<el-form-item v-else-if="item.field_type==='float'" :label="item.field_name">
<el-input type="number" placeholder="请输入" v-model="item.sort"/>
</el-form-item>
<el-form-item v-else-if="item.field_type==='date'" :label="item.field_name">
<el-date-picker
v-model="item.create_time"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
style="width:100%"
>
</el-date-picker>
</el-form-item>
<el-form-item v-else-if="item.field_type==='datetime'" :label="item.field_name">
<el-date-picker
v-model="item.create_time"
type="datetime"
placeholder="选择日期"
value-format="yyyy-MM-dd HH:mm:ss"
style="width:100%"
>
</el-date-picker>
</el-form-item>
<el-form-item v-else-if="item.field_type==='select'" :label="item.field_name">
<el-select style="width: 100%" v-model="item.sort" placeholder="请选择">
<el-option
v-for="item1 in item.field_choice"
:key="item1"
:label="item1"
:value="item1">
</el-option>
</el-select>
</el-form-item>
<el-form-item v-else-if="item.field_type==='selects'" :label="item.field_name">
<el-select style="width: 100%" v-model="optio" multiple placeholder="请选择">
<el-option
v-for="item1 in item.field_choice"
:key="item1"
:label="item1"
:value="item1">
</el-option>
</el-select>
</el-form-item>
</el-row>
</el-form>
<!--<div style="text-align: right">-->
<!--<el-button type="danger">取消</el-button>-->
<!--<el-button type="primary">确认</el-button>-->
<!--</div>-->
</el-dialog>
</el-card> </el-card>
</el-col> </el-col>
<el-col :span="18" > <el-col :span="18" >
<el-card class="box-card"> <el-card >
<div slot="header" class="clearfix">
<span style="font-size: 16px;
font-weight: 700;
">记录字段</span>
</div>
<el-button type="primary" icon="el-icon-plus" @click="handlefieldCreate" <el-button type="primary" icon="el-icon-plus" @click="handlefieldCreate"
>新增</el-button> >新增</el-button>
<el-table <el-table
@ -105,9 +171,7 @@
<el-table-column label="字段标识"> <el-table-column label="字段标识">
<template slot-scope="scope">{{ scope.row.field_key }}</template> <template slot-scope="scope">{{ scope.row.field_key }}</template>
</el-table-column> </el-table-column>
<el-table-column label="布尔类型显示名">
<template slot-scope="scope">{{ scope.row.boolean_field_display }}</template>
</el-table-column>
<el-table-column label="选项显示名"> <el-table-column label="选项显示名">
<template slot-scope="scope">{{ scope.row.field_choice }}</template> <template slot-scope="scope">{{ scope.row.field_choice }}</template>
</el-table-column> </el-table-column>
@ -135,17 +199,8 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
<el-dialog <el-dialog :visible.sync="dialogVisible1" :title="dialogType1 === 'edit' ? '编辑表格字段' : '新增表格字段'">
:visible.sync="dialogVisible1" <el-form ref="Form" :model="field" label-width="80px" label-position="right">
:title="dialogType1 === 'edit' ? '编辑表格字段' : '新增表格字段'"
>
<el-form
ref="Form"
:model="field"
label-width="80px"
label-position="right"
>
<el-form-item label="字段类型" prop="field_type"> <el-form-item label="字段类型" prop="field_type">
<el-select style="width: 100%" v-model="field.field_type" placeholder="请选择"> <el-select style="width: 100%" v-model="field.field_type" placeholder="请选择">
<el-option <el-option
@ -157,36 +212,28 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="字段标识" prop="field_key"> <el-form-item label="字段标识" prop="field_key">
<el-input v-model="field.field_key" placeholder="字段标识" /> <el-input v-model="field.field_key" placeholder="字段标识" onkeyup="value=value.replace(/[^A-Za-z_\/]/ig,'')"/>
</el-form-item> </el-form-item>
<el-form-item label="字段名称" prop="field_name"> <el-form-item label="字段名称" prop="field_name">
<el-input v-model="field.field_name" placeholder="字段名称" /> <el-input v-model="field.field_name" placeholder="字段名称" />
</el-form-item> </el-form-item>
<el-form-item label="布尔类型显示名" prop="boolean_field_display"> <el-form-item label="选项" v-show="field.field_type=='radio'||field.field_type=='checkbox'||field.field_type=='select'||field.field_type=='selects'">
<vue-json-editor <el-button @click.prevent="addDomain" style="border: none;">
v-model="field.boolean_field_display" <i class="el-icon-circle-plus-outline"></i>
:showBtns="false" <span style="font-size:14px;">添加</span>
:mode="'code'" </el-button>
lang="zh" <el-row v-for="(domain, $index) in field_choice" :key=domain+$index style="margin-bottom: 10px">
<el-col :span="20">
/> <el-input v-model="field_choice[$index]" auto-complete="off"></el-input>
</el-form-item> </el-col>
<el-form-item label="选项" prop="field_choice"> <el-col :span="3" style="text-align: center" v-if="$index!==0">
<i class="el-icon-remove-outline" @click.prevent="removeDomain($index,'1')" style="color: red;font-size: 16px;"></i>
<vue-json-editor </el-col>
v-model="field.field_choice" </el-row>
:showBtns="false"
:mode="'code'"
lang="zh"
/>
</el-form-item> </el-form-item>
<el-form-item label="排序" prop="sort"> <el-form-item label="排序" prop="sort">
<el-input v-model="field.sort" placeholder="排序" /> <el-input-number v-model="field.sort" :min="1" placeholder="排序"></el-input-number>
</el-form-item> </el-form-item>
</el-form> </el-form>
<div style="text-align: right"> <div style="text-align: right">
<el-button type="danger" @click="dialogVisible1 = false">取消</el-button> <el-button type="danger" @click="dialogVisible1 = false">取消</el-button>
@ -194,23 +241,27 @@
</div> </div>
</el-dialog> </el-dialog>
</el-card> </el-card>
</el-col> </el-col>
</el-row> </el-row>
</el-tab-pane>
</el-tabs>
</el-card> </el-card>
</div> </div>
</template> </template>
<script>
import { getStep,getrecordformList,createrecordform,updaterecordform,deleterecordform,getrffieldList,createrffield,updaterffield,
deleterffield} from "@/api/mtm";
import vueJsonEditor from 'vue-json-editor' <script>
import checkPermission from "@/utils/permission"; import checkPermission from "@/utils/permission";
import { getEquipmentAll } from "@/api/equipment";
import { upUrl, upHeaders } from "@/api/file";
import {getrecordformList,createrecordform,updaterecordform,deleterecordform,getrffieldList,createrffield,updaterffield,
deleterffield} from "@/api/mtm";
import vueJsonEditor from 'vue-json-editor'
import { genTree } from "@/utils";
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
const defaultstep = {
name: "",
number: "",
};
const defaultrecordform = { const defaultrecordform = {
}; };
@ -218,17 +269,40 @@
}; };
export default { export default {
components: { vueJsonEditor }, components: { Pagination,vueJsonEditor },
data() { data() {
return { return {
step: defaultstep,
stepList:[],
upHeaders: upHeaders(),
upUrl: upUrl(),
fileList:[],
listLoading: true,
dialogVisibles: false,
dialogVisibleForm: false,
dialogTypes: "new",
field: {
field_type:'',
field_key:'',
field_name:'',
sort:'',
field_choice:[""],
},
field_choice:[''],
options:[],
optio:[],
rule1: {
name: [{ required: true, message: "请输入", trigger: "blur" }],
number: [{ required: true, message: "请输入", trigger: "blur" }],
},
recordform: defaultrecordform, recordform: defaultrecordform,
field: defaultfield,
dialogType: "new", dialogType: "new",
dialogVisible:false, dialogVisible:false,
dialogType1: "new", dialogType1: "new",
dialogVisible1:false, dialogVisible1:false,
tableForm:{
name:'',
},
listQueryrecordform: { listQueryrecordform: {
page: 1, page: 1,
page_size: 20, page_size: 20,
@ -244,78 +318,81 @@
page_size: 20, page_size: 20,
}, },
options_: { options_: {
'string':'字符串', 'string':'文本',
'int':'整型', 'int':'整数',
'float': '浮点', 'float':'小数',
'boolean':'布尔',
'date': '日期', 'date': '日期',
'datetime': '日期时间', 'datetime': '日期时间',
'radio': '单选', 'select': '单选',
'checkbox': '多选', 'selects': '多选',
'select': '单选下拉',
'selects': '多选下拉',
'textarea': '文本域'
}, },
fieldtypeoptions: [{ fieldtypeoptions: [{
value: 'string', value: 'string',
label: '字符串' label: '文本'
}, },
{ {
value: 'int', value: 'int',
label: '' label: ''
}, },
{ {
value: 'float', value: 'float',
label: '浮点' label: '小数'
},
{
value: 'boolean',
label: '布尔'
}, },
{ {
value: 'date', value: 'date',
label: '日期' label: '日期'
}, },
{
value: 'time',
label: '时间'
},
{ {
value: 'datetime', value: 'datetime',
label: '日期时间' label: '日期时间'
}, },
{ {
value: 'radio', value: 'select',
label: '单选' label: '单选'
}, },
{
value: 'checkbox',
label: '多选'
},
{
value: 'select',
label: '单选下拉'
},
{ {
value: 'selects', value: 'selects',
label: '多选下拉' label: '多选'
}, }
{ ],
value: 'textarea', typeoptions: [{
label: '文本域' value: 1,
label: '生产记录'
}], }],
}; };
}, },
computed: {}, computed: {},
watch: { watch: {},
},
created() { created() {
this.step.process = this.$route.params.id;
this.recordformLists(); this.recordformLists();
this.material = this.$route.params.id;
}, },
methods: { methods: {
//添加字段选项
addDomain() {
this.field_choice.push('')
},
//删除字段选项
removeDomain(index){
this.field_choice.splice(index, 1)
},
handleLook(scope){
debugger;
console.log(scope);
this.dialogVisibleForm = true;
this.tableForm = Object.assign({}, scope.row); // copy obj
this.formID=this.tableForm.id;
this.fieldLists();
},
checkPermission, checkPermission,
handleCurrentChange(row){ handleCurrentChange(row){
this.formID=row.id; this.formID=row.id;
this.fieldLists(); this.fieldLists();
@ -323,9 +400,8 @@
}, },
recordformLists() recordformLists()
{ {
this.listQueryrecordform.material = this.material; this.listQueryrecordform.step=this.stepid;
console.log(this.materialid) this.listQueryrecordform.type=2;
this.listQueryrecordform.type = 2;
getrecordformList(this.listQueryrecordform).then((response) => { getrecordformList(this.listQueryrecordform).then((response) => {
if (response.data) { if (response.data) {
this.recordformList = response.data; this.recordformList = response.data;
@ -339,10 +415,13 @@
getrffieldList(this.listQueryfield).then((response) => { getrffieldList(this.listQueryfield).then((response) => {
if (response.data) { if (response.data) {
this.fieldList = response.data; this.fieldList = response.data;
debugger;
console.log(this.fieldList)
} }
}); });
}, },
//新增记录表
handleCreate() { handleCreate() {
this.recordform = Object.assign({}, defaultrecordform); this.recordform = Object.assign({}, defaultrecordform);
this.dialogType = "new"; this.dialogType = "new";
@ -351,7 +430,10 @@
this.$refs["Forms"].clearValidate(); this.$refs["Forms"].clearValidate();
}); });
}, },
//新增字段
handlefieldCreate() { handlefieldCreate() {
this.field_choice = [''];
this.field = Object.assign({}, defaultfield); this.field = Object.assign({}, defaultfield);
this.dialogType1 = "new"; this.dialogType1 = "new";
this.dialogVisible1 = true; this.dialogVisible1 = true;
@ -369,6 +451,7 @@
}, },
handlefieldEdit(scope) { handlefieldEdit(scope) {
this.field = Object.assign({}, scope.row); // copy obj this.field = Object.assign({}, scope.row); // copy obj
this.field_choice = this.field.field_choice;
this.dialogType1 = "edit"; this.dialogType1 = "edit";
this.dialogVisible1 = true; this.dialogVisible1 = true;
this.$nextTick(() => { this.$nextTick(() => {
@ -410,7 +493,7 @@
if (valid) { if (valid) {
const isEdit = this.dialogType === "edit"; const isEdit = this.dialogType === "edit";
if (isEdit) { if (isEdit) {
this.recordform.material=this.material; this.recordform.step=this.stepid;
this.recordform.type=2; this.recordform.type=2;
updaterecordform(this.recordform.id, this.recordform).then((res) => { updaterecordform(this.recordform.id, this.recordform).then((res) => {
if (res.code >= 200) { if (res.code >= 200) {
@ -420,7 +503,7 @@
} }
}); });
} else { } else {
this.recordform.material=this.material; this.recordform.step=this.stepid
this.recordform.type=2; this.recordform.type=2;
createrecordform(this.recordform).then((res) => { createrecordform(this.recordform).then((res) => {
if (res.code >= 200) { if (res.code >= 200) {
@ -440,8 +523,8 @@
if (valid) { if (valid) {
const isEdit = this.dialogType1 === "edit"; const isEdit = this.dialogType1 === "edit";
if (isEdit) { if (isEdit) {
this.field.form=this.formID this.field.form=this.formID;
this.field.field_choice = this.field_choice;
updaterffield(this.field.id, this.field).then((res) => { updaterffield(this.field.id, this.field).then((res) => {
if (res.code >= 200) { if (res.code >= 200) {
this.fieldLists() this.fieldLists()
@ -450,8 +533,8 @@
} }
}); });
} else { } else {
this.field.form=this.formID this.field.form=this.formID;
this.field.field_choice = this.field_choice;
createrffield(this.field).then((res) => { createrffield(this.field).then((res) => {
if (res.code >= 200) { if (res.code >= 200) {
this.fieldLists() this.fieldLists()

View File

@ -15,8 +15,7 @@
fit fit
stripe stripe
highlight-current-row highlight-current-row
height="100"
v-el-height-adaptive-table="{bottomOffset: 50}"
@current-change="handleCurrentChange"> @current-change="handleCurrentChange">
<el-table-column type="index" width="50" /> <el-table-column type="index" width="50" />
@ -30,9 +29,6 @@
</el-table> </el-table>
</el-card> </el-card>
</el-col>
<el-col :span="18" >
<el-card > <el-card >
<el-descriptions class="margin-top" title="产品信息" :column="3" border> <el-descriptions class="margin-top" title="产品信息" :column="3" border>
@ -56,7 +52,9 @@
</el-descriptions> </el-descriptions>
</el-card> </el-card>
<el-card class="box-card"> </el-col>
<el-col :span="18" >
<el-card class="box-card">
<div slot="header" class="clearfix"> <div slot="header" class="clearfix">
<span style="font-size: 16px; <span style="font-size: 16px;
font-weight: 700; font-weight: 700;
@ -68,6 +66,87 @@
</el-step> </el-step>
</el-steps> </el-steps>
</el-card> </el-card>
<el-card class="box-card">
<div slot="header" class="clearfix">
<span style="font-size: 16px;
font-weight: 700;
">生产分解</span>
</div>
<el-button type="primary" icon="el-icon-plus" @click="handlesubproducationCreate"
>新增</el-button>
<el-table
:data="subproducationData"
border
fit
stripe
@current-change="handlespChange"
>
<el-table-column type="index" width="50" />
<el-table-column label="物料编号">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<el-table-column label="单位消耗量">
<template slot-scope="scope">{{ scope.row.sort }}</template>
</el-table-column>
<el-table-column
align="center"
label="操作"
width="220px"
>
<template slot-scope="scope">
<el-link
v-if="checkPermission(['material_update'])"
@click="handlesubproducationEdit(scope)"
>编辑</el-link
>
<el-link
v-if="checkPermission(['material_delete'])"
type="danger"
@click="handlesubproducationDelete(scope)"
>删除</el-link
>
</template>
</el-table-column>
</el-table>
<el-dialog
:visible.sync="dialogVisiblesp"
:title="dialogTypesp === 'edit' ? '编辑生产分解' : '新增生产分解'"
>
<el-form
ref="Formsp"
:model="subproducation"
label-width="80px"
label-position="right"
>
<el-form-item label="名称" prop="name">
<el-input v-model="subproducation.name" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number
v-model="subproducation.sort"
:min="-2147483648"
:max="2147483647"
></el-input-number>
</el-form-item>
</el-form>
<div style="text-align: right">
<el-button type="danger" @click="dialogVisible1 = false">取消</el-button>
<el-button type="primary" @click="subproducationfirm('Formsp')">确认</el-button>
</div>
</el-dialog>
</el-card>
<el-tabs type="border-card"> <el-tabs type="border-card">
<el-tab-pane label="输入物料"> <el-tab-pane label="输入物料">
@ -272,7 +351,9 @@
<el-table-column label="子工序名称"> <el-table-column label="子工序名称">
<template slot-scope="scope">{{ scope.row.step_.name }}</template> <template slot-scope="scope">{{ scope.row.step_.name }}</template>
</el-table-column > </el-table-column >
<el-table-column label="备注">
<template slot-scope="scope">{{ scope.row.remark }}</template>
</el-table-column >
<el-table-column <el-table-column
align="center" align="center"
@ -318,7 +399,9 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="usedstep.remark" placeholder="输入备注信息" />
</el-form-item>
</el-form> </el-form>
<div style="text-align: right"> <div style="text-align: right">
@ -327,10 +410,7 @@
</div> </div>
</el-dialog> </el-dialog>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="检验要求">
<el-button type="primary" icon="el-icon-plus"
>新增</el-button>
</el-tab-pane>
<el-tab-pane label="技术文件"> <el-tab-pane label="技术文件">
<el-button type="primary" icon="el-icon-plus" @click="handletechdocCreate" <el-button type="primary" icon="el-icon-plus" @click="handletechdocCreate"
>新增</el-button> >新增</el-button>
@ -426,7 +506,8 @@
<script> <script>
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 } from "@/api/mtm"; getUsedstepList,createUsedstep,deleteUsedstep,getStepList,gettechdocList,createtechdoc,updatetechdoc,deletetechdoc
,getsubproducationList,createsubproducation,updatesubproducation,deletesubproducation } 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'
@ -446,6 +527,9 @@ const defaultusedstep = {
}; };
const defaulttechdoc = { const defaulttechdoc = {
};
const defaultsubproducation = {
}; };
export default { export default {
@ -454,9 +538,11 @@ export default {
data() { data() {
return { return {
materialoptions:[], materialoptions:[],
subproducationData:"",
inputtableData:"", inputtableData:"",
editorOption: {} , editorOption: {} ,
techdoc: defaulttechdoc, techdoc: defaulttechdoc,
subproducation:defaultsubproducation,
inputmaterial: defaultinputmaterial, inputmaterial: defaultinputmaterial,
techdoctableData:"", techdoctableData:"",
outputtableData:"", outputtableData:"",
@ -475,6 +561,9 @@ export default {
page: 1, page: 1,
page_size: 20, page_size: 20,
}, },
listQuerysubproducation: {
page: 0,
},
listQueryinput: { listQueryinput: {
page: 0, page: 0,
}, },
@ -487,10 +576,15 @@ export default {
listQuerytechdoc:{ listQuerytechdoc:{
page: 0, page: 0,
}, },
listQuerystep:{
page: 0,
},
values:7, values:7,
products:"", products:"",
dialogType: "new", dialogType: "new",
dialogVisible:false, dialogVisible:false,
dialogTypesp: "new",
dialogVisiblesp:false,
dialogType1: "new", dialogType1: "new",
dialogVisible1:false, dialogVisible1:false,
dialogTypeusedstep: "new", dialogTypeusedstep: "new",
@ -545,8 +639,19 @@ export default {
handleCurrentChange(row){ handleCurrentChange(row){
this.product=row.id; this.product=row.id;
this.getMaterial(); this.getMaterial();
this.getsubproducationList();
}, },
//点击产品分解弹出输入输出物料子工序技术文件
handlespChange(row){
this.subproduction = row.id;
this.getmaterialList();//物料列表
this.getInputmaterialLists();//输入物料
this.getOutputmaterialLists();//输出物料
this.getstepList();//子工序
this. getUsedstepLists();//
this.gettechdocLists();//技术文件
},
//工艺点击信息 //工艺点击信息
stepclick(id) stepclick(id)
@ -563,15 +668,84 @@ export default {
}, },
//产品分解
getsubproducationList(){
this.listQuerysubproducation.product=this.product
getsubproducationList(this.listQuerysubproducation).then((response) => {
if (response.data) {
this.subproducationData = response.data;//产品信息
}
})
},
handlesubproducationCreate()
{
this.subproducation = Object.assign({}, defaultsubproducation);
this.dialogTypesp = "new";
this.dialogVisiblesp = true;
this.$nextTick(() => {
this.$refs["Formsp"].clearValidate();
});
},
handlesubproducationEdit(scope) {
this.subproducation = Object.assign({}, scope.row); // copy obj
this.dialogTypesp = "edit";
this.dialogVisiblesp = true;
this.$nextTick(() => {
this.$refs["Formsp"].clearValidate();
});
},
handlesubproducationDelete(scope) {
this.$confirm("确认删除?", "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
await deletesubproducation(scope.row.id);
this.getsubproducationList()
this.$message.success("成功");
})
.catch((err) => {
console.error(err);
});
},
async subproducationfirm(form) {
this.$refs[form].validate((valid) => {
if (valid) {
const isEdit = this.dialogTypesp === "edit";
if (isEdit) {
this.subproducation.product=this.product;
updatesubproducation(this.subproducation.id, this.subproducation).then((res) => {
if (res.code >= 200) {
this.getsubproducationList()
this.dialogVisiblesp = false;
this.$message.success("成功");
}
});
} else {
this.subproducation.product=this.product;
createsubproducation(this.subproducation).then((res) => {
if (res.code >= 200) {
this.getsubproducationList()
this.dialogVisiblesp = false;
this.$message.success("成功");
}
});
}
} else {
return false;
}
});
},
//输入物料列表 //输入物料列表
getInputmaterialLists(){ getInputmaterialLists(){
this.listQueryinput.process=this.process; this.listQueryinput.subproduction=this.subproduction;
this.listQueryinput.product=this.product
getInputmaterialList(this.listQueryinput).then((response) => { getInputmaterialList(this.listQueryinput).then((response) => {
if (response.data) { if (response.data) {
@ -617,8 +791,7 @@ export default {
if (valid) { if (valid) {
const isEdit = this.dialogType === "edit"; const isEdit = this.dialogType === "edit";
if (isEdit) { if (isEdit) {
this.inputmaterial.process=this.process; this.inputmaterial.subproduction=this.subproduction;
this.inputmaterial.product=this.product;
updateInputmaterial(this.inputmaterial.id, this.inputmaterial).then((res) => { updateInputmaterial(this.inputmaterial.id, this.inputmaterial).then((res) => {
if (res.code >= 200) { if (res.code >= 200) {
this.getInputmaterialLists() this.getInputmaterialLists()
@ -627,8 +800,7 @@ export default {
} }
}); });
} else { } else {
this.inputmaterial.process=this.process; this.inputmaterial.subproduction=this.subproduction;
this.inputmaterial.product=this.product;
createInputmaterial(this.inputmaterial).then((res) => { createInputmaterial(this.inputmaterial).then((res) => {
if (res.code >= 200) { if (res.code >= 200) {
this.getInputmaterialLists() this.getInputmaterialLists()
@ -650,8 +822,7 @@ export default {
getOutputmaterialLists(){ getOutputmaterialLists(){
this.listQueryoutput.process=this.process; this.listQueryoutput.subproduction=this.subproduction;
this.listQueryoutput.product=this.product
// this.listQueryoutput.page=0; // this.listQueryoutput.page=0;
getOutputmaterialList(this.listQueryoutput).then((response) => { getOutputmaterialList(this.listQueryoutput).then((response) => {
if (response.data) { if (response.data) {
@ -700,8 +871,7 @@ export default {
if (valid) { if (valid) {
const isEdit = this.dialogType1 === "edit"; const isEdit = this.dialogType1 === "edit";
if (isEdit) { if (isEdit) {
this.outputmaterial.process=this.process; this.outputmaterial.subproduction=this.subproduction;
this.outputmaterial.product=this.product;
updateOutputmaterial(this.outputmaterial.id, this.outputmaterial).then((res) => { updateOutputmaterial(this.outputmaterial.id, this.outputmaterial).then((res) => {
if (res.code >= 200) { if (res.code >= 200) {
this.getOutputmaterialLists() this.getOutputmaterialLists()
@ -710,8 +880,7 @@ export default {
} }
}); });
} else { } else {
this.outputmaterial.process=this.process; this.outputmaterial.subproduction=this.subproduction;
this.outputmaterial.product=this.product;
console.log(this.outputmaterial); console.log(this.outputmaterial);
createOutputmaterial(this.outputmaterial).then((res) => { createOutputmaterial(this.outputmaterial).then((res) => {
if (res.code >= 200) { if (res.code >= 200) {
@ -730,8 +899,7 @@ export default {
getUsedstepLists(){ getUsedstepLists(){
this.listQueryusedstep.process=this.process; this.listQueryusedstep.subproduction=this.subproduction;
this.listQueryusedstep.product=this.product
// this.listQueryusedstep.page=0; // this.listQueryusedstep.page=0;
getUsedstepList(this.listQueryusedstep).then((response) => { getUsedstepList(this.listQueryusedstep).then((response) => {
if (response.data) { if (response.data) {
@ -744,7 +912,7 @@ export default {
getstepList() { getstepList() {
getStepList(this.process).then((response) => { getStepList(this.listQuerystep).then((response) => {
if (response.data) { if (response.data) {
this.stepoptions = genTree(response.data); this.stepoptions = genTree(response.data);
} }
@ -794,12 +962,12 @@ export default {
}, },
async usedstepconfirm(form) { async usedstepconfirm(form) {
this.usedstep.process=this.process; this.usedstep.subproduction=this.subproduction;
this.usedstep.product=this.product;
console.log(this.usedstep); console.log(this.usedstep);
createUsedstep(this.usedstep).then((res) => { createUsedstep(this.usedstep).then((res) => {
if (res.code >= 200) { if (res.code >= 200) {
this.getUsedstepLists() this.getUsedstepLists();
this.getMaterial();
this.dialogVisibleusedstep = false; this.dialogVisibleusedstep = false;
this.$message.success("成功"); this.$message.success("成功");
} }
@ -807,8 +975,7 @@ export default {
}, },
//技术文件 //技术文件
gettechdocLists(){ gettechdocLists(){
this.listQuerytechdoc.process=this.process; this.listQuerytechdoc.subproduction=this.subproduction;
this.listQuerytechdoc.product=this.product;
// this.listQuerytechdoc.page=0; // this.listQuerytechdoc.page=0;
gettechdocList(this.listQuerytechdoc).then((response) => { gettechdocList(this.listQuerytechdoc).then((response) => {
if (response.data) { if (response.data) {
@ -887,8 +1054,7 @@ export default {
} }
}); });
} else { } else {
this.techdoc.process=this.process; this.techdoc.subproduction=this.subproduction;
this.techdoc.product=this.product;
createtechdoc(this.techdoc).then((res) => { createtechdoc(this.techdoc).then((res) => {
if (res.code >= 200) { if (res.code >= 200) {
this.gettechdocLists(); this.gettechdocLists();

View File

@ -356,7 +356,7 @@
</template> </template>
<script> <script>
import { getStepList, createStep,updateStep,deleteStep } from "@/api/mtm"; import { getStepLists, createStep,updateStep,deleteStep } from "@/api/mtm";
import checkPermission from "@/utils/permission"; import checkPermission from "@/utils/permission";
import { getEquipmentAll } from "@/api/equipment"; import { getEquipmentAll } from "@/api/equipment";
import { upUrl, upHeaders } from "@/api/file"; import { upUrl, upHeaders } from "@/api/file";
@ -502,7 +502,7 @@
//子工序列表 //子工序列表
getList() { getList() {
this.listLoading = true; this.listLoading = true;
getStepList(this.step.process).then((response) => { getStepLists(this.step.process).then((response) => {
if (response.data) { if (response.data) {
this.stepList = response.data; this.stepList = response.data;
} }

View File

@ -0,0 +1,288 @@
<template>
<div class="app-container">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>生产任务列表</span>
</div>
<el-table
:data="productionplanList.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.number }}</template>
</el-table-column>
<el-table-column label="生产订单编号">
<template slot-scope="scope">{{ scope.row.order_.number }}</template>
</el-table-column>
<el-table-column label="合同编号">
<template slot-scope="scope">{{ scope.row.order_.contract_.number }}</template>
</el-table-column>
<el-table-column label="客户名称">
<template slot-scope="scope">{{ scope.row.order_.customer_.name }}</template>
</el-table-column>
<el-table-column label="产品信息">
<template slot-scope="scope">{{ scope.row.product_.number }}-{{ 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.product_.unit }}</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.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.order_.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="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="productionplanList.count > 0"
:total="productionplanList.count"
:page.sync="listQuery1.page"
:limit.sync="listQuery1.page_size"
@pagination="getplanList"
/>
</el-card>
<el-tabs type="border-card">
<el-tab-pane label="订单排产">
<el-table
:data="orderList.results"
border
fit
stripe
style="width: 100%"
>
<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.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>
</el-table-column>
<el-table-column label="已派数量">
<template slot-scope="scope">{{ scope.row.planed_count }}</template>
</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="220px"
>
<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="orderList.count > 0"
:total="orderList.count"
:page.sync="listQuery.page"
:limit.sync="listQuery.page_size"
@pagination="getorderList"
/>
<el-dialog
:visible.sync="dialogVisible"
:title="'排产计划'"
>
<el-form
ref="Form"
:model="orderplan"
label-width="120px"
label-position="right"
:rules="rule1"
>
<el-form-item label="生产计划编号" prop="number">
<el-input v-model="orderplan.number" placeholder="生产计划编号" />
</el-form-item>
<el-form-item label="排产数量" prop="count">
<el-input type="number" v-model.number="orderplan.count"/>
</el-form-item>
<el-form-item label="计划排产时间" prop="value1">
<el-date-picker
v-model="value1"
type="daterange"
start-placeholder="计划开始日期"
end-placeholder="计划结束日期"
format="yyyy 年 MM 月 dd 日"
value-format="yyyy-MM-dd"
>
</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>
</el-tab-pane>
<el-tab-pane label="甘特图">甘特图</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import { getordertoplan } from "@/api/sam";
import { createProductionplan,getProductionplanList} from "@/api/pm";
import { getMaterialList } from "@/api/mtm";
import checkPermission from "@/utils/permission";
import { genTree } from "@/utils";
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
const defaulteorderplan = {
};
export default {
components: { Pagination },
data() {
return {
orderplan: defaulteorderplan,
orderList: {
count: 0,
},
listQuery: {
page: 1,
page_size: 20,
},
productionplanList: {
count: 0,
},
listQuery1: {
page: 1,
page_size: 20,
},
value1: '',
listLoading: true,
dialogVisible: false,
dialogType: "new",
rule1: {
number: [{ required: true, message: "请输入", trigger: "blur" }],
},
};
},
computed: {},
watch: {},
created() {
this.getorderList();
this.getplanList();
},
methods: {
checkPermission,
//订单列表
getorderList() {
this.listLoading = true;
getordertoplan(this.listQuery).then((response) => {
if (response.data) {
this.orderList = response.data;
}
this.listLoading = false;
});
},
//生产计划列表
//列表
getplanList() {
this.listLoading = true;
getProductionplanList(this.listQuery).then((response) => {
if (response.data) {
this.productionplanList = response.data;
}
this.listLoading = false;
});
},
handleclick(scope){
this.orderID = scope.row.id;
this.dialogVisible = true;
this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});
},
async confirm(form) {
this.orderplan.start_date = this.value1[0];
this.orderplan.end_date = this.value1[1];
this.orderplan.order = this.orderID
createProductionplan(this.orderplan).then((res) => {
if (res.code >= 200) {
this.getorderList();
this.getplanList();
this.dialogVisible = false;
this.$message.success("成功");
}
});
},
},
};
</script>

View File

@ -0,0 +1,308 @@
<template>
<div class="app-container">
<el-row>
<el-col :span="12">
<el-card >
<div slot="header" class="clearfix">
<span>合同订单列表</span>
</div>
<el-table
:data="orderList.results"
border
fit
stripe
style="width: 100%"
>
<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.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>
</el-table-column>
<el-table-column label="已派数量">
<template slot-scope="scope">{{ scope.row.planed_count }}</template>
</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>
<pagination
v-show="orderList.count > 0"
:total="orderList.count"
:page.sync="listQuery.page"
:limit.sync="listQuery.page_size"
@pagination="getorderList"
/>
</el-card>
</el-col>
<el-col :span="12">
<el-row>
<el-col :span="24">
<el-card >
<div slot="header" class="clearfix">
<span>物料配置</span>
</div>
<el-table
:data="orderList.results"
border
fit
stripe
style="width: 100%"
>
<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.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>
</el-table-column>
<el-table-column label="已派数量">
<template slot-scope="scope">{{ scope.row.planed_count }}</template>
</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>
<pagination
v-show="orderList.count > 0"
:total="orderList.count"
:page.sync="listQuery.page"
:limit.sync="listQuery.page_size"
@pagination="getorderList"
/>
</el-card>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-card >
<div slot="header" class="clearfix">
<span>设备配置</span>
</div>
<el-table
:data="orderList.results"
border
fit
stripe
style="width: 100%"
>
<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.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>
</el-table-column>
<el-table-column label="已派数量">
<template slot-scope="scope">{{ scope.row.planed_count }}</template>
</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>
<pagination
v-show="orderList.count > 0"
:total="orderList.count"
:page.sync="listQuery.page"
:limit.sync="listQuery.page_size"
@pagination="getorderList"
/>
</el-card>
</el-col>
</el-row>
</el-col>
</el-row>
</div>
</template>
<script>
import { getordertoplan } from "@/api/sam";
import { createProductionplan,getProductionplanList} from "@/api/pm";
import { getMaterialList } from "@/api/mtm";
import checkPermission from "@/utils/permission";
import { genTree } from "@/utils";
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
const defaulteorderplan = {
};
export default {
components: { Pagination },
data() {
return {
orderplan: defaulteorderplan,
orderList: {
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.getorderList();
},
methods: {
checkPermission,
//订单列表
getorderList() {
this.listLoading = true;
getordertoplan(this.listQuery).then((response) => {
if (response.data) {
this.orderList = response.data;
}
this.listLoading = false;
});
},
handleclick(scope){
this.orderID = scope.row.id;
this.dialogVisible = true;
this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});
},
async confirm(form) {
this.orderplan.start_date = this.value1[0];
this.orderplan.end_date = this.value1[1];
this.orderplan.order = this.orderID
createProductionplan(this.orderplan).then((res) => {
if (res.code >= 200) {
this.getorderList();
this.getplanList();
this.dialogVisible = false;
this.$message.success("成功");
}
});
},
},
};
</script>

View File

@ -94,24 +94,6 @@ class Migration(migrations.Migration):
'verbose_name_plural': '操作记录条目', 'verbose_name_plural': '操作记录条目',
}, },
), ),
migrations.CreateModel(
name='ProductProcess',
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='删除标记')),
('sort', models.IntegerField(default=1, verbose_name='排序号')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='productprocess_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('process', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.process', verbose_name='工序')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='产品')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='productprocess_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'verbose_name': '产品生产工序',
'verbose_name_plural': '产品生产工序',
},
),
migrations.CreateModel( migrations.CreateModel(
name='OutputMaterial', name='OutputMaterial',
fields=[ fields=[

View File

@ -0,0 +1,137 @@
# Generated by Django 3.2.6 on 2021-10-12 01:01
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('mtm', '0018_material_count'),
]
operations = [
migrations.CreateModel(
name='SubProduction',
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='删除标记')),
('name', models.CharField(blank=True, max_length=50, null=True, verbose_name='命名')),
('sort', models.IntegerField(default=1, verbose_name='排序号')),
],
options={
'verbose_name': '产品生产工序',
'verbose_name_plural': '产品生产工序',
},
),
migrations.RemoveField(
model_name='inputmaterial',
name='create_by',
),
migrations.RemoveField(
model_name='inputmaterial',
name='process',
),
migrations.RemoveField(
model_name='inputmaterial',
name='product',
),
migrations.RemoveField(
model_name='inputmaterial',
name='update_by',
),
migrations.RemoveField(
model_name='material',
name='processes',
),
migrations.RemoveField(
model_name='outputmaterial',
name='create_by',
),
migrations.RemoveField(
model_name='outputmaterial',
name='process',
),
migrations.RemoveField(
model_name='outputmaterial',
name='product',
),
migrations.RemoveField(
model_name='outputmaterial',
name='update_by',
),
migrations.RemoveField(
model_name='techdoc',
name='create_by',
),
migrations.RemoveField(
model_name='techdoc',
name='process',
),
migrations.RemoveField(
model_name='techdoc',
name='product',
),
migrations.RemoveField(
model_name='techdoc',
name='update_by',
),
migrations.RemoveField(
model_name='usedstep',
name='create_by',
),
migrations.RemoveField(
model_name='usedstep',
name='process',
),
migrations.RemoveField(
model_name='usedstep',
name='product',
),
migrations.RemoveField(
model_name='usedstep',
name='update_by',
),
migrations.AddField(
model_name='usedstep',
name='remark',
field=models.TextField(blank=True, null=True, verbose_name='生产备注'),
),
migrations.AlterField(
model_name='step',
name='process',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='step_process', to='mtm.process', verbose_name='所属工序'),
),
migrations.AddField(
model_name='subproduction',
name='product',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='产品'),
),
migrations.AddField(
model_name='inputmaterial',
name='subproduction',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='mtm.subproduction', verbose_name='关联生产分解'),
preserve_default=False,
),
migrations.AddField(
model_name='outputmaterial',
name='subproduction',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='mtm.subproduction', verbose_name='关联生产分解'),
preserve_default=False,
),
migrations.AddField(
model_name='techdoc',
name='subproduction',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='mtm.subproduction', verbose_name='关联生产分解'),
preserve_default=False,
),
migrations.AddField(
model_name='usedstep',
name='subproduction',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='mtm.subproduction', verbose_name='关联生产分解'),
preserve_default=False,
),
]

View File

@ -0,0 +1,26 @@
# Generated by Django 3.2.6 on 2021-10-12 08:57
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('mtm', '0019_auto_20211012_0901'),
]
operations = [
migrations.AddField(
model_name='subproduction',
name='create_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='subproduction_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'),
),
migrations.AddField(
model_name='subproduction',
name='update_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='subproduction_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
),
]

View File

@ -0,0 +1,56 @@
# Generated by Django 3.2.6 on 2021-10-13 00:56
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('mtm', '0020_auto_20211012_1657'),
]
operations = [
migrations.AddField(
model_name='inputmaterial',
name='create_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='inputmaterial_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'),
),
migrations.AddField(
model_name='inputmaterial',
name='update_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='inputmaterial_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
),
migrations.AddField(
model_name='outputmaterial',
name='create_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='outputmaterial_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'),
),
migrations.AddField(
model_name='outputmaterial',
name='update_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='outputmaterial_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
),
migrations.AddField(
model_name='techdoc',
name='create_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='techdoc_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'),
),
migrations.AddField(
model_name='techdoc',
name='update_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='techdoc_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
),
migrations.AddField(
model_name='usedstep',
name='create_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='usedstep_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'),
),
migrations.AddField(
model_name='usedstep',
name='update_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='usedstep_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
),
]

View File

@ -16,9 +16,9 @@ class Material(CommonAModel):
(1, '成品'), (1, '成品'),
(2, '半成品'), (2, '半成品'),
(3, '主要原料'), (3, '主要原料'),
(4,'辅助原料') , (4, '辅助原料') ,
(5, '加工工具'), (5, '加工工具'),
(6,'辅助工具') (6, '辅助工具')
) )
unit_choices =( unit_choices =(
('', ''), ('', ''),
@ -29,7 +29,6 @@ class Material(CommonAModel):
specification = models.CharField('型号', max_length=100, null=True, blank=True) specification = models.CharField('型号', max_length=100, null=True, blank=True)
type = models.CharField('物料类型', choices= type_choices, max_length=20, default=1) type = models.CharField('物料类型', choices= type_choices, max_length=20, default=1)
sort_str = models.CharField('排序字符', max_length=100, null=True, blank=True) sort_str = models.CharField('排序字符', max_length=100, null=True, blank=True)
processes = models.JSONField('工艺流程', default=list, blank=True, null=True)
unit = models.CharField('基准计量单位', choices=unit_choices, default='', max_length=10) unit = models.CharField('基准计量单位', choices=unit_choices, default='', max_length=10)
count = models.IntegerField('物料总数', default=0) count = models.IntegerField('物料总数', default=0)
class Meta: class Meta:
@ -60,7 +59,7 @@ class Step(CommonAModel):
""" """
工序步骤 工序步骤
""" """
process = models.ForeignKey(Process, on_delete=models.CASCADE, verbose_name='所属工序') process = models.ForeignKey(Process, on_delete=models.CASCADE, verbose_name='所属工序', related_name='step_process')
name = models.CharField('工序步骤名称', max_length=100) name = models.CharField('工序步骤名称', max_length=100)
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)
@ -127,26 +126,28 @@ class RecordFormField(CommonAModel):
def __str__(self): def __str__(self):
return self.field_key + '-' + self.field_name return self.field_key + '-' + self.field_name
class ProductProcess(CommonAModel):
class SubProduction(CommonAModel):
""" """
产品生产工艺集 产品生产分解
""" """
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)
sort = models.IntegerField('排序号', default=1) sort = models.IntegerField('排序号', default=1)
class Meta: class Meta:
verbose_name = '产品生产工序' verbose_name = '产品生产工序'
verbose_name_plural = verbose_name verbose_name_plural = verbose_name
class InputMaterial(CommonAModel): class InputMaterial(CommonAModel):
""" """
输入物料 输入物料
""" """
material = models.ForeignKey(Material, verbose_name='输入物料', on_delete=models.CASCADE, related_name='inputmaterial') material = models.ForeignKey(Material, verbose_name='输入物料', on_delete=models.CASCADE, related_name='inputmaterial')
count = models.FloatField('消耗量', default=1) count = models.FloatField('消耗量', default=1)
product = models.ForeignKey(Material, verbose_name='关联产品', on_delete=models.CASCADE, related_name='inputmaterial_product') subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE)
process = models.ForeignKey(Process, verbose_name='关联工序', on_delete=models.CASCADE)
sort = models.IntegerField('排序号', default=1) sort = models.IntegerField('排序号', default=1)
class Meta: class Meta:
@ -161,8 +162,7 @@ class OutputMaterial(CommonAModel):
""" """
material = models.ForeignKey(Material, verbose_name='输出物料', on_delete=models.CASCADE, related_name='outputmaterial') material = models.ForeignKey(Material, verbose_name='输出物料', on_delete=models.CASCADE, related_name='outputmaterial')
count = models.FloatField('产出量', default=1) count = models.FloatField('产出量', default=1)
product = models.ForeignKey(Material, verbose_name='关联产品', on_delete=models.CASCADE, related_name='outputmaterial_product') subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE)
process = models.ForeignKey(Process, verbose_name='关联工序', on_delete=models.CASCADE)
sort = models.IntegerField('排序号', default=1) sort = models.IntegerField('排序号', default=1)
class Meta: class Meta:
@ -171,11 +171,11 @@ class OutputMaterial(CommonAModel):
class UsedStep(CommonAModel): 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='usedsteps')
product = models.ForeignKey(Material, verbose_name='关联产品', on_delete=models.CASCADE) remark = models.TextField('生产备注', null=True, blank=True)
process = models.ForeignKey(Process, verbose_name='关联工序', on_delete=models.CASCADE) subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE)
class Meta: class Meta:
verbose_name = '产品生产子工序' verbose_name = '产品生产子工序'
@ -188,8 +188,7 @@ class TechDoc(CommonAModel):
""" """
name = models.CharField('名称', max_length=50) name = models.CharField('名称', max_length=50)
file = models.ForeignKey(File, verbose_name='技术文件', on_delete=models.CASCADE) file = models.ForeignKey(File, verbose_name='技术文件', on_delete=models.CASCADE)
product = models.ForeignKey(Material, verbose_name='关联产品', on_delete=models.CASCADE) subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE)
process = models.ForeignKey(Process, verbose_name='关联工序', on_delete=models.CASCADE)
content = models.TextField('内容', null=True, blank=True) content = models.TextField('内容', null=True, blank=True)
class Meta: class Meta:

View File

@ -1,12 +1,11 @@
from apps.em.serializers import EquipmentSimpleSerializer from apps.em.serializers import EquipmentSimpleSerializer
from rest_framework import serializers from rest_framework import serializers
from rest_framework.exceptions import ParseError, ValidationError from rest_framework.exceptions import ParseError, ValidationError
from .models import InputMaterial, Material, OutputMaterial, Process, ProductProcess, RecordForm, RecordFormField, Step, TechDoc, UsedStep from .models import InputMaterial, Material, OutputMaterial, Process, RecordForm, RecordFormField, Step, TechDoc, UsedStep, SubProduction
from apps.system.serializers import FileSimpleSerializer, OrganizationSimpleSerializer from apps.system.serializers import FileSimpleSerializer, OrganizationSimpleSerializer
class MaterialSerializer(serializers.ModelSerializer): class MaterialSerializer(serializers.ModelSerializer):
processes = serializers.ListField(child=serializers.IntegerField(min_value=1))
class Meta: class Meta:
model = Material model = Material
fields = '__all__' fields = '__all__'
@ -18,7 +17,8 @@ class MaterialDetailSerializer(serializers.ModelSerializer):
fields = '__all__' fields = '__all__'
def get_processes_(self, obj): def get_processes_(self, obj):
objs = Process.objects.filter(id__in=obj.processes).order_by('number') 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')
return ProcessSimpleSerializer(instance=objs, many=True).data return ProcessSimpleSerializer(instance=objs, many=True).data
@ -60,18 +60,11 @@ class StepDetailSerializer(serializers.ModelSerializer):
queryset = queryset.prefetch_related('equipments') queryset = queryset.prefetch_related('equipments')
return queryset return queryset
class ProductProcessListSerializer(serializers.ModelSerializer): class SubProductionSerializer(serializers.ModelSerializer):
process_ = ProcessSimpleSerializer(source='process', read_only=True)
product_ = MaterialSimpleSerializer(source='product', read_only=True)
class Meta: class Meta:
model = ProductProcess model = SubProduction
fields = '__all__' fields = '__all__'
class ProductProcessUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = ProductProcess
fields = ['sort']
class InputMaterialListSerializer(serializers.ModelSerializer): class InputMaterialListSerializer(serializers.ModelSerializer):
material_ = MaterialSimpleSerializer(source='material', read_only=True) material_ = MaterialSimpleSerializer(source='material', read_only=True)
class Meta: class Meta:
@ -88,10 +81,10 @@ class OutputMaterialListSerializer(serializers.ModelSerializer):
class InputMaterialSerializer(serializers.ModelSerializer): class InputMaterialSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = InputMaterial model = InputMaterial
fields = ['count', 'sort', 'material', 'product', 'process'] fields = ['count', 'sort', 'material', 'subproduction']
def create(self, validated_data): def create(self, validated_data):
if InputMaterial.objects.filter(material=validated_data['material'], product=validated_data['product'], process=validated_data['process'], is_deleted=False).exists(): if InputMaterial.objects.filter(material=validated_data['material'], subproduction=validated_data['subproduction'], is_deleted=False).exists():
raise ValidationError('该物料已存在') raise ValidationError('该物料已存在')
return super().create(validated_data) return super().create(validated_data)
@ -103,10 +96,10 @@ class InputMaterialUpdateSerializer(serializers.ModelSerializer):
class OutputMaterialSerializer(serializers.ModelSerializer): class OutputMaterialSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = OutputMaterial model = OutputMaterial
fields = ['count', 'sort', 'material', 'product', 'process'] fields = ['count', 'sort', 'material', 'subproduction']
def create(self, validated_data): def create(self, validated_data):
if OutputMaterial.objects.filter(material=validated_data['material'], product=validated_data['product'], process=validated_data['process'], is_deleted=False).exists(): if OutputMaterial.objects.filter(material=validated_data['material'], subproduction=validated_data['subproduction'], is_deleted=False).exists():
raise ValidationError('该物料已存在') raise ValidationError('该物料已存在')
return super().create(validated_data) return super().create(validated_data)
@ -121,7 +114,15 @@ class UsedStepCreateSerializer(serializers.ModelSerializer):
""" """
class Meta: class Meta:
model = UsedStep model = UsedStep
fields = ['step', 'product', 'process'] fields = ['step', 'subproduction', 'remark']
class UsedStepUpdateSerializer(serializers.ModelSerializer):
"""
产品生产子工序编辑
"""
class Meta:
model = UsedStep
fields = ['remark']
class UsedStepListSerializer(serializers.ModelSerializer): class UsedStepListSerializer(serializers.ModelSerializer):
""" """
@ -198,7 +199,7 @@ class TechDocListSerializer(serializers.ModelSerializer):
class TechDocCreateSerializer(serializers.ModelSerializer): class TechDocCreateSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = TechDoc model = TechDoc
fields = ['file', 'product', 'process', 'name', 'content'] fields = ['file', 'subproduction', 'name', 'content']
class TechDocUpdateSerializer(serializers.ModelSerializer): class TechDocUpdateSerializer(serializers.ModelSerializer):
class Meta: class Meta:

View File

@ -1,14 +1,14 @@
from django.db.models import base from django.db.models import base
from rest_framework import urlpatterns from rest_framework import urlpatterns
from apps.mtm.views import InputMaterialViewSet, MaterialViewSet, OutputMaterialViewSet, ProcessViewSet, RecordFormFieldViewSet, RecordFormViewSet, StepViewSet, TechDocViewSet, UsedStepViewSet from apps.mtm.views import InputMaterialViewSet, MaterialViewSet, OutputMaterialViewSet, ProcessViewSet, RecordFormFieldViewSet, RecordFormViewSet, StepViewSet, SubProductionViewSet, TechDocViewSet, UsedStepViewSet
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('material', MaterialViewSet, basename='material') router.register('material', MaterialViewSet, basename='material')
router.register('process', ProcessViewSet, basename='process') router.register('process', ProcessViewSet, basename='process')
# router.register('productprocess', ProductProcessViewSet, basename='productprocess')
router.register('step', StepViewSet, basename='step') router.register('step', StepViewSet, basename='step')
router.register('subproducation', SubProductionViewSet, basename='subproducation')
router.register('inputmaterial', InputMaterialViewSet, basename='inputmaterial') router.register('inputmaterial', InputMaterialViewSet, basename='inputmaterial')
router.register('outputmaterial', OutputMaterialViewSet, basename='outputmaterial') router.register('outputmaterial', OutputMaterialViewSet, basename='outputmaterial')
router.register('usedstep', UsedStepViewSet, basename='usedstep') router.register('usedstep', UsedStepViewSet, basename='usedstep')

View File

@ -2,8 +2,8 @@ from django.shortcuts import render
from rest_framework.viewsets import ModelViewSet, GenericViewSet from rest_framework.viewsets import ModelViewSet, GenericViewSet
from rest_framework.mixins import CreateModelMixin, ListModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin from rest_framework.mixins import CreateModelMixin, ListModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin
from apps.mtm.models import InputMaterial, Material, OutputMaterial, Process, ProductProcess, RecordForm, RecordFormField, Step, TechDoc, UsedStep from apps.mtm.models import InputMaterial, Material, OutputMaterial, Process, RecordForm, RecordFormField, Step, TechDoc, UsedStep, SubProduction
from apps.mtm.serializers import InputMaterialListSerializer, InputMaterialSerializer, InputMaterialUpdateSerializer, MaterialDetailSerializer, MaterialSerializer, MaterialSimpleSerializer, OutputMaterialListSerializer, OutputMaterialSerializer, OutputMaterialUpdateSerializer, ProductProcessListSerializer, ProductProcessUpdateSerializer, ProcessSerializer, RecordFormCreateSerializer, RecordFormFieldCreateSerializer, RecordFormFieldSerializer, RecordFormFieldUpdateSerializer, RecordFormSerializer, RecordFormUpdateSerializer, StepDetailSerializer, StepSerializer, TechDocCreateSerializer, TechDocListSerializer, TechDocUpdateSerializer, UsedStepCreateSerializer, UsedStepListSerializer from apps.mtm.serializers import InputMaterialListSerializer, InputMaterialSerializer, InputMaterialUpdateSerializer, MaterialDetailSerializer, MaterialSerializer, MaterialSimpleSerializer, OutputMaterialListSerializer, OutputMaterialSerializer, OutputMaterialUpdateSerializer, ProcessSerializer, RecordFormCreateSerializer, RecordFormFieldCreateSerializer, RecordFormFieldSerializer, RecordFormFieldUpdateSerializer, RecordFormSerializer, RecordFormUpdateSerializer, StepDetailSerializer, StepSerializer, SubProductionSerializer, TechDocCreateSerializer, TechDocListSerializer, TechDocUpdateSerializer, UsedStepCreateSerializer, UsedStepListSerializer, UsedStepUpdateSerializer
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 rest_framework.response import Response from rest_framework.response import Response
@ -30,15 +30,6 @@ class MaterialViewSet(PageOrNot, CreateUpdateModelAMixin, ModelViewSet):
return MaterialDetailSerializer return MaterialDetailSerializer
return MaterialSerializer return MaterialSerializer
# @action(methods=['get'], detail=True, perms_map={'get':'*'}, pagination_class=None, serializer_class=MaterialSimpleSerializer)
# def processes(self, request, pk=None):
# """
# 产品生产工艺流程
# """
# material = self.get_object()
# serializer = self.serializer_class(instance=Process.objects.filter(id__in=material.processes), many=True)
# return Response(serializer.data)
class ProcessViewSet(PageOrNot, CreateUpdateModelAMixin, ModelViewSet): class ProcessViewSet(PageOrNot, CreateUpdateModelAMixin, ModelViewSet):
""" """
@ -62,11 +53,11 @@ class ProcessViewSet(PageOrNot, CreateUpdateModelAMixin, ModelViewSet):
serializer = self.serializer_class(instance=Step.objects.prefetch_related('equipments').filter(process=process, is_deleted=False), many=True) serializer = self.serializer_class(instance=Step.objects.prefetch_related('equipments').filter(process=process, is_deleted=False), many=True)
return Response(serializer.data) return Response(serializer.data)
class StepViewSet(OptimizationMixin, CreateUpdateModelAMixin, CreateModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin, GenericViewSet): class StepViewSet(OptimizationMixin, CreateUpdateModelAMixin, ModelViewSet):
""" """
子工序-增删改查 子工序-增删改查
""" """
perms_map = {'*':'process_update'} perms_map = {'*':'*'}
queryset = Step.objects.all() queryset = Step.objects.all()
serializer_class = StepSerializer serializer_class = StepSerializer
search_fields = ['name', 'number'] search_fields = ['name', 'number']
@ -78,20 +69,16 @@ class StepViewSet(OptimizationMixin, CreateUpdateModelAMixin, CreateModelMixin,
return StepDetailSerializer return StepDetailSerializer
return StepSerializer return StepSerializer
# class ProductProcessViewSet(PageOrNot, CreateModelMixin, UpdateModelMixin, ListModelMixin, DestroyModelMixin, GenericViewSet): class SubProductionViewSet(CreateUpdateModelAMixin, ModelViewSet):
# """ """
# 产品生产工艺流程增删改查 产品生产分解增删改查
# """ """
# perms_map={'*':'*'} perms_map={'*':'*'}
# queryset = ProductProcess.objects.select_related('process', 'product').all() queryset = SubProduction.objects.all()
# filterset_fields = ['process', 'product'] filterset_fields = ['product']
# serializer_class = ProductProcessListSerializer search_fields = ['name']
# ordering = ['sort'] serializer_class = SubProductionSerializer
ordering = ['sort']
# def get_serializer_class(self):
# if self.action == 'update':
# return ProductProcessUpdateSerializer
# return super().get_serializer_class()
class InputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet): class InputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet):
""" """
@ -100,7 +87,7 @@ class InputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet):
perms_map = {'*':'*'} perms_map = {'*':'*'}
queryset = InputMaterial.objects.select_related('material').all() queryset = InputMaterial.objects.select_related('material').all()
serializer_class = InputMaterialSerializer serializer_class = InputMaterialSerializer
filterset_fields = ['process', 'product'] filterset_fields = ['subproduction']
ordering = ['sort', '-create_time'] ordering = ['sort', '-create_time']
def get_serializer_class(self): def get_serializer_class(self):
@ -117,7 +104,7 @@ class OutputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet):
perms_map = {'*':'*'} perms_map = {'*':'*'}
queryset = OutputMaterial.objects.select_related('material').all() queryset = OutputMaterial.objects.select_related('material').all()
serializer_class = OutputMaterialSerializer serializer_class = OutputMaterialSerializer
filterset_fields = ['process', 'product'] filterset_fields = ['subproduction']
ordering = ['sort', '-create_time'] ordering = ['sort', '-create_time']
def get_serializer_class(self): def get_serializer_class(self):
@ -127,18 +114,20 @@ class OutputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet):
return OutputMaterialUpdateSerializer return OutputMaterialUpdateSerializer
return OutputMaterialSerializer return OutputMaterialSerializer
class UsedStepViewSet(OptimizationMixin, CreateUpdateModelAMixin, CreateModelMixin, DestroyModelMixin, ListModelMixin, GenericViewSet): class UsedStepViewSet(OptimizationMixin, CreateModelMixin, DestroyModelMixin, ListModelMixin, UpdateModelMixin, GenericViewSet):
""" """
产品生产子工序表 产品生产子工序表
""" """
perms_map = {'*':'*'} perms_map = {'*':'*'}
queryset = UsedStep.objects.all() queryset = UsedStep.objects.all()
filterset_fields = ['process', 'product', 'step'] filterset_fields = ['subproduction', 'step']
ordering = ['step__sort', '-step__create_time'] ordering = ['step__sort', '-step__create_time']
def get_serializer_class(self): def get_serializer_class(self):
if self.action =='create': if self.action =='create':
return UsedStepCreateSerializer return UsedStepCreateSerializer
elif self.action == 'update':
return UsedStepUpdateSerializer
return UsedStepListSerializer return UsedStepListSerializer
class RecordFormViewSet(OptimizationMixin, CreateUpdateModelAMixin, ModelViewSet): class RecordFormViewSet(OptimizationMixin, CreateUpdateModelAMixin, ModelViewSet):
@ -189,7 +178,7 @@ class TechDocViewSet(OptimizationMixin, CreateUpdateModelAMixin, ModelViewSet):
""" """
perms_map = {'*':'*'} perms_map = {'*':'*'}
queryset = TechDoc.objects.select_related('file').all() queryset = TechDoc.objects.select_related('file').all()
filterset_fields = ['process', 'product'] filterset_fields = ['subproduction']
search_fields = ['name'] search_fields = ['name']
ordering = ['-id'] ordering = ['-id']

View File

@ -1,3 +1,4 @@
from rest_framework.views import APIView
from apps.system.mixins import CreateUpdateModelAMixin from apps.system.mixins import CreateUpdateModelAMixin
from apps.pm.serializers import ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer from apps.pm.serializers import ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer
from rest_framework.mixins import CreateModelMixin, ListModelMixin from rest_framework.mixins import CreateModelMixin, ListModelMixin
@ -50,3 +51,6 @@ 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()
class ResourceCalculate(APIView):
pass

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-10-12 08:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wf', '0011_auto_20210930_0954'),
]
operations = [
migrations.AddField(
model_name='ticketflow',
name='intervene_type',
field=models.IntegerField(choices=[(0, '正常处理'), (1, '转交'), (2, '加签'), (3, '加签处理完成'), (4, '接单'), (5, '评论'), (6, '删除'), (7, '强制关闭'), (8, '强制修改状态'), (9, 'hook操作'), (10, '撤回')], default=0, help_text='流转类型', verbose_name='干预类型'),
),
]

View File

@ -39,7 +39,7 @@ class State(CommonAModel):
PARTICIPANT_TYPE_ROBOT = 6 PARTICIPANT_TYPE_ROBOT = 6
PARTICIPANT_TYPE_FIELD = 7 PARTICIPANT_TYPE_FIELD = 7
PARTICIPANT_TYPE_PARENT_FIELD = 8 PARTICIPANT_TYPE_PARENT_FIELD = 8
type2_choices = ( state_participanttype_choices = (
(0, '无处理人'), (0, '无处理人'),
(PARTICIPANT_TYPE_PERSONAL, '个人'), (PARTICIPANT_TYPE_PERSONAL, '个人'),
(PARTICIPANT_TYPE_MULTI, '多人'), (PARTICIPANT_TYPE_MULTI, '多人'),
@ -70,7 +70,7 @@ class State(CommonAModel):
sort = models.IntegerField('状态顺序', default=0, help_text='用于工单步骤接口时step上状态的顺序(因为存在网状情况,所以需要人为设定顺序),值越小越靠前') sort = models.IntegerField('状态顺序', default=0, help_text='用于工单步骤接口时step上状态的顺序(因为存在网状情况,所以需要人为设定顺序),值越小越靠前')
type = models.IntegerField('状态类型', default=0, choices=type_choices, help_text='0.普通类型 1.初始状态(用于新建工单时,获取对应的字段必填及transition信息) 2.结束状态(此状态下的工单不得再处理即没有对应的transition)') type = models.IntegerField('状态类型', default=0, choices=type_choices, help_text='0.普通类型 1.初始状态(用于新建工单时,获取对应的字段必填及transition信息) 2.结束状态(此状态下的工单不得再处理即没有对应的transition)')
enable_retreat = models.BooleanField('允许撤回', default=False, help_text='开启后允许工单创建人在此状态直接撤回工单到初始状态') enable_retreat = models.BooleanField('允许撤回', default=False, help_text='开启后允许工单创建人在此状态直接撤回工单到初始状态')
participant_type = models.IntegerField('参与者类型', choices=type2_choices, default=1, blank=True, help_text='0.无处理人,1.个人,2.多人,3.部门,4.角色,5.变量(支持工单创建人,创建人的leader),6.脚本,7.工单的字段内容(如表单中的"测试负责人",需要为用户名或者逗号隔开的多个用户名),8.父工单的字段内容。 初始状态请选择类型5参与人填create_by') participant_type = models.IntegerField('参与者类型', choices=state_participanttype_choices, default=1, blank=True, help_text='0.无处理人,1.个人,2.多人,3.部门,4.角色,5.变量(支持工单创建人,创建人的leader),6.脚本,7.工单的字段内容(如表单中的"测试负责人",需要为用户名或者逗号隔开的多个用户名),8.父工单的字段内容。 初始状态请选择类型5参与人填create_by')
participant = models.JSONField('参与者', default=list, blank=True, help_text='可以为空(无处理人的情况,如结束状态)、userid、userid列表\部门id\角色id\变量(create_by,create_by_tl)\脚本记录的id等包含子工作流的需要设置处理人为loonrobot') participant = models.JSONField('参与者', default=list, blank=True, help_text='可以为空(无处理人的情况,如结束状态)、userid、userid列表\部门id\角色id\变量(create_by,create_by_tl)\脚本记录的id等包含子工作流的需要设置处理人为loonrobot')
state_fields = models.JSONField('表单字段', default=dict, help_text='json格式字典存储,包括读写属性1只读2必填3可选. 示例:{"create_time":1,"title":2, "sn":1}, 内置特殊字段participant_info.participant_name:当前处理人信息(部门名称、角色名称)state.state_name:当前状态的状态名,workflow.workflow_name:工作流名称') # json格式存储,包括读写属性1只读2必填3可选4不显示, 字典的字典 state_fields = models.JSONField('表单字段', default=dict, help_text='json格式字典存储,包括读写属性1只读2必填3可选. 示例:{"create_time":1,"title":2, "sn":1}, 内置特殊字段participant_info.participant_name:当前处理人信息(部门名称、角色名称)state.state_name:当前状态的状态名,workflow.workflow_name:工作流名称') # json格式存储,包括读写属性1只读2必填3可选4不显示, 字典的字典
distribute_type = models.IntegerField('分配方式', default=1, choices=state_distribute_choices, help_text='1.主动接单(如果当前处理人实际为多人的时候,需要先接单才能处理) 2.直接处理(即使当前处理人实际为多人,也可以直接处理) 3.随机分配(如果实际为多人,则系统会随机分配给其中一个人) 4.全部处理(要求所有参与人都要处理一遍,才能进入下一步)') distribute_type = models.IntegerField('分配方式', default=1, choices=state_distribute_choices, help_text='1.主动接单(如果当前处理人实际为多人的时候,需要先接单才能处理) 2.直接处理(即使当前处理人实际为多人,也可以直接处理) 3.随机分配(如果实际为多人,则系统会随机分配给其中一个人) 4.全部处理(要求所有参与人都要处理一遍,才能进入下一步)')
@ -87,6 +87,31 @@ class Transition(CommonAModel):
(2, '拒绝'), (2, '拒绝'),
(3, '其他') (3, '其他')
) )
TRANSITION_INTERVENE_TYPE_DELIVER = 1 # 转交操作
TRANSITION_INTERVENE_TYPE_ADD_NODE = 2 # 加签操作
TRANSITION_INTERVENE_TYPE_ADD_NODE_END = 3 # 加签处理完成
TRANSITION_INTERVENE_TYPE_ACCEPT = 4 # 接单操作
TRANSITION_INTERVENE_TYPE_COMMENT = 5 # 评论操作
TRANSITION_INTERVENE_TYPE_DELETE = 6 # 删除操作
TRANSITION_INTERVENE_TYPE_CLOSE = 7 # 强制关闭操作
TRANSITION_INTERVENE_TYPE_ALTER_STATE = 8 # 强制修改状态操作
TRANSITION_INTERVENE_TYPE_HOOK = 9 # hook操作
TRANSITION_INTERVENE_TYPE_RETREAT = 10 # 撤回
intervene_type_choices = (
(0, '正常处理'),
(TRANSITION_INTERVENE_TYPE_DELIVER, '转交'),
(TRANSITION_INTERVENE_TYPE_ADD_NODE, '加签'),
(TRANSITION_INTERVENE_TYPE_ADD_NODE_END, '加签处理完成'),
(TRANSITION_INTERVENE_TYPE_ACCEPT, '接单'),
(TRANSITION_INTERVENE_TYPE_COMMENT, '评论'),
(TRANSITION_INTERVENE_TYPE_DELETE, '删除'),
(TRANSITION_INTERVENE_TYPE_CLOSE, '强制关闭'),
(TRANSITION_INTERVENE_TYPE_ALTER_STATE, '强制修改状态'),
(TRANSITION_INTERVENE_TYPE_HOOK, 'hook操作'),
(TRANSITION_INTERVENE_TYPE_RETREAT, '撤回')
)
name = models.CharField('操作', max_length=50) name = models.CharField('操作', max_length=50)
workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='所属工作流') workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='所属工作流')
timer = models.IntegerField('定时器(单位秒)', default=0, help_text='单位秒。处于源状态X秒后如果状态都没有过变化则自动流转到目标状态。设置时间有效') timer = models.IntegerField('定时器(单位秒)', default=0, help_text='单位秒。处于源状态X秒后如果状态都没有过变化则自动流转到目标状态。设置时间有效')
@ -166,7 +191,7 @@ class Ticket(CommonAModel):
in_add_node = models.BooleanField('加签状态中', default=False, help_text='是否处于加签状态下') in_add_node = models.BooleanField('加签状态中', default=False, help_text='是否处于加签状态下')
add_node_man = models.ForeignKey(User, verbose_name='加签人', on_delete=models.SET_NULL, null=True, blank=True, help_text='加签操作的人,工单当前处理人处理完成后会回到该处理人,当处于加签状态下才有效') add_node_man = models.ForeignKey(User, verbose_name='加签人', on_delete=models.SET_NULL, null=True, blank=True, help_text='加签操作的人,工单当前处理人处理完成后会回到该处理人,当处于加签状态下才有效')
participant_type = models.IntegerField('当前处理人类型', default=0, help_text='0.无处理人,1.个人,2.多人', choices=State.type2_choices) participant_type = models.IntegerField('当前处理人类型', default=0, help_text='0.无处理人,1.个人,2.多人', choices=State.state_participanttype_choices)
participant = models.JSONField('当前处理人', default=list, blank=True, help_text='可以为空(无处理人的情况,如结束状态)、userid、userid列表') participant = models.JSONField('当前处理人', default=list, blank=True, help_text='可以为空(无处理人的情况,如结束状态)、userid、userid列表')
act_state = models.IntegerField('进行状态', default=1, help_text='当前工单的进行状态', choices=act_state_choices) act_state = models.IntegerField('进行状态', default=1, help_text='当前工单的进行状态', choices=act_state_choices)
multi_all_person = models.JSONField('全部处理的结果', default=dict, blank=True, help_text='需要当前状态处理人全部处理时实际的处理结果json格式') multi_all_person = models.JSONField('全部处理的结果', default=dict, blank=True, help_text='需要当前状态处理人全部处理时实际的处理结果json格式')
@ -178,7 +203,8 @@ 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关联 为0时表示认为干预的操作', 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.type2_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')
state = models.ForeignKey(State, verbose_name='当前状态', default=0, blank=True, on_delete=models.CASCADE) state = models.ForeignKey(State, verbose_name='当前状态', default=0, blank=True, on_delete=models.CASCADE)
ticket_data = models.JSONField('工单数据', default=dict, blank=True, help_text='可以用于记录当前表单数据json格式') ticket_data = models.JSONField('工单数据', default=dict, blank=True, help_text='可以用于记录当前表单数据json格式')
intervene_type = models.IntegerField('干预类型', default=0, help_text='流转类型', choices=Transition.intervene_type_choices)

View File

@ -23,7 +23,7 @@ class WorkflowSimpleSerializer(serializers.ModelSerializer):
class StateSimpleSerializer(serializers.ModelSerializer): class StateSimpleSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = State model = State
fields = ['id', 'name', 'type', 'distribute_type'] fields = ['id', 'name', 'type', 'distribute_type', 'enable_retreat']
class TransitionSerializer(serializers.ModelSerializer): class TransitionSerializer(serializers.ModelSerializer):
source_state_ = StateSimpleSerializer(source='source_state', read_only=True) source_state_ = StateSimpleSerializer(source='source_state', read_only=True)
@ -77,7 +77,7 @@ class TicketListSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Ticket model = Ticket
fields = ['id', 'title', 'sn', 'workflow', 'workflow_', 'state', 'state_', 'act_state', 'create_time', 'update_time', 'participant_type'] fields = ['id', 'title', 'sn', 'workflow', 'workflow_', 'state', 'state_', 'act_state', 'create_time', 'update_time', 'participant_type', 'create_by']
@staticmethod @staticmethod
def setup_eager_loading(queryset): def setup_eager_loading(queryset):
@ -127,3 +127,6 @@ class TicketHandleSerializer(serializers.Serializer):
transition = serializers.IntegerField(label="流转id") transition = serializers.IntegerField(label="流转id")
ticket_data = serializers.JSONField(label="表单数据json") ticket_data = serializers.JSONField(label="表单数据json")
suggestion = serializers.CharField(label="处理意见", required = False) suggestion = serializers.CharField(label="处理意见", required = False)
class TicketRetreatSerializer(serializers.Serializer):
suggestion = serializers.CharField(label="撤回原因", required = False)

View File

@ -3,7 +3,7 @@ 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, TicketSerializer, TransitionSerializer, WorkflowSerializer, TicketListSerializer, TicketDetailSerializer from apps.wf.serializers import CustomFieldSerializer, StateSerializer, TicketCreateSerializer, 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
@ -102,6 +102,8 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
return TicketCreateSerializer return TicketCreateSerializer
elif self.action == 'handle': elif self.action == 'handle':
return TicketHandleSerializer return TicketHandleSerializer
elif self.action == 'retreat':
return TicketRetreatSerializer
elif self.action == 'list': elif self.action == 'list':
return TicketListSerializer return TicketListSerializer
elif self.action == 'retrieve': elif self.action == 'retrieve':
@ -281,7 +283,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
# 接单日志 # 接单日志
# 更新工单流转记录 # 更新工单流转记录
TicketFlow.objects.create(ticket=ticket, state=ticket.state, ticket_data=WfService.get_ticket_all_field_value(ticket), TicketFlow.objects.create(ticket=ticket, state=ticket.state, ticket_data=WfService.get_ticket_all_field_value(ticket),
suggestion='接单处理', participant_type=State.PARTICIPANT_TYPE_PERSONAL, suggestion='', participant_type=State.PARTICIPANT_TYPE_PERSONAL, intervene_type=Transition.TRANSITION_ATTRIBUTE_TYPE_ACCEPT,
participant=request.user, transition=None) participant=request.user, transition=None)
return Response() return Response()
else: else:
@ -292,7 +294,35 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
""" """
撤回工单允许创建人在指定状态撤回工单至初始状态状态设置中开启允许撤回 撤回工单允许创建人在指定状态撤回工单至初始状态状态设置中开启允许撤回
""" """
pass ticket = self.get_object()
if ticket.create_by != request.user:
raise APIException('非创建人不可撤回')
if not ticket.state.enable_retreat:
raise APIException('该状态不可撤回')
start_state = WfService.get_workflow_start_state(ticket.workflow)
ticket.state = start_state
ticket.participant_type = State.PARTICIPANT_TYPE_PERSONAL
ticket.participant = request.user.id
ticket.act_state = Ticket.TICKET_ACT_STATE_RETREAT
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_RETREAT,
participant=request.user, transition=None)
return Response()
@action(methods=['post'], detail=True, perms_map={'post':'*'})
def add_node(self, request, pk=None):
"""
加签
"""
def close(self, request, pk=None):
"""
关闭工单(超级管理员或者创建人在初始状态)
"""
class TicketFlowViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet): class TicketFlowViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
""" """