ticketDelete

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,45 @@
# Generated by Django 3.2.6 on 2021-10-14 07:03
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('system', '0003_auto_20210812_0909'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('mtm', '0022_auto_20211014_0944'),
('pm', '0002_alter_productionplan_count'),
]
operations = [
migrations.AddField(
model_name='productionplan',
name='is_planed',
field=models.BooleanField(default=False, verbose_name='是否已排产'),
),
migrations.CreateModel(
name='SubProductionPlan',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('start_date', models.DateField(verbose_name='计划开工日期')),
('end_date', models.DateField(verbose_name='计划完工日期')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='subproductionplan_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('process', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.process', verbose_name='关联大工序')),
('production_plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pm.productionplan', verbose_name='关联主生产计划')),
('subproduction', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.subproduction', verbose_name='关联生产分解')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='subproductionplan_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
('workshop', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='system.organization', verbose_name='生产车间')),
],
options={
'verbose_name': '子生产计划',
'verbose_name_plural': '子生产计划',
},
),
]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -201,7 +201,7 @@ class TicketFlow(BaseModel):
工单流转日志
"""
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, verbose_name='关联工单', related_name='ticketflow_ticket')
transition = models.ForeignKey(Transition, verbose_name='流转id', help_text='与worklow.Transition关联0时表示认为干预的操作', on_delete=models.CASCADE, null=True, blank=True)
transition = models.ForeignKey(Transition, verbose_name='流转id', help_text='与worklow.Transition关联时表示认为干预的操作', on_delete=models.CASCADE, null=True, blank=True)
suggestion = models.CharField('处理意见', max_length=10000, default='', blank=True)
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')

View File

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

View File

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

View File

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

View File

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