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

This commit is contained in:
shijing 2022-02-18 10:51:20 +08:00
commit 2c4f82d393
22 changed files with 539 additions and 380 deletions

View File

@ -357,7 +357,7 @@ export default {
handleDetail(scope) { handleDetail(scope) {
this.$router.push({ this.$router.push({
name: "fifodetail", name: "fifodetail",
params: { id: scope.row.id, pu_order: scope.row.pu_order }, params: { id: scope.row.id, pu_order: scope.row.pu_order},
}); });
}, },

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<el-card style="margin-top: 2px"> <el-card style="margin-top: 2px">
<el-button type="primary" icon="el-icon-plus" @click="handlecgxCreate" <el-button v-if="this.$route.params.pu_order!=null" type="primary" icon="el-icon-plus" @click="handlecgxCreate"
>新增采购项入库</el-button >新增采购项入库</el-button
> >
<el-table <el-table
@ -39,20 +39,20 @@
<el-table-column label="入库数量"> <el-table-column label="入库数量">
<template slot-scope="scope">{{ scope.row.count }}</template> <template slot-scope="scope">{{ scope.row.count }}</template>
</el-table-column> </el-table-column>
<el-table-column label="是否需要复验"> <el-table-column label="是否需要复验" v-if="this.$route.params.pu_order!=null">
<template slot-scope="scope"> <template slot-scope="scope">
<el-tag v-if="scope.row.need_test == false"></el-tag> <el-tag v-if="scope.row.need_test == false"></el-tag>
<el-tag v-else></el-tag> <el-tag v-else></el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="复验是否合格"> <el-table-column label="复验是否合格" v-if="this.$route.params.pu_order!=null">
<template slot-scope="scope"> <template slot-scope="scope">
<el-tag v-if="scope.row.is_testok == false">不合格</el-tag> <el-tag v-if="scope.row.is_testok == false">不合格</el-tag>
<el-tag v-else>合格</el-tag> <el-tag v-else>合格</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="文件"> <el-table-column label="文件" v-if="this.$route.params.pu_order!=null">
<template slot-scope="scope" v-if="scope.row.files"> <template slot-scope="scope" v-if="scope.row.files">
<div v-for="item in scope.row.files_" v-bind:key="item.id"> <div v-for="item in scope.row.files_" v-bind:key="item.id">
<el-link :href="item.path" target="_blank" type="primary">{{ <el-link :href="item.path" target="_blank" type="primary">{{
@ -61,7 +61,7 @@
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="操作" width="220px"> <el-table-column align="center" label="操作" width="220px" v-if="this.$route.params.pu_order!=null">
<template slot-scope="scope"> <template slot-scope="scope">
<el-link <el-link
type="primary" type="primary"

View File

@ -1,282 +1,310 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
<el-card > <el-card>
<div slot="header" class="clearfix"> <div slot="header" class="clearfix">
<span>合同订单列表</span> <span>合同订单列表</span>
</div>
</div> <el-button type="primary" @click="handlecount">计算物料</el-button>
<el-button type="primary" @click="handlecount" <el-table
>计算物料</el-button> :data="orderList.results"
<el-table ref="multipleTable"
:data="orderList.results" border
ref="multipleTable" fit
border stripe
fit highlight-current-row
stripe height="100"
highlight-current-row v-el-height-adaptive-table="{ bottomOffset: 25 }"
height="100" >
v-el-height-adaptive-table="{bottomOffset: 25}" <el-table-column type="selection" width="55"> </el-table-column>
>
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column label="订单编号" width="110">
<template slot-scope="scope">{{ scope.row.number }}</template>
</el-table-column>
<el-table-column label="所需产品" show-overflow-tooltip width="150">
<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="产品型号">
<template slot-scope="scope">{{ scope.row.product_.specification }}</template>
</el-table-column>
<el-table-column label="客户名称" show-overflow-tooltip width="150">
<template slot-scope="scope">{{ scope.row.customer_.name }}</template>
</el-table-column>
<el-table-column label="合同编号" show-overflow-tooltip width="150">
<template slot-scope="scope">{{ scope.row.contract_.number }}</template>
</el-table-column>
<el-table-column label="合同名称" show-overflow-tooltip width="150">
<template slot-scope="scope">{{ scope.row.contract_.name }}</template>
</el-table-column>
<el-table-column label="交货日期" width="110">
<template slot-scope="scope">{{ scope.row.delivery_date }}</template>
</el-table-column>
<el-table-column label="创建时间" width="110">
<template slot-scope="scope">{{ scope.row.create_time }}</template>
</el-table-column>
<el-table-column label="计划生产数" width="150px" fixed="right">
<template slot-scope="scope" >
<el-form :model="scope.row">
<el-form-item size="mini">
<el-input-number v-model="scope.row.pgcount" :min="0" :value="0"></el-input-number>
</el-form-item>
</el-form>
</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>
<el-button style="float: right; padding: 3px 0" @click="handlebcpcount" type="primary">半成品折合</el-button>
</div>
<el-table
:data="materialpzTable"
border
fit
stripe
style="width: 100%"
height="300"
ref="multipleTables"
>
<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.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="生产所需数量" width="150">
<template slot-scope="scope">
<div v-if="scope.row.type==2">
<el-form :model="scope.row">
<el-form-item size="mini">
<el-input-number v-model="scope.row.bcpcount" :min="0" :value="0"></el-input-number>
</el-form-item>
</el-form>
</div>
<div v-else> {{ scope.row.count }}</div></template>
</el-table-column>
<el-table-column label="剩余量" >
<template slot-scope="scope">
<el-tag v-if="scope.row.count_safe!=null&&scope.row.count_safe>(scope.row.inv_count-scope.row.count)" type="danger"> {{ scope.row.inv_count-scope.row.count }}</el-tag>
<el-tag v-else> {{ scope.row.inv_count-scope.row.count }}</el-tag>
</template>
</el-table-column>
<el-table-column label="安全库存">
<template slot-scope="scope">{{ scope.row.count_safe }}</template>
</el-table-column>
</el-table>
</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="equipmentTable"
border
fit
stripe
style="width: 100%"
height="280"
>
<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"> <el-table-column label="订单编号" width="110">
<div v-if="scope.row.equip_.type===2"> <template slot-scope="scope">{{ scope.row.number }}</template>
<el-tag v-if="scope.row.equip_.state===40" type="danger"> </el-table-column>
禁用
</el-tag> <el-table-column label="所需产品" show-overflow-tooltip width="150">
<el-tag v-else type="success"> <template slot-scope="scope">{{
合格 scope.row.product_.name
</el-tag> }}</template>
</div> </el-table-column>
<div v-else>
<el-tag v-if="scope.row.state===10" type="success"> <el-table-column label="产品数量">
{{ state_[scope.row.state] }} <template slot-scope="scope">{{ scope.row.count }}</template>
</el-tag> </el-table-column>
<el-tag v-else-if="scope.row.state===20"> <el-table-column label="已派数量">
{{ state_[scope.row.state] }} <template slot-scope="scope">{{
</el-tag> scope.row.planed_count
<el-tag v-else-if="scope.row.state===30" type="warning"> }}</template>
{{ state_[scope.row.state] }} </el-table-column>
</el-tag>
<el-tag v-else type="danger"> <el-table-column label="产品型号">
{{ state_[scope.row.state] }} <template slot-scope="scope">{{
</el-tag> scope.row.product_.specification
</div> }}</template>
</template> </el-table-column>
<template slot-scope="scope"> {{state_[scope.row.state]}}</template> <el-table-column label="客户名称" show-overflow-tooltip width="150">
</el-table-column> <template slot-scope="scope">{{
<el-table-column label="设备编号"> scope.row.customer_.name
<template slot-scope="scope"> {{scope.row.number}}</template> }}</template>
</el-table-column> </el-table-column>
<el-table-column label="设备型号"> <el-table-column label="合同编号" show-overflow-tooltip width="150">
<template slot-scope="scope"> {{scope.row.model}}</template> <template slot-scope="scope">{{
</el-table-column> scope.row.contract_.number
}}</template>
</el-table-column>
</el-table> <el-table-column label="合同名称" show-overflow-tooltip width="150">
<pagination <template slot-scope="scope">{{
v-show="equipmentTable.count > 0" scope.row.contract_.name
:total="equipmentTable.count" }}</template>
:page.sync="listQuery.page" </el-table-column>
:limit.sync="listQuery.page_size" <el-table-column label="交货日期" width="110">
@pagination="getorderList" <template slot-scope="scope">{{
/> scope.row.delivery_date
}}</template>
</el-card> </el-table-column>
<el-table-column label="创建时间" width="110">
<template slot-scope="scope">{{
scope.row.create_time
}}</template>
</el-table-column>
<el-table-column label="计划生产数" width="150px" fixed="right">
<template slot-scope="scope">
<el-form :model="scope.row">
<el-form-item size="mini">
<el-input-number
v-model="scope.row.pgcount"
:min="0"
:value="0"
></el-input-number>
</el-form-item>
</el-form>
</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>
<el-button
style="float: right; "
@click="handlebcpcount"
type="primary"
v-if="butshow"
>半成品折合</el-button
>
</div>
<el-table
:data="materialpzTable"
border
fit
stripe
style="width: 100%"
height="330"
ref="multipleTables"
>
<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.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="生产所需数量" width="150">
<template slot-scope="scope">
<div v-if="scope.row.type == 2">
<el-form :model="scope.row">
<el-form-item size="mini">
<el-input-number
v-model="scope.row.bcpcount"
:min="0"
:value="0"
></el-input-number>
</el-form-item>
</el-form>
</div>
<div v-else>{{ scope.row.count }}</div></template
>
</el-table-column>
<el-table-column label="剩余量">
<template slot-scope="scope">
<el-tag
v-if="
scope.row.count_safe != null &&
scope.row.count_safe >
scope.row.inv_count - scope.row.count
"
type="danger"
>
{{ scope.row.inv_count - scope.row.count }}</el-tag
>
<el-tag v-else>
{{ scope.row.inv_count - scope.row.count }}</el-tag
>
</template>
</el-table-column>
<el-table-column label="安全库存">
<template slot-scope="scope">{{
scope.row.count_safe
}}</template>
</el-table-column>
</el-table>
</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="equipmentTable"
border
fit
stripe
style="width: 100%"
height="280"
>
<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">
<div v-if="scope.row.equip_.type === 2">
<el-tag
v-if="scope.row.equip_.state === 40"
type="danger"
>
禁用
</el-tag>
<el-tag v-else type="success"> 合格 </el-tag>
</div>
<div v-else>
<el-tag v-if="scope.row.state === 10" type="success">
{{ state_[scope.row.state] }}
</el-tag>
<el-tag v-else-if="scope.row.state === 20">
{{ state_[scope.row.state] }}
</el-tag>
<el-tag v-else-if="scope.row.state === 30" type="warning">
{{ state_[scope.row.state] }}
</el-tag>
<el-tag v-else type="danger">
{{ state_[scope.row.state] }}
</el-tag>
</div>
</template>
<template slot-scope="scope">
{{ state_[scope.row.state] }}</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.model }}</template>
</el-table-column>
</el-table>
<pagination
v-show="equipmentTable.count > 0"
:total="equipmentTable.count"
:page.sync="listQuery.page"
:limit.sync="listQuery.page_size"
@pagination="getorderList"
/>
</el-card>
</el-col>
</el-row>
</el-col> </el-col>
</el-row> </el-row>
</el-col>
</el-row>
</div> </div>
</template> </template>
<script> <script>
import { getordertoplan } from "@/api/sam"; import { getordertoplan } from "@/api/sam";
import { createProductionplan,createConvert,createresource,createequip} from "@/api/pm"; import {
createProductionplan,
createConvert,
createresource,
createequip,
} from "@/api/pm";
import { getMaterialList } from "@/api/mtm"; import { getMaterialList } from "@/api/mtm";
import checkPermission from "@/utils/permission"; import checkPermission from "@/utils/permission";
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 }, components: { Pagination },
data() { data() {
return { return {
orderplan: defaulteorderplan,
orderplan: defaulteorderplan,
orderList: { orderList: {
count: 0, count: 0,
}, },
options_:{ options_: {
1: "成品",
"1":'成品', 2: "半成品",
"2":'半成品', 3: "主要原料",
"3":'主要原料', 4: "辅助原料",
"4":'辅助原料', 5: "加工工具",
"5":'加工工具', 6: "辅助工具",
"6":'辅助工具',
}, },
state_: { state_: {
10: '完好', 10: "完好",
20: '限用', 20: "限用",
30: '在修', 30: "在修",
40: '禁用', 40: "禁用",
}, },
listQuery: { listQuery: {
page: 1, page: 1,
page_size: 20, page_size: 20,
}, },
materialpzTable:"", materialpzTable: [],
mutipID:[], mutipID: [],
bcpID:[], bcpID: [],
equipmentTable:[], equipmentTable: [],
listLoading: true, listLoading: true,
dialogVisible: false, dialogVisible: false,
dialogType: "new", dialogType: "new",
zhbcp:[], zhbcp: [],
rule1: { rule1: {
number: [{ required: true, message: "请输入", trigger: "blur" }], number: [{ required: true, message: "请输入", trigger: "blur" }],
}, },
butshow:true,
}; };
}, },
computed: {}, computed: {},
watch: {}, watch: {},
created() { created() {
this.getorderList(); this.getorderList();
}, },
methods: { methods: {
checkPermission, checkPermission,
//订单列表 //订单列表
getorderList() { getorderList() {
this.listLoading = true; this.listLoading = true;
getordertoplan(this.listQuery).then((response) => { getordertoplan(this.listQuery).then((response) => {
@ -286,88 +314,81 @@ export default {
this.listLoading = false; this.listLoading = false;
}); });
}, },
//物料计算
handlecount()
{
let _this=this
_this.mutipID=[]
this.$refs.multipleTable.selection.forEach((item) => {
_this.mutipID.push({
"id":item.product_.id,
"count": item.pgcount
});
});
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("成功");
}
});
},
//半成品折合物料 //物料计算
handlebcpcount(){ handlecount() {
let _this=this let _this = this;
_this.bcpID=[] this.butshow=true;
this.$refs.multipleTables.selection.forEach((item) => { _this.mutipID = [];
_this.bcpID.push({ this.$refs.multipleTable.selection.forEach((item) => {
"id":item.id, _this.mutipID.push({
"count": item.bcpcount id: item.product_.id,
count: item.pgcount,
}); });
}); });
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("成功");
}
});
},
//半成品折合物料
_this.zhbcp=[], handlebcpcount() {
createConvert(this.bcpID).then((res) => { let _this = this;
if (res.code >= 200) { _this.bcpID = [];
this.zhbcp= res.data this.$refs.multipleTables.selection.forEach((item) => {
_this.bcpID.push({
id: item.id,
count: item.bcpcount,
});
});
createConvert(this.bcpID).then((res) => {
if (res.code >= 200) {
} this.materialpzTable.forEach((items) => {
res.data.forEach((item) => {
if (item.id == items.id) {
items.count = items.count - item.count;
}
}); });
});
},
console.log(this.materialpzTable);
this.butshow=false;
}
});
},
handleclick(scope) {
this.orderID = scope.row.id;
handleclick(scope){
this.orderID = scope.row.id;
this.dialogVisible = true; this.dialogVisible = true;
this.$nextTick(() => { this.$nextTick(() => {
this.$refs["Form"].clearValidate(); this.$refs["Form"].clearValidate();
}); });
}, },
async confirm(form) { async confirm(form) {
this.orderplan.start_date = this.value1[0];
this.orderplan.start_date = this.value1[0]; this.orderplan.end_date = this.value1[1];
this.orderplan.end_date = this.value1[1]; this.orderplan.order = this.orderID;
this.orderplan.order = this.orderID
createProductionplan(this.orderplan).then((res) => { createProductionplan(this.orderplan).then((res) => {
if (res.code >= 200) { if (res.code >= 200) {
this.getorderList(); this.getorderList();
this.getplanList(); this.getplanList();
this.dialogVisible = false; this.dialogVisible = false;
this.$message.success("成功"); this.$message.success("成功");
} }
}); });
}, },
}, },
}; };
</script>
</script>

View File

@ -806,7 +806,7 @@ filediv.innerHTML=' <div id="wordView" v-html='+this.wordText+' />';
{ {
this.pdf=tab.name; this.pdf=tab.name;
var filediv = document.getElementById('file'); var filediv = document.getElementById('file');
filediv.innerHTML=' <frame width=800 height=900 frameborder=0 scrolling=auto src='+tab.name+'></frame>'; filediv.innerHTML=' <iframe width=1000 height=900 frameborder=0 scrolling=auto src='+tab.name+'></iframe>';
} }

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.9 on 2022-02-17 13:55
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('hrm', '0005_auto_20220126_1351'),
]
operations = [
migrations.AddField(
model_name='employee',
name='is_atwork',
field=models.BooleanField(default=False, verbose_name='当前在岗'),
),
migrations.AddField(
model_name='employee',
name='last_check_time',
field=models.DateTimeField(blank=True, null=True, verbose_name='打卡时间'),
),
]

View File

@ -0,0 +1,41 @@
# Generated by Django 3.2.9 on 2022-02-18 00:43
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),
('hrm', '0006_auto_20220217_2155'),
]
operations = [
migrations.AddField(
model_name='employee',
name='not_work_remark',
field=models.CharField(blank=True, max_length=200, null=True, verbose_name='当前未打卡说明'),
),
migrations.CreateModel(
name='NotWorkRemark',
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='删除标记')),
('year', models.PositiveSmallIntegerField(default=2022, verbose_name='')),
('month', models.PositiveSmallIntegerField(default=2, verbose_name='')),
('day', models.PositiveSmallIntegerField(default=1, verbose_name='')),
('remark', models.CharField(blank=True, max_length=200, null=True, verbose_name='未打卡说明')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='notworkremark_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='notworkremark_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户')),
],
options={
'abstract': False,
},
),
]

View File

@ -30,6 +30,9 @@ class Employee(CommonAModel):
qualification = models.CharField('学历', max_length=50, null=True, blank=True) qualification = models.CharField('学历', max_length=50, null=True, blank=True)
job_state = models.IntegerField('在职状态', choices=jobstate_choices, default=1) job_state = models.IntegerField('在职状态', choices=jobstate_choices, default=1)
face_data = models.JSONField('人脸识别数据', null=True, blank=True) face_data = models.JSONField('人脸识别数据', null=True, blank=True)
is_atwork = models.BooleanField('当前在岗', default=False)
last_check_time = models.DateTimeField('打卡时间', null=True, blank=True)
not_work_remark = models.CharField('当前未打卡说明', null=True, blank=True, max_length=200)
class Meta: class Meta:
verbose_name = '员工补充信息' verbose_name = '员工补充信息'
verbose_name_plural = verbose_name verbose_name_plural = verbose_name
@ -37,6 +40,16 @@ class Employee(CommonAModel):
def __str__(self): def __str__(self):
return self.name return self.name
class NotWorkRemark(CommonAModel):
"""
离岗说明
"""
year = models.PositiveSmallIntegerField('', default=2022)
month = models.PositiveSmallIntegerField('', default=2)
day = models.PositiveSmallIntegerField('', default=1)
user = models.ForeignKey(User, verbose_name='用户', on_delete=models.CASCADE)
remark = models.CharField('未打卡说明', null=True, blank=True, max_length=200)
class ClockRecord(CommonADModel): class ClockRecord(CommonADModel):
""" """
打卡记录 打卡记录

View File

@ -10,8 +10,6 @@ from django.db.models.query import Prefetch
class EmployeeSerializer(DynamicFieldsSerializerMixin, ModelSerializer): class EmployeeSerializer(DynamicFieldsSerializerMixin, ModelSerializer):
name = serializers.CharField(source='user.name', read_only=True) name = serializers.CharField(source='user.name', read_only=True)
dept_ = OrganizationSimpleSerializer(source='user.dept', read_only=True) dept_ = OrganizationSimpleSerializer(source='user.dept', read_only=True)
is_atwork = serializers.BooleanField(source='user.is_atwork', read_only=True)
last_check_time = serializers.DateTimeField(source='user.last_check_time', read_only=True)
class Meta: class Meta:
model = Employee model = Employee
exclude = ['face_data'] exclude = ['face_data']

View File

@ -2,16 +2,15 @@ from __future__ import absolute_import, unicode_literals
from celery import shared_task from celery import shared_task
from apps.hrm.models import Employee from apps.hrm.models import Employee
from apps.system.models import User
from django.core.cache import cache from django.core.cache import cache
@shared_task @shared_task
def update_all_user_not_atwork(): def update_all_employee_not_atwork():
""" """
将所有员工设为非在岗状态 将所有员工设为非在岗状态
""" """
User.objects.all().update(is_atwork=False, last_check_time = None) Employee.objects.all().update(is_atwork=False, last_check_time = None)
@shared_task @shared_task
def update_all_user_facedata_cache(): def update_all_user_facedata_cache():

View File

@ -92,9 +92,7 @@ class ClockRecordViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
ins.update_time = now ins.update_time = now
ins.save() ins.save()
# 设为在岗 # 设为在岗
user.is_atwork = True Employee.objects.filter(user=user).update(is_atwork=True, last_check_time=now)
user.last_check_time = now
user.save()
return Response(UserSimpleSerializer(instance=user).data) return Response(UserSimpleSerializer(instance=user).data)
return Response(msg, status=status.HTTP_400_BAD_REQUEST) return Response(msg, status=status.HTTP_400_BAD_REQUEST)
return Response('非打卡时间范围', status=status.HTTP_400_BAD_REQUEST) return Response('非打卡时间范围', status=status.HTTP_400_BAD_REQUEST)
@ -140,9 +138,7 @@ class FaceLogin(CreateAPIView):
}) })
# 设为在岗 # 设为在岗
if created: if created:
user.is_atwork = True Employee.objects.filter(user=user).update(is_atwork=True, last_check_time=now)
user.last_check_time = now
user.save()
return Response({ return Response({
'refresh': str(refresh), 'refresh': str(refresh),

View File

@ -186,15 +186,31 @@ class RecordFormSerializer(serializers.ModelSerializer):
return queryset return queryset
class RecordFormCreateSerializer(serializers.ModelSerializer): class RecordFormCreateSerializer(serializers.ModelSerializer):
form = serializers.PrimaryKeyRelatedField(
queryset=RecordForm.objects.all(), label="复制表ID")
class Meta: class Meta:
model = RecordForm model = RecordForm
fields = ['name', 'type', 'step', 'material', 'number', 'enabled'] fields = ['name', 'type', 'step', 'material', 'number', 'enabled', 'form']
# def validate(self, attrs):
# if attrs['enabled']:
# if RecordForm.objects.filter(type=attrs['type'],
# enabled=True).exists():
# raise ValidationError('已存在启用的同类检查表')
# return super().validate(attrs)
class RecordFormUpdateSerializer(serializers.ModelSerializer): class RecordFormUpdateSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = RecordForm model = RecordForm
fields = ['name', 'type', 'number', 'enabled'] fields = ['name', 'type', 'number', 'enabled']
# def validate(self, attrs):
# if attrs['enabled']:
# if RecordForm.objects.filter(type=attrs['type'],
# enabled=True).exists():
# raise ValidationError('已存在启用的同类检查表')
# return super().validate(attrs)
class RecordFormFieldSerializer(serializers.ModelSerializer): class RecordFormFieldSerializer(serializers.ModelSerializer):
class Meta: class Meta:

View File

@ -10,7 +10,7 @@ from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
from utils.pagination import PageOrNot from utils.pagination import PageOrNot
from rest_framework.exceptions import APIException from rest_framework.exceptions import APIException
from django.db import transaction
# Create your views here. # Create your views here.
class MaterialViewSet(PageOrNot, CreateUpdateModelAMixin, ModelViewSet): class MaterialViewSet(PageOrNot, CreateUpdateModelAMixin, ModelViewSet):
@ -208,6 +208,22 @@ class RecordFormFieldViewSet(OptimizationMixin, CreateUpdateModelAMixin, ModelVi
return RecordFormFieldUpdateSerializer return RecordFormFieldUpdateSerializer
return RecordFormFieldSerializer return RecordFormFieldSerializer
@transaction.atomic
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
vdata = serializer.validated_data
form = vdata.pop('form', None)
instance = RecordForm(**vdata)
instance.save(create_by=request.user)
if form:
for i in RecordFormField.objects.filter(form=form, is_deleted=False):
i.pk = None
i.form = instance
i.parent = None
i.save()
return super().create(request, *args, **kwargs)
class TechDocViewSet(OptimizationMixin, CreateUpdateModelAMixin, ModelViewSet): class TechDocViewSet(OptimizationMixin, CreateUpdateModelAMixin, ModelViewSet):
""" """
技术文件增删改查 技术文件增删改查

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.9 on 2022-02-17 07:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pm', '0023_alter_productionplan_order'),
]
operations = [
migrations.AddField(
model_name='productionplan',
name='old_state',
field=models.PositiveIntegerField(blank=True, choices=[(10, '制定中'), (20, '已下达'), (30, '已接收'), (40, '生产中'), (50, '生产完成'), (60, '军检完成'), (70, '暂停'), (80, '终止')], null=True, verbose_name='原状态'),
),
migrations.AlterField(
model_name='productionplan',
name='state',
field=models.PositiveIntegerField(choices=[(10, '制定中'), (20, '已下达'), (30, '已接收'), (40, '生产中'), (50, '生产完成'), (60, '军检完成'), (70, '暂停'), (80, '终止')], default=10, verbose_name='状态'),
),
]

View File

@ -20,13 +20,17 @@ class ProductionPlan(CommonAModel):
PLAN_STATE_WORKING = 40 PLAN_STATE_WORKING = 40
PLAN_STATE_DONE = 50 PLAN_STATE_DONE = 50
PLAN_MTEST_DONE = 60 PLAN_MTEST_DONE = 60
PLAN_STATE_PAUSE = 70
PLAN_STATE_STOP = 80
state_choices=( state_choices=(
(PLAN_STATE_PLANING, '制定中'), (PLAN_STATE_PLANING, '制定中'),
(PLAN_STATE_ASSGINED, '已下达'), (PLAN_STATE_ASSGINED, '已下达'),
(PLAN_STATE_ACCEPTED, '已接收'), (PLAN_STATE_ACCEPTED, '已接收'),
(PLAN_STATE_WORKING, '生产中'), (PLAN_STATE_WORKING, '生产中'),
(PLAN_STATE_DONE, '生产完成'), (PLAN_STATE_DONE, '生产完成'),
(PLAN_MTEST_DONE, '军检完成') (PLAN_MTEST_DONE, '军检完成'),
(PLAN_STATE_PAUSE, '暂停'),
(PLAN_STATE_STOP, '终止')
) )
number = models.CharField('编号', max_length=50, unique=True) number = models.CharField('编号', max_length=50, unique=True)
order = models.ForeignKey(Order, verbose_name='关联订单', null=True, blank=True, on_delete=models.SET_NULL, related_name='plan_order') order = models.ForeignKey(Order, verbose_name='关联订单', null=True, blank=True, on_delete=models.SET_NULL, related_name='plan_order')
@ -41,6 +45,7 @@ class ProductionPlan(CommonAModel):
end_date = models.DateField('计划完工日期') end_date = models.DateField('计划完工日期')
process_json = models.JSONField('按工序的统计数', default=dict, null=True, blank=True) process_json = models.JSONField('按工序的统计数', default=dict, null=True, blank=True)
is_planed = models.BooleanField('是否已排产', default=False) is_planed = models.BooleanField('是否已排产', default=False)
old_state = models.PositiveIntegerField('原状态', choices=state_choices, null=True, blank=True)
class Meta: class Meta:
verbose_name = '生产计划' verbose_name = '生产计划'
verbose_name_plural = verbose_name verbose_name_plural = verbose_name
@ -62,7 +67,7 @@ class SubProductionPlan(CommonAModel):
(SUBPLAN_STATE_ASSGINED, '已下达'), (SUBPLAN_STATE_ASSGINED, '已下达'),
(SUBPLAN_STATE_ACCEPTED, '已接收'), (SUBPLAN_STATE_ACCEPTED, '已接收'),
(SUBPLAN_STATE_WORKING, '生产中'), (SUBPLAN_STATE_WORKING, '生产中'),
(SUBPLAN_STATE_DONE, '已完成') (SUBPLAN_STATE_DONE, '已完成'),
) )
number = models.CharField('子计划编号', max_length=50, unique=True, null=True, blank=True) number = models.CharField('子计划编号', max_length=50, unique=True, null=True, blank=True)
production_plan = models.ForeignKey(ProductionPlan, verbose_name='关联主生产计划', on_delete=models.CASCADE, related_name='subplan_plan') production_plan = models.ForeignKey(ProductionPlan, verbose_name='关联主生产计划', on_delete=models.CASCADE, related_name='subplan_plan')
@ -91,15 +96,15 @@ class SubProductionPlan(CommonAModel):
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

@ -16,7 +16,7 @@ from apps.pm.models import ProductionPlan, SubProductionProgress, SubProductionP
from rest_framework.viewsets import GenericViewSet, ModelViewSet from rest_framework.viewsets import GenericViewSet, ModelViewSet
from django.shortcuts import render from django.shortcuts import render
from apps.sam.models import Order from apps.sam.models import Order
from rest_framework.exceptions import APIException from rest_framework.exceptions import APIException, ParseError
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.decorators import action from rest_framework.decorators import action
from django.db.models import F from django.db.models import F
@ -86,6 +86,8 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel
production_plan=self.get_object() production_plan=self.get_object()
if production_plan.is_planed: if production_plan.is_planed:
raise APIException('已生成子计划') raise APIException('已生成子计划')
if production_plan.state != ProductionPlan.PLAN_STATE_PLANING:
raise APIException('不可操作')
subps = SubProduction.objects.filter(product=production_plan.product).order_by('process__number') subps = SubProduction.objects.filter(product=production_plan.product).order_by('process__number')
for index, i in enumerate(subps): for index, i in enumerate(subps):
steps = Step.objects.filter(usedstep__subproduction=i, usedstep__subproduction__is_deleted=False, steps = Step.objects.filter(usedstep__subproduction=i, usedstep__subproduction__is_deleted=False,
@ -101,9 +103,42 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel
is_main=m.is_main, is_main=m.is_main,
count=m.count*production_plan.count, subproduction_plan=instance) count=m.count*production_plan.count, subproduction_plan=instance)
production_plan.is_planed=True production_plan.is_planed=True
production_plan.state = ProductionPlan.PLAN_STATE_PLANING
production_plan.save() production_plan.save()
return Response() return Response()
@action(methods=['put'], detail=True, perms_map={'post':'plan_toggle'}, serializer_class=serializers.Serializer)
@transaction.atomic
def toggle(self, request, pk=None):
"""
计划暂停或启动
"""
plan = self.get_object()
if plan.state == ProductionPlan.PLAN_STATE_PAUSE:
plan.state = plan.old_state
plan.old_state = None
plan.save()
return Response()
elif plan.state <= ProductionPlan.PLAN_STATE_WORKING:
plan.old_state = plan.state
plan.state = ProductionPlan.PLAN_STATE_PAUSE
plan.save()
return Response()
raise APIException('不可操作')
@action(methods=['put'], detail=True, perms_map={'post':'plan_stop'}, serializer_class=serializers.Serializer)
@transaction.atomic
def stop(self, request, pk=None):
"""
计划终止
"""
plan = self.get_object()
if plan.state == ProductionPlan.PLAN_STATE_PAUSE:
plan.state = ProductionPlan.PLAN_STATE_STOP
plan.save()
return Response()
raise APIException('不可操作')
class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateModelMixin, GenericViewSet): class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateModelMixin, GenericViewSet):
""" """
@ -166,6 +201,7 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo
plan.save() plan.save()
return Response() return Response()
raise APIException('计划状态有误') raise APIException('计划状态有误')
@action(methods=['get'], detail=True, perms_map={'get':'*'}, serializer_class=serializers.Serializer) @action(methods=['get'], detail=True, perms_map={'get':'*'}, serializer_class=serializers.Serializer)
def pick_need_(self, request, pk=None): def pick_need_(self, request, pk=None):

View File

@ -7,4 +7,4 @@ class UserFilter(DynamicFieldsFilterMixin, filters.FilterSet):
name = filters.CharFilter(field_name='name', lookup_expr='contains') name = filters.CharFilter(field_name='name', lookup_expr='contains')
class Meta: class Meta:
model = User model = User
fields = ['name', 'is_active', 'is_atwork'] fields = ['name', 'is_active']

View File

@ -1,18 +0,0 @@
# Generated by Django 3.2.9 on 2022-01-21 05:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('system', '0003_auto_20210812_0909'),
]
operations = [
migrations.AddField(
model_name='user',
name='is_atwork',
field=models.BooleanField(default=False, verbose_name='当前在岗'),
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 3.2.9 on 2022-01-25 08:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('system', '0004_user_is_atwork'),
]
operations = [
migrations.AddField(
model_name='user',
name='last_check_time',
field=models.DateTimeField(blank=True, null=True, verbose_name='打卡时间'),
),
]

View File

@ -116,8 +116,6 @@ class User(AbstractUser):
superior = models.ForeignKey( superior = models.ForeignKey(
'self', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='上级主管') 'self', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='上级主管')
roles = models.ManyToManyField(Role, blank=True, verbose_name='角色') roles = models.ManyToManyField(Role, blank=True, verbose_name='角色')
is_atwork = models.BooleanField('当前在岗', default=False)
last_check_time = models.DateTimeField('打卡时间', null=True, blank=True)
class Meta: class Meta:
verbose_name = '用户信息' verbose_name = '用户信息'

View File

@ -144,7 +144,7 @@ class UserListSerializer(DynamicFieldsSerializerMixin, serializers.ModelSerializ
fields = ['id', 'name', 'phone', 'email', 'position', fields = ['id', 'name', 'phone', 'email', 'position',
'username', 'is_active', 'date_joined', 'username', 'is_active', 'date_joined',
'dept_', 'dept', 'roles', 'avatar', 'dept_', 'dept', 'roles', 'avatar',
'roles_', 'is_atwork', 'last_check_time'] 'roles_']
@staticmethod @staticmethod
def setup_eager_loading(queryset): def setup_eager_loading(queryset):

View File

@ -6,7 +6,7 @@ from apps.inm.models import FIFO, FIFOItem, FIFOItemProduct
from apps.inm.services import InmService from apps.inm.services import InmService
from apps.mtm.models import Material, RecordForm, RecordFormField, Step, SubprodctionMaterial, TechDoc from apps.mtm.models import Material, RecordForm, RecordFormField, Step, SubprodctionMaterial, TechDoc
from apps.mtm.serializers import RecordFormDetailSerializer, SubprodctionMaterialListSerializer, TechDocListSerializer from apps.mtm.serializers import RecordFormDetailSerializer, SubprodctionMaterialListSerializer, TechDocListSerializer
from apps.pm.models import SubProductionPlan, SubProductionProgress from apps.pm.models import ProductionPlan, SubProductionPlan, SubProductionProgress
from apps.pm.serializers import SubProductionPlanListSerializer, SubProductionProgressSerializer from apps.pm.serializers import SubProductionPlanListSerializer, SubProductionProgressSerializer
from apps.qm.models import TestRecord, TestRecordItem from apps.qm.models import TestRecord, TestRecordItem
from apps.qm.serializers import TestRecordDetailSerializer from apps.qm.serializers import TestRecordDetailSerializer
@ -35,7 +35,7 @@ from rest_framework import exceptions, serializers
from apps.wpm.services import WpmService from apps.wpm.services import WpmService
from django.utils import timezone from django.utils import timezone
from rest_framework import status from rest_framework import status
from django.db.models import Count from django.db.models import Count, Q
from utils.tools import ranstr from utils.tools import ranstr
@ -49,7 +49,11 @@ class WPlanViewSet(ListModelMixin, GenericViewSet):
""" """
perms_map = {'get': '*'} perms_map = {'get': '*'}
queryset = SubProductionPlan.objects.select_related( queryset = SubProductionPlan.objects.select_related(
'process', 'workshop', 'subproduction', 'product').exclude(state=0) 'process', 'workshop', 'subproduction', 'product').filter(
production_plan__state__in =[
ProductionPlan.PLAN_STATE_WORKING, ProductionPlan.PLAN_STATE_ASSGINED
]
)
search_fields = [] search_fields = []
serializer_class = SubProductionPlanListSerializer serializer_class = SubProductionPlanListSerializer
filterset_fields = ['production_plan', filterset_fields = ['production_plan',
@ -57,6 +61,7 @@ class WPlanViewSet(ListModelMixin, GenericViewSet):
ordering_fields = [] ordering_fields = []
ordering = ['-update_time'] ordering = ['-update_time']
@action(methods=['post', 'get'], detail=True, perms_map={'post': 'pick_half', 'get': '*'}, serializer_class=PickHalfsSerializer) @action(methods=['post', 'get'], detail=True, perms_map={'post': 'pick_half', 'get': '*'}, serializer_class=PickHalfsSerializer)
@transaction.atomic @transaction.atomic
def pick_half(self, request, pk=None): def pick_half(self, request, pk=None):
@ -116,6 +121,10 @@ class WPlanViewSet(ListModelMixin, GenericViewSet):
pw.save() pw.save()
sp.is_picked = True sp.is_picked = True
sp.save() sp.save()
if sp.production_plan.state in \
[ProductionPlan.PLAN_STATE_ASSGINED, ProductionPlan.PLAN_STATE_ACCEPTED]:
sp.production_plan.state = ProductionPlan.PLAN_STATE_WORKING
sp.production_plan.save()
return Response() return Response()

View File

@ -59,8 +59,9 @@ class FitJSONRenderer(JSONRenderer):
if isinstance(data, dict): if isinstance(data, dict):
prefix = list(data.keys())[0] prefix = list(data.keys())[0]
data = data[prefix] data = data[prefix]
elif isinstance(data, list): if isinstance(data, list):
data = data[0] data = data[0]
response_body.msg = prefix + ":" + str(data) # 取一部分放入msg,方便前端alert response_body.msg = prefix + ":" + str(data) # 取一部分放入msg,方便前端alert
else: else:
response_body.data = data response_body.data = data