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

This commit is contained in:
shijing 2022-02-21 09:29:12 +08:00
commit 87e94c7b9f
16 changed files with 707 additions and 248 deletions

View File

@ -13,6 +13,15 @@ export function clockRecord(data) {
data data
}) })
} }
//员工离岗说明
export function notWork(id, data) {
return request({
url: `/hrm/employee/${id}/not_work_remark/`,
method: 'POST',
data
})
}
export function getEmployee(data) { export function getEmployee(data) {
return request({ return request({
url: '/hrm/employee/', url: '/hrm/employee/',

View File

@ -105,3 +105,19 @@ export function createConvert(data) {
data data
}) })
} }
//任务终止
export function planstop(id) {
return request({
url: `/pm/production_plan/${id}/stop/`,
method: 'put',
})
}
//任务启动,暂停
export function plantoggle(id) {
return request({
url: `/pm/production_plan/${id}/toggle/`,
method: 'put',
})
}

View File

@ -28,11 +28,29 @@
<el-tag type="danger" v-else>离岗</el-tag> <el-tag type="danger" v-else>离岗</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="离岗备注">
<template slot-scope="scope">{{ scope.row.not_work_remark }}</template>
</el-table-column>
<el-table-column align="header-center" label="部门"> <el-table-column align="header-center" label="部门">
<template v-if="scope.row.dept_" slot-scope="scope">{{ <template v-if="scope.row.dept_" slot-scope="scope">{{
scope.row.dept_.name scope.row.dept_.name
}}</template> }}</template>
</el-table-column>
<el-table-column
align="center"
label="操作"
fixed="right"
>
<template slot-scope="scope">
<el-link
type="primary"
v-if="scope.row.is_atwork==false"
@click="handlestopwork(scope)"
>离岗说明
</el-link>
</template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -42,7 +60,33 @@
:page.sync="listQuery.page" :page.sync="listQuery.page"
:limit.sync="listQuery.page_size" :limit.sync="listQuery.page_size"
@pagination="getList" @pagination="getList"
/></el-tab-pane> />
<el-dialog
:visible.sync="dialogVisible"
:close-on-click-modal="false"
title="离岗备注"
>
<el-form
ref="Form"
:model="stopwork"
label-width="100px"
label-position="right"
>
<el-form-item label="备注" prop="remark">
<el-input type="textarea" v-model="stopwork.not_work_remark" placeholder="备注" />
</el-form-item>
</el-form>
<div style="text-align: right">
<el-button type="danger" @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="smtconfirm()">确认</el-button>
</div>
</el-dialog>
</el-tab-pane>
<el-tab-pane label="到岗统计"> <el-tab-pane label="到岗统计">
<div class="container"> <div class="container">
@ -55,7 +99,8 @@
</el-date-picker> </el-date-picker>
<el-button type="primary" @click="submit">查询</el-button> <el-button type="primary" @click="submit">查询</el-button>
</div> </div>
<el-row>
<el-col :span="12">
<el-table <el-table
:data="atworkList" :data="atworkList"
@ -87,7 +132,20 @@
</el-table> </el-table>
</el-col>
<el-col :span="12">
<el-calendar >
<template
slot="dateCell"
slot-scope="{date, data}">
<p>
{{ data.day.split('-').slice(1).join('-') }}<br /> {{dealMyDate(data.day)}}
</p>
</template>
</el-calendar>
</el-col>
</el-row>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
@ -123,6 +181,7 @@
<script> <script>
import { getEmployeeList } from "@/api/employee"; import { getEmployeeList } from "@/api/employee";
import checkPermission from "@/utils/permission"; import checkPermission from "@/utils/permission";
import { notWork } from "@/api/hrm";
import {getatwork } from "@/api/srm"; import {getatwork } from "@/api/srm";
import { upUrl, upHeaders } from "@/api/file"; import { upUrl, upHeaders } from "@/api/file";
@ -136,14 +195,21 @@ export default {
return { return {
userList: { count: 0 }, userList: { count: 0 },
atworkList: [], atworkList: [],
stopwork:{not_work_remark:""},
value3:null, value3:null,
value2:null, value2:null,
listLoading: true, listLoading: true,
dialogVisible:false,
stopworkID:null,
listQuery: { listQuery: {
page: 1, page: 1,
page_size: 20, page_size: 20,
}, },
resDate: [
{"date":"2022-02-20","content":"放假"},
{"date":"2022-02-26","content":"去交电费"},
{"date":"2022-02-25","content":"去学习vue"}
],
atworkDate:{year:null,month:null}, atworkDate:{year:null,month:null},
}; };
}, },
@ -157,7 +223,7 @@ value2:null,
checkPermission, checkPermission,
//今日到岗 //今日到岗
getList() { getList() {
this.listQuery.fields='number,name,is_atwork,dept_'; this.listQuery.fields='number,name,is_atwork,dept_,id,not_work_remark';
getEmployeeList(this.listQuery).then((response) => { getEmployeeList(this.listQuery).then((response) => {
if (response.data) { if (response.data) {
this.userList = response.data; this.userList = response.data;
@ -165,6 +231,24 @@ value2:null,
}); });
}, },
handlestopwork(scope)
{
this.dialogVisible=true;
this.stopworkID=scope.row.id;
},
//离职备注提交
smtconfirm()
{
console.log(this.stopwork);
notWork(this.stopworkID, this.stopwork).then((res) => {
if (res.code >= 200) {
this.$message.success("离职备注提交成功!");
this.dialogVisible=false;
this.getList();
}
})
},
//到岗统计 //到岗统计
submit() submit()
@ -180,6 +264,19 @@ value2:null,
}); });
},
//考勤日历
dealMyDate(v) {
console.log(v)
let len = this.resDate.length
let res = ""
for(let i=0; i<len; i++){
if(this.resDate[i].date == v) {
res = this.resDate[i].content
break
}
}
return res
} }
}, },
}; };

View File

@ -132,12 +132,14 @@ export default {
page_size: 20, page_size: 20,
}, },
state_:{ state_:{
10:'制定中', 10: "制定中",
20:'已下达', 20: "已下达",
30:'已接受', 30: "已接受",
40:'生产中', 40: "生产中",
50:'已完成', 50: "已完成",
60:'军检完成'}, 60: "军检完成",
70: "暂停",
80: "终止"},
listLoading: true, listLoading: true,
proList: [], proList: [],

View File

@ -33,7 +33,7 @@
style="width: 100%" style="width: 100%"
height="300" height="300"
> >
<el-table-column type="index" width="50"/> <el-table-column type="index" width="50" />
<el-table-column label="任务编号" width="110"> <el-table-column label="任务编号" width="110">
<template slot-scope="scope">{{ scope.row.number }}</template> <template slot-scope="scope">{{ scope.row.number }}</template>
</el-table-column> </el-table-column>
@ -41,13 +41,17 @@
<template slot-scope="scope">{{ scope.row.order_.number }}</template> <template slot-scope="scope">{{ scope.row.order_.number }}</template>
</el-table-column> </el-table-column>
<el-table-column label="合同编号" width="110"> <el-table-column label="合同编号" width="110">
<template slot-scope="scope" v-if="scope.row.contract">{{ scope.row.order_.contract_.number }}</template> <template slot-scope="scope" v-if="scope.row.contract">{{
scope.row.order_.contract_.number
}}</template>
</el-table-column> </el-table-column>
<el-table-column label="产品名称" width="150" show-overflow-tooltip> <el-table-column label="产品名称" width="150" show-overflow-tooltip>
<template slot-scope="scope">{{ scope.row.product_.name }}</template> <template slot-scope="scope">{{ scope.row.product_.name }}</template>
</el-table-column> </el-table-column>
<el-table-column label="产品型号" width="110"> <el-table-column label="产品型号" width="110">
<template slot-scope="scope">{{ scope.row.product_.specification }}</template> <template slot-scope="scope">{{
scope.row.product_.specification
}}</template>
</el-table-column> </el-table-column>
<el-table-column label="产品单位" width="110"> <el-table-column label="产品单位" width="110">
<template slot-scope="scope">{{ scope.row.product_.unit }}</template> <template slot-scope="scope">{{ scope.row.product_.unit }}</template>
@ -65,13 +69,15 @@
<template slot-scope="scope">{{ scope.row.end_date }}</template> <template slot-scope="scope">{{ scope.row.end_date }}</template>
</el-table-column> </el-table-column>
<el-table-column label="交货日期" width="110"> <el-table-column label="交货日期" width="110">
<template slot-scope="scope">{{ scope.row.order_.delivery_date }}</template> <template slot-scope="scope">{{
scope.row.order_.delivery_date
}}</template>
</el-table-column> </el-table-column>
<el-table-column label="是否生成子计划" width="120"> <el-table-column label="是否生成子计划" width="120">
<template slot-scope="scope"> <template slot-scope="scope">
<el-tag v-if="scope.row.is_planed==false"></el-tag> <el-tag v-if="scope.row.is_planed == false"></el-tag>
<el-tag v-if="scope.row.is_planed==true"></el-tag> <el-tag v-if="scope.row.is_planed == true"></el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="创建时间" width="160"> <el-table-column label="创建时间" width="160">
@ -81,17 +87,33 @@
align="center" align="center"
label="操作" label="操作"
fixed="right" fixed="right"
width="100px" width="150px"
> >
<template slot-scope="scope"> <template slot-scope="scope">
<el-link type="primary" <el-link type="warning"
v-if="scope.row.state != 70"
@click="handlestatesuspended(scope)"
>暂停
</el-link>
<el-link
type="primary"
v-if="scope.row.state == 70"
@click="handlestate(scope)"
>启用
</el-link>
<el-link
type="danger"
v-if="scope.row.state == 70"
@click="handlestatestop(scope)"
>终止
</el-link>
<el-link
type="primary"
v-if="scope.row.is_planed" v-if="scope.row.is_planed"
@click="handleselectplan(scope)" @click="handleselectplan(scope)"
>查看子计划 >查看子计划
</el-link> </el-link>
<el-link type="primary" <el-link type="primary" v-else @click="handleWork(scope)"
v-else
@click="handleWork(scope)"
>生成子计划 >生成子计划
</el-link> </el-link>
</template> </template>
@ -105,7 +127,12 @@
@pagination="getplanList" @pagination="getplanList"
/> />
</el-card> </el-card>
<el-tabs class="overFlowShow" type="border-card" v-model="activeName" @tab-click="activeNameClick"> <el-tabs
class="overFlowShow"
type="border-card"
v-model="activeName"
@tab-click="activeNameClick"
>
<el-tab-pane label="订单排产" name="订单排产"> <el-tab-pane label="订单排产" name="订单排产">
<el-table <el-table
:data="orderList.results" :data="orderList.results"
@ -132,7 +159,9 @@
<template slot-scope="scope">{{ scope.row.product_.name }}</template> <template slot-scope="scope">{{ scope.row.product_.name }}</template>
</el-table-column> </el-table-column>
<el-table-column label="产品型号" width="110"> <el-table-column label="产品型号" width="110">
<template slot-scope="scope">{{ scope.row.product_.specification }}</template> <template slot-scope="scope">{{
scope.row.product_.specification
}}</template>
</el-table-column> </el-table-column>
<el-table-column label="产品数量"> <el-table-column label="产品数量">
<template slot-scope="scope">{{ scope.row.count }}</template> <template slot-scope="scope">{{ scope.row.count }}</template>
@ -180,17 +209,17 @@
label-position="right" label-position="right"
:rules="rule1" :rules="rule1"
> >
<el-form-item label="排产数量" prop="count"> <el-form-item label="排产数量" prop="count">
<el-input-number v-model="orderplan.count" :min="0"></el-input-number> <el-input-number
v-model="orderplan.count"
:min="0"
></el-input-number>
</el-form-item> </el-form-item>
<el-form-item label="计划排产时间" prop="value1"> <el-form-item label="计划排产时间" prop="value1">
<el-date-picker <el-date-picker
v-model="value1" v-model="value1"
type="daterange" type="daterange"
start-placeholder="计划开始日期" start-placeholder="计划开始日期"
end-placeholder="计划结束日期" end-placeholder="计划结束日期"
format="yyyy 年 MM 月 dd 日" format="yyyy 年 MM 月 dd 日"
@ -198,36 +227,39 @@
> >
</el-date-picker> </el-date-picker>
</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="dialogVisible = false">取消</el-button> <el-button type="danger" @click="dialogVisible = false"
>取消</el-button
>
<el-button type="primary" @click="confirm('Form')">确认</el-button> <el-button type="primary" @click="confirm('Form')">确认</el-button>
</div> </div>
</el-dialog> </el-dialog>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="甘特图" name="甘特图"> <el-tab-pane label="甘特图" name="甘特图">
<gantt <gantt v-if="ganttShow" :proList="proList"></gantt>
v-if="ganttShow"
:proList="proList"
></gantt>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</div> </div>
</template> </template>
<script> <script>
import gantt from "@/components/Gantt/index"; import gantt from "@/components/Gantt/index";
import {getordertoplan} from "@/api/sam"; import { getordertoplan } from "@/api/sam";
import {createProductionplan, getProductionplanList, createsubplan} from "@/api/pm"; import {
import {getMaterialList} from "@/api/mtm"; createProductionplan,
import checkPermission from "@/utils/permission"; getProductionplanList,
createsubplan,
plantoggle,
planstop
} from "@/api/pm";
import { getMaterialList } from "@/api/mtm";
import checkPermission from "@/utils/permission";
import { genTree } from "@/utils";
import {genTree} from "@/utils"; import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination const defaulteorderplan = {};
const defaulteorderplan = {}; export default {
export default { components: { Pagination, gantt },
components: {Pagination, gantt},
data() { data() {
return { return {
orderplan: defaulteorderplan, orderplan: defaulteorderplan,
@ -235,12 +267,14 @@
count: 0, count: 0,
}, },
state_: { state_: {
10: '制定中', 10: "制定中",
20: '已下达', 20: "已下达",
30: '已接受', 30: "已接受",
40: '生产中', 40: "生产中",
50: '已完成', 50: "已完成",
60: '军检完成' 60: "军检完成",
70: "暂停",
80: "终止",
}, },
listQuery: { listQuery: {
page: 1, page: 1,
@ -253,15 +287,15 @@
page: 1, page: 1,
page_size: 20, page_size: 20,
}, },
value1: '', value1: "",
listLoading: true, listLoading: true,
dialogVisiblestate: false,
ganttShow: false, ganttShow: false,
dialogVisible: false, dialogVisible: false,
dialogType: "new", dialogType: "new",
activeName: "订单排产", activeName: "订单排产",
rule1: { rule1: {
number: [{required: true, message: "请输入", trigger: "blur"}], number: [{ required: true, message: "请输入", trigger: "blur" }],
}, },
proList: [], proList: [],
}; };
@ -269,7 +303,6 @@
computed: {}, computed: {},
watch: {}, watch: {},
created() { created() {
this.getorderList(); this.getorderList();
this.getplanList(); this.getplanList();
}, },
@ -296,7 +329,7 @@
this.productionplanList = response.data; this.productionplanList = response.data;
let list = response.data.results; let list = response.data.results;
let arr = []; let arr = [];
list.forEach(item => { list.forEach((item) => {
if (!item.children || item.children.length < 1) { if (!item.children || item.children.length < 1) {
let startTime = new Date(item.start_date).getTime(); let startTime = new Date(item.start_date).getTime();
let endTime = new Date(item.end_date).getTime(); let endTime = new Date(item.end_date).getTime();
@ -320,6 +353,65 @@
this.listLoading = false; this.listLoading = false;
}); });
}, },
//修改任务状态设置暂停
handlestatesuspended(scope) {
this.$confirm("确认暂停任务?", "提醒", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
await plantoggle(scope.row.id).then((res) => {
if (res.code >= 200) {
this.$message.success("任务暂停成功!");
this.getplanList();
}
});
})
.catch((err) => {
console.error(err);
});
},
//修改任务状态设置启动
handlestate(scope) {
this.$confirm("确认启动任务?", "提醒", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
await plantoggle(scope.row.id).then((res) => {
if (res.code >= 200) {
this.$message.success("任务启动成功!");
this.getplanList();
}
});
})
.catch((err) => {
console.error(err);
});
},
//修改任务状态设置终止
handlestatestop(scope) {
this.$confirm("确认终止任务?", "提醒", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
await planstop(scope.row.id).then((res) => {
if (res.code >= 200) {
this.$message.success("任务终止成功!");
this.getplanList();
}
});
})
.catch((err) => {
console.error(err);
});
},
//搜索生产计划 //搜索生产计划
handleFilter() { handleFilter() {
this.listQuery1.page = 1; this.listQuery1.page = 1;
@ -329,7 +421,7 @@
this.listQuery1 = { this.listQuery1 = {
page: 1, page: 1,
page_size: 20, page_size: 20,
} };
this.getplanList(); this.getplanList();
}, },
handleclick(scope) { handleclick(scope) {
@ -340,12 +432,10 @@
this.dialogVisible = true; this.dialogVisible = true;
this.$nextTick(() => { this.$nextTick(() => {
this.$refs["Form"].resetFields(); this.$refs["Form"].resetFields();
this.value1=""; this.value1 = "";
}); });
}, },
async confirm(form) { async confirm(form) {
this.orderplan.start_date = this.value1[0]; this.orderplan.start_date = this.value1[0];
if (this.delivery_date >= this.value1[1]) { if (this.delivery_date >= this.value1[1]) {
@ -354,8 +444,8 @@
this.$message.error("计划完成时间超过订单交付日期,请从新选择日期!"); this.$message.error("计划完成时间超过订单交付日期,请从新选择日期!");
} }
this.orderplan.order = this.orderID this.orderplan.order = this.orderID;
if (this.orderplan.count <= (this.countsx - this.planed_count)) { if (this.orderplan.count <= this.countsx - this.planed_count) {
createProductionplan(this.orderplan).then((res) => { createProductionplan(this.orderplan).then((res) => {
if (res.code >= 200) { if (res.code >= 200) {
this.getorderList(); this.getorderList();
@ -367,7 +457,6 @@
} else { } else {
this.$message.error("排产数超过所需数,请合理排产!"); this.$message.error("排产数超过所需数,请合理排产!");
} }
}, },
handleWork(scope) { handleWork(scope) {
this.$confirm("确认生成子计划?", "提醒", { this.$confirm("确认生成子计划?", "提醒", {
@ -379,20 +468,19 @@
await createsubplan(scope.row.id).then((res) => { await createsubplan(scope.row.id).then((res) => {
if (res.code >= 200) { if (res.code >= 200) {
this.$message.success("生成子计划成功!"); this.$message.success("生成子计划成功!");
this.$router.push({name: "work", params: {id: scope.row.id},}) this.$router.push({ name: "work", params: { id: scope.row.id } });
this.getplanList() this.getplanList();
} }
}); });
}) })
.catch((err) => { .catch((err) => {
console.error(err); console.error(err);
}); });
}, },
activeNameClick(tab, event) { activeNameClick(tab, event) {
debugger; debugger;
console.log(tab, event); console.log(tab, event);
if (tab.label === '甘特图') { if (tab.label === "甘特图") {
this.ganttShow = true; this.ganttShow = true;
} else { } else {
this.ganttShow = false; this.ganttShow = false;
@ -400,21 +488,19 @@
}, },
//查看子计划 //查看子计划
handleselectplan(scope) { handleselectplan(scope) {
this.$router.push({name: "work", params: {id: scope.row.id},}) this.$router.push({ name: "work", params: { id: scope.row.id } });
}
}, },
}; },
};
</script> </script>
<style> <style>
.el-table .warning-row { .el-table .warning-row {
background: oldlace; background: oldlace;
} }
.el-table .success-row { .el-table .success-row {
background: #f0f9eb; background: #f0f9eb;
} }
</style> </style>

View File

@ -134,12 +134,14 @@
page_size: 20, page_size: 20,
}, },
state_: { state_: {
10: '制定中', 10: "制定中",
20: '已下达', 20: "已下达",
30: '已接受', 30: "已接受",
40: '生产中', 40: "生产中",
50: '已完成', 50: "已完成",
60: '军检完成' 60: "军检完成",
70: "暂停",
80: "终止",
}, },
actstate_: { actstate_: {
6: "待复检", 6: "待复检",

View File

@ -0,0 +1,37 @@
# Generated by Django 3.2.9 on 2022-02-18 07:20
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('mtm', '0046_alter_recordform_type'),
]
operations = [
migrations.CreateModel(
name='PackItem',
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(max_length=100, verbose_name='名称')),
('specification', models.CharField(blank=True, max_length=100, null=True, verbose_name='型号')),
('unit', models.CharField(max_length=10, verbose_name='单位')),
('count', models.PositiveIntegerField(default=1, verbose_name='数量')),
('sort', models.PositiveIntegerField(default=1, verbose_name='序号')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='packitem_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('material', 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='packitem_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
]

View File

@ -1,3 +1,4 @@
from cv2 import meanStdDev
from django.db import models from django.db import models
from django.db.models.base import Model from django.db.models.base import Model
import django.utils.timezone as timezone import django.utils.timezone as timezone
@ -49,6 +50,18 @@ class Material(CommonAModel):
def __str__(self): def __str__(self):
return self.name return self.name
class PackItem(CommonAModel):
"""
装箱项目
"""
material = models.ForeignKey(Material, verbose_name='关联成品',
on_delete=models.CASCADE)
name = models.CharField('名称', max_length=100)
specification = models.CharField('型号', max_length=100, null=True, blank=True)
unit = models.CharField('单位', max_length=10)
count = models.PositiveIntegerField('数量', default=1)
sort = models.PositiveIntegerField('序号', default=1)
class Process(CommonAModel): class Process(CommonAModel):
""" """
工序 工序

View File

@ -3,7 +3,7 @@ from rest_framework import serializers
from rest_framework.exceptions import ParseError, ValidationError from rest_framework.exceptions import ParseError, ValidationError
from utils.mixins import DynamicFieldsSerializerMixin from utils.mixins import DynamicFieldsSerializerMixin
from .models import Material, Process, RecordForm, RecordFormField, Step, SubprodctionMaterial, TechDoc, UsedStep, SubProduction from .models import Material, PackItem, Process, RecordForm, RecordFormField, Step, SubprodctionMaterial, TechDoc, UsedStep, SubProduction
from apps.system.serializers import FileSimpleSerializer, OrganizationSimpleSerializer from apps.system.serializers import FileSimpleSerializer, OrganizationSimpleSerializer
@ -24,6 +24,20 @@ class MaterialDetailSerializer(serializers.ModelSerializer):
objs = Process.objects.filter(subproduction_process__product=obj, subproduction_process__is_deleted=False, is_deleted=False).distinct().order_by('number') objs = Process.objects.filter(subproduction_process__product=obj, subproduction_process__is_deleted=False, is_deleted=False).distinct().order_by('number')
return ProcessSimpleSerializer(instance=objs, many=True).data return ProcessSimpleSerializer(instance=objs, many=True).data
class PackItemSerializer(serializers.ModelSerializer):
class Meta:
model = PackItem
fields = '__all__'
class PackItemCreateSerializer(serializers.ModelSerializer):
class Meta:
model = PackItem
fields = ['material', 'name', 'specification', 'unit', 'count', 'sort']
class PackItemUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = PackItem
fields = ['count', 'sort']
class MaterialSimpleSerializer(serializers.ModelSerializer): class MaterialSimpleSerializer(serializers.ModelSerializer):
class Meta: class Meta:

View File

@ -1,11 +1,12 @@
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, OtherMaterialViewSet, OutputMaterialViewSet, ProcessViewSet, RecordFormFieldViewSet, RecordFormViewSet, StepViewSet, SubProductionViewSet, TechDocViewSet, UsedStepViewSet from apps.mtm.views import InputMaterialViewSet, MaterialViewSet, OtherMaterialViewSet, OutputMaterialViewSet, PackItemViewSet, 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('packitem', PackItemViewSet, basename='packitem')
router.register('process', ProcessViewSet, basename='process') router.register('process', ProcessViewSet, basename='process')
router.register('step', StepViewSet, basename='step') router.register('step', StepViewSet, basename='step')
router.register('subproducation', SubProductionViewSet, basename='subproducation') router.register('subproducation', SubProductionViewSet, basename='subproducation')

View File

@ -3,8 +3,8 @@ 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.filters import MaterialFilterSet, TechDocFilterset from apps.mtm.filters import MaterialFilterSet, TechDocFilterset
from apps.mtm.models import Material, Process, RecordForm, RecordFormField, Step, SubprodctionMaterial, TechDoc, UsedStep, SubProduction from apps.mtm.models import Material, PackItem, Process, RecordForm, RecordFormField, Step, SubprodctionMaterial, TechDoc, UsedStep, SubProduction
from apps.mtm.serializers import InputMaterialSerializer, InputMaterialUpdateSerializer, MaterialDetailSerializer, MaterialSerializer, MaterialSimpleSerializer, OtherMaterialSerializer, OutputMaterialSerializer, OutputMaterialUpdateSerializer, ProcessSerializer, RecordFormCreateSerializer, RecordFormDetailSerializer, RecordFormFieldCreateSerializer, RecordFormFieldSerializer, RecordFormFieldUpdateSerializer, RecordFormSerializer, RecordFormUpdateSerializer, StepDetailSerializer, StepSerializer, SubProductionCreateUpdateSerializer, SubProductionSerializer, SubprodctionMaterialListSerializer, TechDocCreateSerializer, TechDocListSerializer, TechDocUpdateSerializer, UsedStepCreateSerializer, UsedStepListSerializer, UsedStepUpdateSerializer from apps.mtm.serializers import InputMaterialSerializer, InputMaterialUpdateSerializer, MaterialDetailSerializer, MaterialSerializer, MaterialSimpleSerializer, OtherMaterialSerializer, OutputMaterialSerializer, OutputMaterialUpdateSerializer, PackItemCreateSerializer, PackItemSerializer, PackItemUpdateSerializer, ProcessSerializer, RecordFormCreateSerializer, RecordFormDetailSerializer, RecordFormFieldCreateSerializer, RecordFormFieldSerializer, RecordFormFieldUpdateSerializer, RecordFormSerializer, RecordFormUpdateSerializer, StepDetailSerializer, StepSerializer, SubProductionCreateUpdateSerializer, SubProductionSerializer, SubprodctionMaterialListSerializer, 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
@ -31,6 +31,25 @@ class MaterialViewSet(PageOrNot, CreateUpdateModelAMixin, ModelViewSet):
return MaterialDetailSerializer return MaterialDetailSerializer
return MaterialSerializer return MaterialSerializer
class PackItemViewSet(CreateUpdateModelAMixin, ModelViewSet):
"""
装箱项目-增删改查
"""
perms_map = {'get': '*', 'post': 'packitem_create',
'put': 'packitem_update', 'delete': 'packitem_delete'}
queryset = PackItem.objects.all()
serializer_class = PackItemSerializer
search_fields = ['name', 'number']
filterset_fields = ['material']
ordering = ['sort']
def get_serializer_class(self):
if self.action == 'create':
return PackItemCreateSerializer
elif self.action == 'update':
return PackItemUpdateSerializer
return super().get_serializer_class()
class ProcessViewSet(PageOrNot, CreateUpdateModelAMixin, ModelViewSet): class ProcessViewSet(PageOrNot, CreateUpdateModelAMixin, ModelViewSet):
""" """

View File

@ -0,0 +1,45 @@
# Generated by Django 3.2.9 on 2022-02-18 08:16
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('mtm', '0047_packitem'),
('pm', '0024_auto_20220217_1524'),
]
operations = [
migrations.AddField(
model_name='subproductionplan',
name='first_tester',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='首件检查员'),
),
migrations.AddField(
model_name='subproductionplan',
name='is_first_testok',
field=models.BooleanField(default=True, verbose_name='首件是否合格'),
),
migrations.CreateModel(
name='FirstItem',
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='删除标记')),
('field_value', models.JSONField(blank=True, null=True, verbose_name='录入值')),
('is_hidden', models.BooleanField(default=False, verbose_name='是否隐藏')),
('is_testok', models.BooleanField(blank=True, null=True, verbose_name='是否合格')),
('form_field', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.recordformfield', verbose_name='关联自定义表格字段')),
('subproduction_plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='item_test_record', to='pm.subproductionplan', verbose_name='关联的子计划')),
],
options={
'abstract': False,
},
),
]

View File

@ -0,0 +1,65 @@
# Generated by Django 3.2.9 on 2022-02-18 08:36
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('mtm', '0047_packitem'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('pm', '0025_auto_20220218_1616'),
]
operations = [
migrations.RemoveField(
model_name='subproductionplan',
name='first_tester',
),
migrations.RemoveField(
model_name='subproductionplan',
name='is_first_testok',
),
migrations.AddField(
model_name='subproductionplan',
name='first_sign_time',
field=models.DateTimeField(blank=True, null=True, verbose_name='首件签字时间'),
),
migrations.AddField(
model_name='subproductionplan',
name='form',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.recordform', verbose_name='首件检查表'),
),
migrations.AddField(
model_name='subproductionplan',
name='is_testok',
field=models.BooleanField(blank=True, null=True, verbose_name='首件是否合格'),
),
migrations.AddField(
model_name='subproductionplan',
name='leader_1',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='first_leader_1', to=settings.AUTH_USER_MODEL, verbose_name='工序负责人'),
),
migrations.AddField(
model_name='subproductionplan',
name='leader_2',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='first_leader_2', to=settings.AUTH_USER_MODEL, verbose_name='技术负责人'),
),
migrations.AddField(
model_name='subproductionplan',
name='leader_3',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='first_leader_3', to=settings.AUTH_USER_MODEL, verbose_name='总检'),
),
migrations.AddField(
model_name='subproductionplan',
name='remark',
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='备注'),
),
migrations.AddField(
model_name='subproductionplan',
name='tester',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='first_tester', to=settings.AUTH_USER_MODEL, verbose_name='首件检查员'),
),
]

View File

@ -1,5 +1,5 @@
from io import open_code
from apps.system.models import CommonAModel, Organization from apps.system.models import CommonAModel, Organization, User
from django.db import models from django.db import models
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
from django.db.models.base import Model from django.db.models.base import Model
@ -7,7 +7,7 @@ import django.utils.timezone as timezone
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from utils.model import SoftModel, BaseModel from utils.model import SoftModel, BaseModel
from apps.mtm.models import Material, Process, RecordFormField, SubProduction, SubprodctionMaterial from apps.mtm.models import Material, Process, RecordForm, RecordFormField, SubProduction, SubprodctionMaterial
from apps.sam.models import Order from apps.sam.models import Order
class ProductionPlan(CommonAModel): class ProductionPlan(CommonAModel):
@ -92,19 +92,32 @@ class SubProductionPlan(CommonAModel):
is_picked = models.BooleanField('是否已领料', default=False) is_picked = models.BooleanField('是否已领料', default=False)
# wproducts = models.JSONField('半成品表', default=list, blank=True) # wproducts = models.JSONField('半成品表', default=list, blank=True)
is_testok = models.BooleanField('首件是否合格', null=True, blank=True)
form = models.ForeignKey(RecordForm, verbose_name='首件检查表', on_delete=models.CASCADE, null=True, blank=True)
tester = models.ForeignKey(User, on_delete=models.CASCADE,
verbose_name="首件检查员", null=True, blank=True, related_name='first_tester')
leader_1 = models.ForeignKey(User, on_delete=models.CASCADE,
verbose_name="工序负责人", null=True, blank=True, related_name='first_leader_1')
leader_2 = models.ForeignKey(User, on_delete=models.CASCADE,
verbose_name="技术负责人", null=True, blank=True, related_name='first_leader_2')
leader_3 = models.ForeignKey(User, on_delete=models.CASCADE,
verbose_name="总检", null=True, blank=True, related_name='first_leader_3')
first_sign_time = models.DateTimeField('首件签字时间', null=True, blank=True)
remark = models.CharField('备注', max_length=100, null=True, blank=True)
class Meta: class Meta:
verbose_name = '子生产计划' verbose_name = '子生产计划'
verbose_name_plural = verbose_name verbose_name_plural = verbose_name
# class FirstItem(BaseModel): class FirstItem(BaseModel):
# """ """
# 首件确认表记录条目 首件确认表记录条目
# """ """
# form_field = models.ForeignKey(RecordFormField, verbose_name='关联自定义表格字段', on_delete=models.CASCADE) form_field = models.ForeignKey(RecordFormField, verbose_name='关联自定义表格字段', on_delete=models.CASCADE)
# field_value = models.JSONField('录入值', null=True, blank=True) field_value = models.JSONField('录入值', null=True, blank=True)
# is_hidden = models.BooleanField('是否隐藏', default=False) is_hidden = models.BooleanField('是否隐藏', default=False)
# is_testok = models.BooleanField('是否合格', null=True, blank=True) is_testok = models.BooleanField('是否合格', null=True, blank=True)
# subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联的子计划', on_delete=models.CASCADE, related_name='item_test_record') subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联的子计划', on_delete=models.CASCADE, related_name='item_test_record')
class SubProductionProgress(BaseModel): class SubProductionProgress(BaseModel):
""" """

View File

@ -1,8 +1,9 @@
from apps.mtm.models import RecordForm
from apps.pm.models import ProductionPlan, SubProductionPlan, SubProductionProgress from apps.pm.models import ProductionPlan, SubProductionPlan, SubProductionProgress
from rest_framework import serializers from rest_framework import serializers
from apps.sam.serializers import OrderSerializer, OrderSimpleSerializer from apps.sam.serializers import OrderSerializer, OrderSimpleSerializer
from apps.mtm.serializers import MaterialSimpleSerializer, ProcessSimpleSerializer, SubProductionSimpleSerializer from apps.mtm.serializers import MaterialSimpleSerializer, ProcessSimpleSerializer, RecordFormSimpleSerializer, SubProductionSimpleSerializer
from apps.system.serializers import OrganizationSimpleSerializer from apps.system.serializers import OrganizationSimpleSerializer, UserSimpleSerializer
from utils.mixins import DynamicFieldsSerializerMixin from utils.mixins import DynamicFieldsSerializerMixin
@ -75,3 +76,25 @@ class SubProductionProgressSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = SubProductionProgress model = SubProductionProgress
fields = '__all__' fields = '__all__'
class FirstTestInitSerializer(serializers.Serializer):
form = serializers.PrimaryKeyRelatedField(queryset=RecordForm.objects.all(), required=True)
class FirstTestDetailSerializer(serializers.ModelSerializer):
tester_ = UserSimpleSerializer(source='tester', read_only=True)
leader_1_ = UserSimpleSerializer(source='leader_1', read_only=True)
leader_2_ = UserSimpleSerializer(source='leader_2', read_only=True)
leader_3_ = UserSimpleSerializer(source='leader_3', read_only=True)
form_ = RecordFormSimpleSerializer(source='form', read_only=True)
# record_data = TestRecordItemSerializer(source='item_test_record', read_only=True, many=True)
record_data = serializers.SerializerMethodField()
class Meta:
model = SubProductionPlan
fields = ['id', 'form', 'form_', 'is_testok', 'remark', 'first_sign_time'
'tester', 'tester_', 'leader_1', 'leader_1_', 'leader_2',
'leader_2_', 'leader_3', 'leader_3_']
def get_record_data(self, obj):
return None

View File

@ -10,7 +10,7 @@ from apps.inm.serializers import MaterialBatchSerializer
from apps.mtm.models import Material, Step, SubProduction, SubprodctionMaterial from apps.mtm.models import Material, Step, SubProduction, SubprodctionMaterial
from apps.pm.filters import PlanFilterSet, SubproductionProgressFilterSet from apps.pm.filters import PlanFilterSet, SubproductionProgressFilterSet
from apps.system.mixins import CreateUpdateModelAMixin from apps.system.mixins import CreateUpdateModelAMixin
from apps.pm.serializers import GenSubPlanSerializer, PickNeedSerializer, PlanDestorySerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, ResourceConvertListSerializer, ResourceConvertSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer, SubProductionProgressSerializer from apps.pm.serializers import FirstTestInitSerializer, GenSubPlanSerializer, PickNeedSerializer, PlanDestorySerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, ResourceConvertListSerializer, ResourceConvertSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer, SubProductionProgressSerializer
from rest_framework.mixins import CreateModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin from rest_framework.mixins import CreateModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin
from apps.pm.models import ProductionPlan, SubProductionProgress, SubProductionPlan from apps.pm.models import ProductionPlan, SubProductionProgress, SubProductionPlan
from rest_framework.viewsets import GenericViewSet, ModelViewSet from rest_framework.viewsets import GenericViewSet, ModelViewSet
@ -229,6 +229,23 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo
have = MaterialBatchSerializer(instance=objs, many=True).data have = MaterialBatchSerializer(instance=objs, many=True).data
return Response({'need':need, 'have':have}) return Response({'need':need, 'have':have})
@action(methods=['put'], detail=True, perms_map={'post':'first_test'}, serializer_class=FirstTestInitSerializer)
@transaction.atomic
def first_test_init(self, request, pk=None):
"""
获取首件检查表
"""
obj = self.get_object()
if obj.is_testok is None:
rdata = request.data
serializer = self.get_serializer(data=rdata)
serializer.is_valid(raise_exception=True)
form = serializer.validated_data.get('form')
raise APIException('已经过首件确认')
class SubProductionProgressViewSet(ListModelMixin, GenericViewSet): class SubProductionProgressViewSet(ListModelMixin, GenericViewSet):
""" """
生产进度 生产进度