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

This commit is contained in:
shilixia 2022-01-26 15:59:59 +08:00
commit 2c937c983f
41 changed files with 654 additions and 406 deletions

View File

@ -63,4 +63,7 @@ export default {
.overFlowShow .el-tabs__content{ .overFlowShow .el-tabs__content{
overflow: visible; overflow: visible;
} }
#warningTabs .el-tabs__item{
padding: 0 10px!important;
}
</style> </style>

View File

@ -19,14 +19,12 @@
@expand-change="handlerExpand" @expand-change="handlerExpand"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
> >
<el-table-column label="任务编号" prop="name" width="140" show-overflow-tooltip> <el-table-column label="任务编号" prop="name" min-width="140" show-overflow-tooltip>
</el-table-column> </el-table-column>
<el-table-column label="产品名称" prop="productName" width="120" show-overflow-tooltip> <el-table-column label="产品名称" prop="productName" min-width="120" show-overflow-tooltip>
</el-table-column> </el-table-column>
<el-table-column label="产品型号" prop="productNum"> <el-table-column label="产品型号" prop="productNum">
</el-table-column> </el-table-column>
<el-table-column label="生产数量" prop="per">
</el-table-column>
</el-table> </el-table>
</div> </div>
</template> </template>

View File

@ -255,11 +255,11 @@
<span>{{ currentProjectMsg.allTime }}</span> <span>{{ currentProjectMsg.allTime }}</span>
</div> </div>
<div class="lineMsg" v-if="currentProjectMsg.per1"> <div class="lineMsg" v-if="currentProjectMsg.per1">
<span class="title">当前进度</span> <span class="title">订单计划</span>
<span>{{ currentProjectMsg.per }}</span> <span>{{ currentProjectMsg.per }}</span>
</div> </div>
<div class="lineMsg" v-if="currentProjectMsg.per1"> <div class="lineMsg" v-if="currentProjectMsg.per1">
<span class="title">合格数量</span> <span class="title">当前进度</span>
<span>{{ currentProjectMsg.per1 }}</span> <span>{{ currentProjectMsg.per1 }}</span>
</div> </div>
<div class="lineMsg"> <div class="lineMsg">
@ -1389,8 +1389,9 @@
.searchWrap{ .searchWrap{
position: absolute; position: absolute;
z-index: 2000; z-index: 2000;
top: -38px; top: -35px;
display: flex right: 10px;
display: flex;
} }
#searchWrap .el-range-editor--medium.el-input__inner{ #searchWrap .el-range-editor--medium.el-input__inner{
height: 30px!important; height: 30px!important;

View File

@ -86,22 +86,11 @@
</div> </div>
</div> </div>
<div id="dashboardMiddle" class="dashboardMiddle"> <div id="dashboardMiddle" class="dashboardMiddle">
<el-row> <div class="dashboardCardPadding">
<el-col :span="15"> <div style="width: 65%;float: left;">
<div class="CardTitleWrap"> <div class="CardTitleWrap">
<span class="verticalLine"></span><span class="dashboardCardTitle">任务进度</span> <span class="verticalLine"></span><span class="dashboardCardTitle">任务进度</span>
</div> </div>
</el-col>
<el-col :span="1" style="height: 1px"></el-col>
<el-col :span="8">
<div class="CardTitleWrap">
<span class="verticalLine"></span><span class="dashboardCardTitle">成品率</span>
</div>
</el-col>
</el-row>
<el-row class="dashboardCardPadding">
<el-col :span="15">
<div style="height: 40px;line-height: 40px;"></div>
<div> <div>
<gantt <gantt
v-if="proList.length>0" v-if="proList.length>0"
@ -110,45 +99,37 @@
:ganttHeight="ganttHeight" :ganttHeight="ganttHeight"
></gantt> ></gantt>
</div> </div>
</el-col> </div>
<el-col :span="1" style="height: 1px"></el-col> <div style="width: 34%;position:relative;float: right" >
<el-col :span="8"> <div class="CardTitleWrap">
<div class="dashboardCardHand"> <span class="verticalLine"></span><span class="dashboardCardTitle">成品率</span>
<div class="dashboardCardFilter"> </div>
<el-date-picker <div class="dashboardCardHand" style="position: absolute;top: 0;right: 10px;">
v-model="chartDate" <el-date-picker
type="monthrange" v-model="chartDate"
start-placeholder="开始日期" type="monthrange"
end-placeholder="结束日期" start-placeholder="开始日期"
range-separator="" end-placeholder="结束日期"
format="yyyy 年 MM 月" range-separator=""
value-format="yyyy-MM" format="yyyy 年 MM 月"
@change="searchTimeChange('1')" value-format="yyyy-MM"
> @change="searchTimeChange('1')"
</el-date-picker> >
<div class="convenientWrap"> </el-date-picker>
<div class="convenientBtn" :class="{activeIndex:chartIndex==='1'}" @click="convenientClick('1','week')">
本周
</div>
<div class="convenientBtn" :class="{activeIndex:chartIndex==='2'}"
@click="convenientClick('1','month')">本月
</div>
<div class="convenientBtn" :class="{activeIndex:chartIndex==='3'}"
@click="convenientClick('1','quarter')">三个月
</div>
</div>
</div>
</div> </div>
<div id="chartColumn" style="width:100%;" :style="{height:ganttHeight+'px'}"></div> <div id="chartColumn" style="width:100%;" :style="{height:ganttHeight+'px'}"></div>
</el-col> </div>
</el-row> </div>
</div> </div>
<el-row :gutter="5"> <el-row :gutter="5">
<el-col :span="8"> <el-col :span="8">
<div class="dashboardBottomRow"> <div class="dashboardBottomRow">
<div class="dashboardCardHand"> <div class="dashboardCardHand">
<div class="CardTitleWrap"> <div class="CardTitleWrap" style="border-bottom: 0">
<span class="verticalLine"></span><span class="dashboardCardTitle">生产设备</span> <span class="verticalLine"></span><span class="dashboardCardTitle">生产设备</span>
<span @click="refreshBottomTabel('1')">
<el-icon class="el-icon-refresh refreshIcon"></el-icon>
</span>
</div> </div>
<div class="block"> <div class="block">
<el-pagination <el-pagination
@ -183,7 +164,7 @@
<el-table-column label="设备状态" prop="material_"> <el-table-column label="设备状态" prop="material_">
<!--type=1生产设备--> <!--type=1生产设备-->
<template slot-scope="scope"> <template slot-scope="scope">
<div v-if="scope.row.type===1"> <div v-if="scope.row.type===2">
<el-tag v-if="scope.row.state===40" type="danger"> <el-tag v-if="scope.row.state===40" type="danger">
禁用 禁用
</el-tag> </el-tag>
@ -207,14 +188,14 @@
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="下次校准日期" prop="model"> <!-- <el-table-column label="下次校准日期" prop="model">
<template slot-scope="scope"> <template slot-scope="scope">
<el-tag v-if="scope.row.next_check_date" :type="setClass(scope.row.next_check_date)"> <el-tag v-if="scope.row.next_check_date" :type="setClass(scope.row.next_check_date)">
{{scope.row.next_check_date}} {{scope.row.next_check_date}}
</el-tag> </el-tag>
<div v-else></div> <div v-else></div>
</template> </template>
</el-table-column> </el-table-column>-->
</el-table> </el-table>
</div> </div>
</div> </div>
@ -222,8 +203,11 @@
<el-col :span="8"> <el-col :span="8">
<div class="dashboardBottomRow"> <div class="dashboardBottomRow">
<div class="dashboardCardHand"> <div class="dashboardCardHand">
<div class="CardTitleWrap"> <div class="CardTitleWrap" style="border-bottom: 0">
<span class="verticalLine"></span><span class="dashboardCardTitle">人员到岗</span> <span class="verticalLine"></span><span class="dashboardCardTitle">人员到岗</span>
<span @click="refreshBottomTabel('2')">
<el-icon class="el-icon-refresh refreshIcon"></el-icon>
</span>
</div> </div>
<div class="block"> <div class="block">
<el-pagination <el-pagination
@ -274,8 +258,11 @@
<el-col :span="8"> <el-col :span="8">
<div class="dashboardBottomRow"> <div class="dashboardBottomRow">
<div class="dashboardCardHand"> <div class="dashboardCardHand">
<div class="CardTitleWrap"> <div class="CardTitleWrap" style="border-bottom: 0">
<span class="verticalLine"></span><span class="dashboardCardTitle">提醒</span> <span class="verticalLine"></span><span class="dashboardCardTitle">提醒</span>
<span @click="refreshBottomTabel('3')" style="cursor: pointer">
<el-icon class="el-icon-refresh refreshIcon"></el-icon>
</span>
</div> </div>
<div class="block"> <div class="block">
<el-pagination <el-pagination
@ -293,13 +280,13 @@
</div> </div>
<div class="dashboardCardPadding"> <div class="dashboardCardPadding">
<el-tabs type="card" :style="{height:cardTabelHeight+'px'}" v-model="activeName" <el-tabs type="card" :style="{height:cardTabelHeight+'px'}" v-model="activeName"
@tab-click="activeNameClick"> @tab-click="activeNameClick" id="warningTabs">
<el-tab-pane label="库存警告" name="库存警告"> <el-tab-pane label="库存警告" name="库存警告">
<ul :style="{height:cardTabelHeight-47+'px'}" class="lists" :class="{anim:animate}" @mouseenter="Stop()" <ul :style="{height:cardTabelHeight-47+'px'}" class="lists" :class="{anim:animate}" @mouseenter="Stop()"
@mouseleave="Up()"> @mouseleave="Up()">
<li class="listItem" v-for="item in warningList" :key="item.id"> <li class="listItem" v-for="(item,$index) in warningList" :key="$index">
<div class="itemText"> <div class="itemText">
<span>{{item.name}}({{item.unit}})</span><span>剩余{{item.count}},低于安全库存{{item.count_safe}}</span> <span>{{item.name}}({{item.unit}})</span><span>低于安全库存</span>
</div> </div>
</li> </li>
</ul> </ul>
@ -307,9 +294,9 @@
<el-tab-pane label="临近交货" name="临近交货"> <el-tab-pane label="临近交货" name="临近交货">
<ul :style="{height:cardTabelHeight-47+'px'}" class="lists" :class="{anim:animate}" @mouseenter="Stop()" <ul :style="{height:cardTabelHeight-47+'px'}" class="lists" :class="{anim:animate}" @mouseenter="Stop()"
@mouseleave="Up()"> @mouseleave="Up()">
<li class="listItem" v-for="item in warningList" :key="item.id"> <li class="listItem" v-for="(item,$index) in warningList" :key="$index">
<div class="itemText"> <div class="itemText">
<span>{{item.name}}({{item.number}})</span><span>{{item.delivery_date}}交货日期</span> <span>{{item.name}}({{item.number}})</span><span>{{item.delivery_date}}交货日期</span>
</div> </div>
</li> </li>
</ul> </ul>
@ -317,13 +304,39 @@
<el-tab-pane label="过期提醒" name="过期提醒"> <el-tab-pane label="过期提醒" name="过期提醒">
<ul :style="{height:cardTabelHeight-47+'px'}" class="lists" :class="{anim:animate}" @mouseenter="Stop()" <ul :style="{height:cardTabelHeight-47+'px'}" class="lists" :class="{anim:animate}" @mouseenter="Stop()"
@mouseleave="Up()"> @mouseleave="Up()">
<li class="listItem" v-for="item in warningList" :key="item.id"> <li class="listItem" v-for="(item,$index) in warningList" :key="$index">
<div class="itemText"> <div class="itemText">
<span>{{item.name}}({{item.number}})</span><span>{{item.delivery_date}}到期</span> <span>{{item.name}}({{item.number}})</span><span>{{item.delivery_date}}到期</span>
</div> </div>
</li> </li>
</ul> </ul>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="设备检测" name="设备检测">
<ul :style="{height:cardTabelHeight-47+'px'}" class="lists" :class="{anim:animate}" @mouseenter="Stop()"
@mouseleave="Up()">
<li class="listItem" v-for="(item,$index) in warningList" :key="$index"
:class="{nearStatus:item.warningType===1,outStatus:item.warningType===2}">
<div class="itemText">
<span>{{item.name}}({{item.number}})</span>
<span v-if="item.warningType===1">接近校准日期</span>
<span v-else>已过校准日期</span>
</div>
</li>
</ul>
</el-tab-pane>
<el-tab-pane label="任务到期" name="任务到期">
<ul :style="{height:cardTabelHeight-47+'px'}" class="lists" :class="{anim:animate}" @mouseenter="Stop()"
@mouseleave="Up()">
<li class="listItem" v-for="(item,$index) in warningList" :key="$index"
:class="{nearStatus:item.warningType===1,outStatus:item.warningType===2}">
<div class="itemText">
<span>{{item.name}}({{item.number}})</span>
<span v-if="item.warningType===1">接近计划日期</span>
<span v-else>已过计划日期</span>
</div>
</li>
</ul>
</el-tab-pane>
</el-tabs> </el-tabs>
</div> </div>
</div> </div>
@ -331,7 +344,6 @@
</el-row> </el-row>
</div> </div>
</template> </template>
<script> <script>
import echarts from 'echarts' import echarts from 'echarts'
import {mapGetters} from 'vuex'; import {mapGetters} from 'vuex';
@ -544,11 +556,9 @@
this.drawChart(); this.drawChart();
} }
}); });
//获取库存警告 //获取库存警告
getMaterialList({page: 1, page_size:that.warningPageSize,tag: 'low_inm'}).then((response) => { getMaterialList({page: 1, page_size:that.warningPageSize,tag: 'low_inm'}).then((response) => {
if (response.data) { if (response.data) {
debugger;
that.warningList = response.data.results; that.warningList = response.data.results;
that.warningTotal = response.data.count; that.warningTotal = response.data.count;
} }
@ -558,6 +568,7 @@
getEquipmentList() { getEquipmentList() {
let that = this; let that = this;
this.listLoadingEm = true; this.listLoadingEm = true;
that.equipmentPage = 1;
getpEquipmentList({page: that.equipmentPage, page_size: that.equipmentPageSize}).then((response) => { getpEquipmentList({page: that.equipmentPage, page_size: that.equipmentPageSize}).then((response) => {
if (response.data) { if (response.data) {
that.equipmentList = response.data.results; that.equipmentList = response.data.results;
@ -583,6 +594,7 @@
getUserList() { getUserList() {
let that = this; let that = this;
that.listLoadingUser = true; that.listLoadingUser = true;
that.userPage =1;
getUserList({page: that.userPage, page_size: that.userPageSize, fields: 'id,name,dept_name,is_atwork'}).then((response) => { getUserList({page: that.userPage, page_size: that.userPageSize, fields: 'id,name,dept_name,is_atwork'}).then((response) => {
if (response.data) { if (response.data) {
that.userList = response.data.results; that.userList = response.data.results;
@ -633,40 +645,6 @@
this.$router.push({name: 'ticket', params: {}}) this.$router.push({name: 'ticket', params: {}})
} }
}, },
//任务排期列表
/*getPlanList() {
let that = this;
this.listLoadingPlan = true;
getProductionplanList({page: 0}).then((response) => {
if (response.data) {
that.planList = response.data;
}
this.listLoadingPlan = false;
});
},*/
//库存列表
/*getStockList() {
let that = this;
this.listLoadingStock = true;
getInventoryList({page: this.stockPage, page_size: this.stockPageSize}).then((response) => {
if (response.data) {
that.stockList = response.data.results;
that.stockTotal = response.data.count;
}
this.listLoadingStock = false;
});
},
//更多库存
stockMore() {
this.$router.push({name: 'warehouse', params: {}})
},
//库存pageSize改变
// @size-change="handleStockSizeChange"
handleEquipmentSizeChange(val) {
this.stockPageSize = val;
this.stockPage = 1;
},
*/
//图标渲染 //图标渲染
drawChart() { drawChart() {
let that = this; let that = this;
@ -794,60 +772,6 @@
this.$router.push({name: 'unproduct'}) this.$router.push({name: 'unproduct'})
} }
}, },
//便捷查询按钮
convenientClick(index, type) {
let that = this;
let startTime = '', endTime = '', activeIndex = '1';
let dat = new Date();
let week = dat.getDay();//0-6
let currentTime = dat.getTime();
let currentYear = dat.getFullYear();
let currentMonth = dat.getMonth() + 1;
let currentDay = dat.getDate();
endTime = currentYear + '-' + currentMonth + '-' + currentDay;
if (type === 'week') {
activeIndex = '1';
let num = week === 0 ? 6 : week - 1;
let time = currentTime - num * 24 * 60 * 60 * 1000;
let start = new Date(time);
startTime = start.getFullYear() + '-' + (start.getMonth() + 1) + '-' + start.getDate();
} else if (type === 'month') {
activeIndex = '2';
startTime = currentYear + '-' + currentMonth + '-01';
} else if (type === 'quarter') {
activeIndex = '3';
let mon = null, yea = null;
if (currentMonth > 2) {
mon = currentMonth - 2;
yea = currentYear;
} else if (currentMonth === 2) {
mon = 12;
yea = currentYear - 1;
} else if (currentMonth === 1) {
mon = 11;
yea = currentYear - 1;
}
startTime = yea + '-' + mon + '-01';
}
if (index === '1') {
this.chartIndex = activeIndex;
getProcessYield({datetime_start: startTime, datetime_end: endTime}).then((response) => {
if (response.data) {
let list = response.data;
let xAxisData = [], seriesData = [];
list.forEach(item => {
xAxisData.push(item.name);
let rate = item.rate * 100;
seriesData.push(rate.toFixed(2))
});
that.chartData.xAxisData = xAxisData;
that.chartData.seriesData = seriesData;
this.drawChart();
}
});
}
//根据时间和类型获取数据
},
//选择月份 //选择月份
searchTimeChange(index) { searchTimeChange(index) {
let that = this; let that = this;
@ -878,33 +802,93 @@
} }
}, },
//提示 //提示
activeNameClick(tab) { activeNameClick() {
debugger;
debugger;
let that = this; let that = this;
that.warningPage = 1; that.warningPage = 1;
that.warningList = []; that.warningList = [];
if (tab.label === '库存警告') { if (that.activeName === '库存警告') {
getMaterialList({page: 1, page_size:that.warningPageSize, tag: 'low_inm'}).then((response) => { getMaterialList({page: 1, page_size:that.warningPageSize, tag: 'low_inm'}).then((response) => {
if (response.data) { if (response.data) {
that.warningList = response.data.results; that.warningList = response.data.results;
that.warningTotal = response.data.count; that.warningTotal = response.data.count;
} }
}); });
} else if (tab.label === '临近交货') { } else if (that.activeName === '临近交货') {
getOrderList({page: 1, page_size:that.warningPageSize,tag:'near_delivery'}).then((response) => { getOrderList({page: 1, page_size:that.warningPageSize,tag:'near_delivery'}).then((response) => {
if (response.data) { if (response.data) {
that.warningList = response.data.results; that.warningList = response.data.results;
that.warningTotal = response.data.count; that.warningTotal = response.data.count;
} }
}); });
} else if (tab.label === '过期提醒') { } else if (that.activeName === '过期提醒') {
getmaterialbatchList({page: 1, page_size:that.warningPageSize, tag: 'expired'}).then((response) => { getmaterialbatchList({page: 1, page_size:that.warningPageSize, tag: 'expired'}).then((response) => {
if (response.data) { if (response.data) {
that.warningList = response.data.results; that.warningList = response.data.results;
that.warningTotal = response.data.count; that.warningTotal = response.data.count;
} }
}); });
}else if (that.activeName === '设备检测') {
that.warningPageSize = 100;
let warningList = [];
getpEquipmentList({page: 0,tag:'near_done'}).then((response) => {
if (response.data) {
let results = response.data;
if(results.length>0){
results.forEach(item=>{
let obj = new Object();
obj = item;
obj.warningType = 1;
warningList.push(obj);
});
}
getpEquipmentList({page: 0,tag:'out_done'}).then((res) => {
if (response.data) {
let resData = res.data;
if(resData.length>0){
resData.forEach(item=>{
let obj1 = new Object();
obj1 = item;
obj1.warningType = 2;
warningList.push(obj1);
});
}
that.warningList = warningList;
that.warningTotal = warningList.length;
}
});
}
});
}else if (that.activeName === '任务到期') {
that.warningPageSize = 100;
let warningList = [];
getProductionplanList({page: 0,tag:'near_done'}).then((response) => {
if (response.data) {
let results = response.data;
if(results.length>0){
results.forEach(item=>{
let obj = new Object();
obj = item;
obj.warningType = 1;
warningList.push(obj);
});
}
getProductionplanList({page: 0,tag:'out_done'}).then((res) => {
if (response.data) {
let resData = res.data;
if(resData.length>0) {
resData.forEach(item => {
let obj1 = new Object();
obj1 = item;
obj1.warningType = 2;
warningList.push(obj1);
});
}
that.warningList = warningList;
that.warningTotal = warningList.length;
}
});
}
});
} }
}, },
handleWarningSizeChange(val) { handleWarningSizeChange(val) {
@ -912,7 +896,30 @@
this.warningPage = 1; this.warningPage = 1;
}, },
handleWarningCurrentChange(val) { handleWarningCurrentChange(val) {
console.log(`当前页: ${val}`); let that = this;
that.warningPage = val;
if (that.activeName === '库存警告') {
getMaterialList({page: val, page_size:that.warningPageSize, tag: 'low_inm'}).then((response) => {
if (response.data) {
that.warningList = response.data.results;
that.warningTotal = response.data.count;
}
});
} else if (that.activeName === '临近交货') {
getOrderList({page: val, page_size:that.warningPageSize,tag:'near_delivery'}).then((response) => {
if (response.data) {
that.warningList = response.data.results;
that.warningTotal = response.data.count;
}
});
} else if (that.activeName === '过期提醒') {
getmaterialbatchList({page: val, page_size:that.warningPageSize, tag: 'expired'}).then((response) => {
if (response.data) {
that.warningList = response.data.results;
that.warningTotal = response.data.count;
}
});
}
}, },
getGanttData() { getGanttData() {
let that = this; let that = this;
@ -986,13 +993,23 @@
} }
}) })
}, },
refreshBottomTabel(index){
if(index==='1') {//生产设备
this.getEquipmentList();//设备列表
}else if(index==='2'){//人员到岗
this.getUserList();//用户列表
}else{//提醒
this.activeNameClick();
}
},
}, },
mounted() { mounted() {
let hei = document.getElementsByClassName('app-main')[0].clientHeight; let hei = document.getElementsByClassName('app-main')[0].clientHeight;
let heig = document.getElementsByClassName('dashboardTopCard')[0].clientHeight; let heig = document.getElementsByClassName('dashboardTopCard')[0].clientHeight;
let domHeight = ((hei - heig - 140) / 2); let domHeight = ((hei - heig - 140) / 2);
this.cardTabelHeight = domHeight-35; this.cardTabelHeight = domHeight-37;
this.ganttHeight = domHeight - 10; this.ganttHeight = domHeight+35;
document.getElementById('chartColumn').style.height = domHeight + 'px'; document.getElementById('chartColumn').style.height = domHeight + 'px';
this.getUserList();//用户列表 this.getUserList();//用户列表
this.getEquipmentList();//设备列表 this.getEquipmentList();//设备列表
@ -1015,12 +1032,14 @@
border-radius: 5px; border-radius: 5px;
margin-bottom: 5px; margin-bottom: 5px;
background: #ffffff; background: #ffffff;
overflow: hidden;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1); box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
} }
.dashboardBottomRow { .dashboardBottomRow {
margin-bottom: 0; margin-bottom: 0;
} }
.dashboardCardPadding { .dashboardCardPadding {
overflow: hidden;
padding: 5px 20px 20px 20px; padding: 5px 20px 20px 20px;
} }
/**/ /**/
@ -1130,7 +1149,9 @@
/*成品率筛选条件*/ /*成品率筛选条件*/
.dashboardCardHand { .dashboardCardHand {
display: flex; display: flex;
padding-left: 1%;
justify-content: space-between; justify-content: space-between;
border-bottom: 1px solid #f5f5f5;
.dashboardCardFilter { .dashboardCardFilter {
display: flex; display: flex;
.convenientWrap { .convenientWrap {
@ -1161,6 +1182,12 @@
} }
} }
} }
.refreshIcon{
color: #409EFF;
font-weight: bold;
font-size: 18px;
cursor: pointer;
}
.anim { .anim {
transition: all 0.5s; transition: all 0.5s;
margin-top: -35px; //高度等于行高 margin-top: -35px; //高度等于行高
@ -1175,13 +1202,19 @@
.listItem { .listItem {
height: 40px; height: 40px;
line-height: 40px; line-height: 40px;
font-size: 16px; font-size: 12px;
.itemText { .itemText {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
} }
} }
.nearStatus{
color: #e6a23c;
}
.outStatus{
color: #f56c6c;
}
#chartColumn > div { #chartColumn > div {
height: 100% !important; height: 100% !important;
canvas { canvas {

View File

@ -2,7 +2,7 @@ from django.db.models import base
from rest_framework import urlpatterns from rest_framework import urlpatterns
from django.urls import path, include from django.urls import path, include
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from apps.develop.views import CleanDataView, UpdateCuttingView, UpdateEquipState, UpdateFIFOItem, UpdateLastTestResult, UpdateSpg from apps.develop.views import CleanDataView, UpdateCuttingView, UpdateEquipState, UpdateFIFOItem, UpdateLastTestResult, UpdateNeedToOrder, UpdateSpg
urlpatterns = [ urlpatterns = [
path('cleandata/', CleanDataView.as_view()), path('cleandata/', CleanDataView.as_view()),
@ -11,6 +11,7 @@ urlpatterns = [
path('update_last_result/', UpdateLastTestResult.as_view()), path('update_last_result/', UpdateLastTestResult.as_view()),
path('update_fifoitem/', UpdateFIFOItem.as_view()), path('update_fifoitem/', UpdateFIFOItem.as_view()),
path('update_spg/', UpdateSpg.as_view()), path('update_spg/', UpdateSpg.as_view()),
path('update_equip_state/', UpdateEquipState.as_view()) path('update_equip_state/', UpdateEquipState.as_view()),
path('update_need_to_order/', UpdateNeedToOrder.as_view())
] ]

View File

@ -100,3 +100,9 @@ class UpdateEquipState(APIView):
def post(self, request, format=None): def post(self, request, format=None):
update_equip_state_by_next_check_date() update_equip_state_by_next_check_date()
return Response() return Response()
class UpdateNeedToOrder(APIView):
permission_classes = [IsAdminUser]
def post(self, request):
WProduct.objects.exclude(to_order=None).update(need_to_order=True)
return Response()

View File

@ -0,0 +1,24 @@
from django_filters import rest_framework as filters
from apps.em.models import Equipment
from datetime import *
from utils.mixins import DynamicFieldsFilterMixin
class EquipFilterSet(DynamicFieldsFilterMixin, filters.FilterSet):
tag = filters.CharFilter(method='filter_tag')
class Meta:
model = Equipment
fields = ['keeper', 'type', 'tag']
def filter_tag(self, queryset, name, value):
now = datetime.now()
day7_after = now + timedelta(days=7)
if value == 'near_check':
queryset = queryset.filter(
next_check_date__lte = datetime.date(day7_after))
elif value == 'out_check':
queryset = queryset.filter(
next_check_date__gt = datetime.date(now))
return queryset

View File

@ -2,11 +2,13 @@ from apps.mtm.models import Step
from rest_framework import serializers from rest_framework import serializers
from rest_framework.serializers import ModelSerializer from rest_framework.serializers import ModelSerializer
from rest_framework import exceptions from rest_framework import exceptions
from utils.mixins import DynamicFieldsSerializerMixin
from .models import Equipment, ECheckRecord from .models import Equipment, ECheckRecord
from apps.system.serializers import OrganizationSimpleSerializer, UserSimpleSerializer from apps.system.serializers import OrganizationSimpleSerializer, UserSimpleSerializer
class EquipmentListSerializer(ModelSerializer): class EquipmentListSerializer(DynamicFieldsSerializerMixin, ModelSerializer):
keeper_ = UserSimpleSerializer(source='keeper', read_only=True) keeper_ = UserSimpleSerializer(source='keeper', read_only=True)
step_ = serializers.SerializerMethodField() step_ = serializers.SerializerMethodField()
class Meta: class Meta:
@ -37,7 +39,7 @@ class EquipmentCreateUpdateSerializer(ModelSerializer):
class EquipmentSimpleSerializer(ModelSerializer): class EquipmentSimpleSerializer(ModelSerializer):
class Meta: class Meta:
model = Equipment model = Equipment
fields = ['id', 'number', 'name', 'state', 'model'] fields = ['id', 'number', 'name', 'state', 'model', 'type']

View File

@ -7,6 +7,7 @@ from rest_framework.viewsets import ModelViewSet, GenericViewSet
from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin, ListModelMixin, DestroyModelMixin from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin, ListModelMixin, DestroyModelMixin
from rest_framework import serializers, status from rest_framework import serializers, status
from rest_framework.response import Response from rest_framework.response import Response
from apps.em.filters import EquipFilterSet
from apps.em.models import Equipment, ECheckRecord from apps.em.models import Equipment, ECheckRecord
from apps.em.serializers import DaqCreateSerializer, EChcekRecordCreateSerializer, ECheckRecordListSerializer, \ from apps.em.serializers import DaqCreateSerializer, EChcekRecordCreateSerializer, ECheckRecordListSerializer, \
EquipmentCreateUpdateSerializer, EquipmentListSerializer EquipmentCreateUpdateSerializer, EquipmentListSerializer
@ -26,7 +27,7 @@ class EquipmentViewSet(CreateUpdateModelAMixin, OptimizationMixin, ModelViewSet)
queryset = Equipment.objects.all() queryset = Equipment.objects.all()
serializer_class = EquipmentListSerializer serializer_class = EquipmentListSerializer
search_fields = ['number', 'name','description'] search_fields = ['number', 'name','description']
filterset_fields = ['keeper', 'type'] filterset_class = EquipFilterSet
ordering_fields = ['create_time'] ordering_fields = ['create_time']
ordering = ['-create_time'] ordering = ['-create_time']

View File

@ -1,5 +1,6 @@
from django_filters import rest_framework as filters from django_filters import rest_framework as filters
from apps.hrm.models import ClockRecord from apps.hrm.models import ClockRecord, Employee
from utils.mixins import DynamicFieldsFilterMixin
class ClockRecordFilterSet(filters.FilterSet): class ClockRecordFilterSet(filters.FilterSet):
create_time_start = filters.DateFilter(field_name="create_time", lookup_expr='gte') create_time_start = filters.DateFilter(field_name="create_time", lookup_expr='gte')
@ -7,3 +8,9 @@ class ClockRecordFilterSet(filters.FilterSet):
class Meta: class Meta:
model = ClockRecord model = ClockRecord
fields = ['create_by', 'create_time_start', 'create_time_end'] fields = ['create_by', 'create_time_start', 'create_time_end']
class EmployeeFilterSet(DynamicFieldsFilterMixin, filters.FilterSet):
class Meta:
model = Employee
fields = ['job_state']

View File

@ -0,0 +1,37 @@
# Generated by Django 3.2.9 on 2022-01-26 05:51
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('hrm', '0004_clockrecord'),
]
operations = [
migrations.RenameField(
model_name='employee',
old_name='birthdate',
new_name='birthday',
),
migrations.RenameField(
model_name='employee',
old_name='ID_number',
new_name='id_number',
),
migrations.RenameField(
model_name='employee',
old_name='jobstate',
new_name='job_state',
),
migrations.RenameField(
model_name='employee',
old_name='academic',
new_name='qualification',
),
migrations.RemoveField(
model_name='employee',
name='job',
),
]

View File

@ -14,20 +14,21 @@ class Employee(CommonAModel):
""" """
员工信息 员工信息
""" """
JOB_ON = 1
JOB_OFF = 2
jobstate_choices = ( jobstate_choices = (
(1, '在职'), (JOB_ON, '在职'),
(2, '离职'), (JOB_OFF, '离职'),
) )
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='employee_user') user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='employee_user')
number = models.CharField('人员编号', max_length=50,null=True, blank=True, unique=True) number = models.CharField('人员编号', max_length=50,null=True, blank=True, unique=True)
photo = models.CharField('证件照', max_length=1000, null=True, blank=True) photo = models.CharField('证件照', max_length=1000, null=True, blank=True)
ID_number = models.CharField('身份证号', max_length=100, null=True, blank=True) id_number = models.CharField('身份证号', max_length=100, null=True, blank=True)
gender = models.CharField('性别', max_length=10, default='') gender = models.CharField('性别', max_length=10, default='')
signature = models.CharField('签名图片', max_length=200, null=True, blank=True) signature = models.CharField('签名图片', max_length=200, null=True, blank=True)
birthdate = models.DateField('出生年月', null=True, blank=True) birthday = models.DateField('出生年月', null=True, blank=True)
academic = models.CharField('学历', max_length=50, null=True, blank=True) qualification = models.CharField('学历', max_length=50, null=True, blank=True)
jobstate = models.IntegerField('在职状态', choices=jobstate_choices, default=1) job_state = models.IntegerField('在职状态', choices=jobstate_choices, default=1)
job = models.ForeignKey(Position, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='岗位')
face_data = models.JSONField('人脸识别数据', null=True, blank=True) face_data = models.JSONField('人脸识别数据', null=True, blank=True)
class Meta: class Meta:
verbose_name = '员工补充信息' verbose_name = '员工补充信息'

View File

@ -1,11 +1,15 @@
from apps.system.models import User from apps.system.models import User
from rest_framework.serializers import ModelSerializer from rest_framework.serializers import ModelSerializer
from rest_framework import serializers from rest_framework import serializers
from utils.mixins import DynamicFieldsSerializerMixin
from .models import ClockRecord, Employee from .models import ClockRecord, Employee
from apps.system.serializers import UserListSerializer, UserSimpleSerializer from apps.system.serializers import OrganizationSimpleSerializer, UserListSerializer, UserSimpleSerializer
from django.db.models.query import Prefetch from django.db.models.query import Prefetch
class EmployeeSerializer(ModelSerializer): class EmployeeSerializer(DynamicFieldsSerializerMixin, ModelSerializer):
name = serializers.CharField(source='user.name', read_only=True)
dept_ = OrganizationSimpleSerializer(source='user.dept_', read_only=True)
class Meta: class Meta:
model = Employee model = Employee
exclude = ['face_data'] exclude = ['face_data']

View File

@ -28,8 +28,11 @@ class HRMService:
face_users = cache.get('face_users') face_users = cache.get('face_users')
if face_datas is None: if face_datas is None:
update_all_user_facedata_cache() update_all_user_facedata_cache()
try:
results = face_recognition.compare_faces(face_datas, unknown_face_encoding, tolerance=0.5) results = face_recognition.compare_faces(face_datas,
unknown_face_encoding, tolerance=0.5)
except:
return None, '识别失败'
for index, value in enumerate(results): for index, value in enumerate(results):
if value: if value:
# 识别成功 # 识别成功

View File

@ -11,7 +11,7 @@ def update_all_user_not_atwork():
""" """
将所有员工设为非在岗状态 将所有员工设为非在岗状态
""" """
User.objects.all().update(is_atwork=False) User.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

@ -4,7 +4,7 @@ from django.utils import timezone
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet, GenericViewSet from rest_framework.viewsets import ModelViewSet, GenericViewSet
from rest_framework.mixins import UpdateModelMixin, RetrieveModelMixin, CreateModelMixin, ListModelMixin from rest_framework.mixins import UpdateModelMixin, RetrieveModelMixin, CreateModelMixin, ListModelMixin
from apps.hrm.filters import ClockRecordFilterSet from apps.hrm.filters import ClockRecordFilterSet, EmployeeFilterSet
from apps.hrm.services import HRMService from apps.hrm.services import HRMService
from apps.hrm.tasks import update_all_user_facedata_cache from apps.hrm.tasks import update_all_user_facedata_cache
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
@ -30,6 +30,7 @@ class EmployeeViewSet(CreateUpdateModelAMixin, OptimizationMixin, UpdateModelMix
""" """
perms_map = {'get': '*', 'put': 'employee_update'} perms_map = {'get': '*', 'put': 'employee_update'}
queryset = Employee.objects.all() queryset = Employee.objects.all()
filterset_class = EmployeeFilterSet
serializer_class = EmployeeSerializer serializer_class = EmployeeSerializer
ordering = ['-pk'] ordering = ['-pk']
@ -91,6 +92,7 @@ class ClockRecordViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
ins.save() ins.save()
# 设为在岗 # 设为在岗
user.is_atwork = True user.is_atwork = True
user.last_check_time = now
user.save() 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)

View File

@ -3,9 +3,9 @@ from django_filters import rest_framework as filters
from apps.mtm.models import Material from apps.mtm.models import Material
from .models import IProduct, MaterialBatch from .models import IProduct, MaterialBatch
from django.utils import timezone from django.utils import timezone
from utils.mixins import DynamicFieldsFilterMixin
class MbFilterSet(DynamicFieldsFilterMixin, filters.FilterSet):
class MbFilterSet(filters.FilterSet):
material = filters.ModelMultipleChoiceFilter(field_name="material", queryset=Material.objects.all()) material = filters.ModelMultipleChoiceFilter(field_name="material", queryset=Material.objects.all())
tag = filters.CharFilter(method="filter_tag") tag = filters.CharFilter(method="filter_tag")
@ -20,12 +20,14 @@ class MbFilterSet(filters.FilterSet):
return queryset return queryset
class IProductFilterSet(filters.FilterSet): class IProductFilterSet(DynamicFieldsFilterMixin, filters.FilterSet):
order = filters.NumberFilter(field_name="wproduct__subproduction_plan__production_plan__order") order = filters.NumberFilter(field_name="wproduct__subproduction_plan__production_plan__order")
to_order = filters.NumberFilter(field_name="wproduct__to_order") to_order = filters.NumberFilter(field_name="wproduct__to_order")
need_to_order = filters.BooleanFilter(field_name="wproduct__need_to_order")
update_time_start = filters.DateFilter(field_name="update_time", lookup_expr='gte') update_time_start = filters.DateFilter(field_name="update_time", lookup_expr='gte')
update_time_end = filters.DateFilter(field_name="update_time", lookup_expr='lte') update_time_end = filters.DateFilter(field_name="update_time", lookup_expr='lte')
class Meta: class Meta:
model = IProduct model = IProduct
fields = ['material', 'warehouse', 'batch', 'order', 'material__type', fields = ['material', 'warehouse', 'batch', 'order', 'material__type',
'is_saled', 'update_time_start', 'update_time_end', 'to_order'] 'is_saled', 'update_time_start', 'update_time_end',
'to_order', 'need_to_order']

View File

@ -3,10 +3,13 @@ from rest_framework import serializers
from apps.inm.models import FIFO, FIFOItem, FIFOItemProduct, IProduct, MaterialBatch, WareHouse, Inventory from apps.inm.models import FIFO, FIFOItem, FIFOItemProduct, IProduct, MaterialBatch, WareHouse, Inventory
from apps.qm.models import TestRecord, TestRecordItem from apps.qm.models import TestRecord, TestRecordItem
from apps.sam.serializers import OrderSimpleSerializer
from apps.system.serializers import FileSimpleSerializer, UserSimpleSerializer from apps.system.serializers import FileSimpleSerializer, UserSimpleSerializer
from apps.mtm.serializers import MaterialSimpleSerializer from apps.mtm.serializers import MaterialSimpleSerializer
from django.db import transaction from django.db import transaction
from utils.mixins import DynamicFieldsSerializerMixin
@ -39,7 +42,7 @@ class InventorySerializer(serializers.ModelSerializer):
fields = '__all__' fields = '__all__'
class MaterialBatchSerializer(serializers.ModelSerializer): class MaterialBatchSerializer(DynamicFieldsSerializerMixin, serializers.ModelSerializer):
material_ = MaterialSimpleSerializer(source='material', read_only=True) material_ = MaterialSimpleSerializer(source='material', read_only=True)
warehouse_ = WareHouseSimpleSerializer(source='warehouse', read_only=True) warehouse_ = WareHouseSimpleSerializer(source='warehouse', read_only=True)
@ -48,12 +51,15 @@ class MaterialBatchSerializer(serializers.ModelSerializer):
fields = '__all__' fields = '__all__'
class IProductListSerializer(serializers.ModelSerializer): class IProductListSerializer(DynamicFieldsSerializerMixin, serializers.ModelSerializer):
material_ = MaterialSimpleSerializer(source='material', read_only=True) material_ = MaterialSimpleSerializer(source='material', read_only=True)
warehouse_ = WareHouseSimpleSerializer(source='warehouse', read_only=True) warehouse_ = WareHouseSimpleSerializer(source='warehouse', read_only=True)
need_to_order = serializers.BooleanField(source='wproduct.need_to_order', read_only=True)
is_mtestok = serializers.BooleanField(source='wproduct.is_mtestok', read_only=True) is_mtestok = serializers.BooleanField(source='wproduct.is_mtestok', read_only=True)
remark_mtest = serializers.CharField(source='wproduct.remark_mtest', read_only=True) remark_mtest = serializers.CharField(source='wproduct.remark_mtest', read_only=True)
to_order_ = OrderSimpleSerializer(source='wproduct.to_order', read_only=True)
order_ = OrderSimpleSerializer(
source='wproduct.subproduction_plan.production_plan.order', read_only=True)
class Meta: class Meta:
model = IProduct model = IProduct
fields = '__all__' fields = '__all__'

View File

@ -187,7 +187,9 @@ class IProductViewSet(ListModelMixin, GenericViewSet):
""" """
perms_map = {'*': '*'} perms_map = {'*': '*'}
queryset = IProduct.objects.select_related( queryset = IProduct.objects.select_related(
'material', 'warehouse', 'wproduct__subproduction_plan__production_plan__order') 'material', 'warehouse',
'wproduct__subproduction_plan__production_plan__order',
'wproduct__to_order')
serializer_class = IProductListSerializer serializer_class = IProductListSerializer
filterset_class = IProductFilterSet filterset_class = IProductFilterSet
search_fields = [] search_fields = []

View File

@ -1,6 +1,7 @@
from django.db.models.expressions import F from django.db.models.expressions import F
from django_filters import rest_framework as filters from django_filters import rest_framework as filters
from apps.mtm.models import Material, TechDoc from apps.mtm.models import Material, TechDoc
from utils.mixins import DynamicFieldsFilterMixin
@ -15,7 +16,7 @@ class TechDocFilterset(filters.FilterSet):
def filter_operation(self, queryset, name, value): def filter_operation(self, queryset, name, value):
return queryset.filter(subproduction__subplan_subprod__ow_subplan__operation=value).distinct() return queryset.filter(subproduction__subplan_subprod__ow_subplan__operation=value).distinct()
class MaterialFilterSet(filters.FilterSet): class MaterialFilterSet(DynamicFieldsFilterMixin, filters.FilterSet):
tag = filters.CharFilter(method='filter_tag') tag = filters.CharFilter(method='filter_tag')
class Meta: class Meta:
model = Material model = Material

View File

@ -1,11 +1,13 @@
from apps.em.serializers import EquipmentSimpleSerializer from apps.em.serializers import EquipmentSimpleSerializer
from rest_framework import serializers from rest_framework import serializers
from rest_framework.exceptions import ParseError, ValidationError from rest_framework.exceptions import ParseError, ValidationError
from utils.mixins import DynamicFieldsSerializerMixin
from .models import Material, Process, RecordForm, RecordFormField, Step, SubprodctionMaterial, TechDoc, UsedStep, SubProduction from .models import Material, Process, RecordForm, RecordFormField, Step, SubprodctionMaterial, TechDoc, UsedStep, SubProduction
from apps.system.serializers import FileSimpleSerializer, OrganizationSimpleSerializer from apps.system.serializers import FileSimpleSerializer, OrganizationSimpleSerializer
class MaterialSerializer(serializers.ModelSerializer): class MaterialSerializer(DynamicFieldsSerializerMixin, serializers.ModelSerializer):
class Meta: class Meta:
model = Material model = Material
fields = '__all__' fields = '__all__'

View File

@ -2,10 +2,13 @@ from django_filters import rest_framework as filters
from apps.mtm.models import Material, Step from apps.mtm.models import Material, Step
from apps.pm.models import ProductionPlan, SubProductionProgress from apps.pm.models import ProductionPlan, SubProductionProgress
from apps.wpm.models import Operation, WProduct from apps.wpm.models import Operation, WProduct
from datetime import *
from apps.wpm.services import WpmServies from apps.wpm.services import WpmServies
from django.db.models import F
class PlanFilterSet(filters.FilterSet): from utils.mixins import DynamicFieldsFilterMixin
class PlanFilterSet(DynamicFieldsFilterMixin, filters.FilterSet):
create_time_start = filters.DateFilter(field_name="create_time", lookup_expr='gte') create_time_start = filters.DateFilter(field_name="create_time", lookup_expr='gte')
create_time_end = filters.DateFilter(field_name="create_time", lookup_expr='lte') create_time_end = filters.DateFilter(field_name="create_time", lookup_expr='lte')
tag = filters.CharFilter(method='filter_tag') tag = filters.CharFilter(method='filter_tag')
@ -15,10 +18,19 @@ class PlanFilterSet(filters.FilterSet):
fields = ['product', 'order', 'create_time_start', 'create_time_end'] fields = ['product', 'order', 'create_time_start', 'create_time_end']
def filter_tag(self, queryset, name, value): def filter_tag(self, queryset, name, value):
now = datetime.now()
day7_after = now + timedelta(days=7)
if value == 'planed': if value == 'planed':
queryset = queryset.filter(is_planed=True) queryset = queryset.filter(is_planed=True)
elif value == 'working': elif value == 'working':
queryset = queryset.exclude(state__in=[ProductionPlan.PLAN_STATE_DONE, ProductionPlan.PLAN_MTEST_DONE]) queryset = queryset.exclude(state__in=[ProductionPlan.PLAN_STATE_DONE,
ProductionPlan.PLAN_MTEST_DONE])
elif value == 'near_done':
queryset = queryset.filter(count_ok__lt=F('count'),
end_date__lte = datetime.date(day7_after))
elif value == 'out_done':
queryset = queryset.filter(count_ok__lt=F('count'),
end_date__gt = datetime.date(now))
return queryset return queryset
def filter_material(self, queryset, name, value): def filter_material(self, queryset, name, value):

View File

@ -3,6 +3,7 @@ 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, SubProductionSimpleSerializer
from apps.system.serializers import OrganizationSimpleSerializer from apps.system.serializers import OrganizationSimpleSerializer
from utils.mixins import DynamicFieldsSerializerMixin
class ProductionPlanCreateFromOrderSerializer(serializers.ModelSerializer): class ProductionPlanCreateFromOrderSerializer(serializers.ModelSerializer):
@ -10,7 +11,7 @@ class ProductionPlanCreateFromOrderSerializer(serializers.ModelSerializer):
model = ProductionPlan model = ProductionPlan
fields = ['order', 'count', 'start_date', 'end_date'] fields = ['order', 'count', 'start_date', 'end_date']
class ProductionPlanSerializer(serializers.ModelSerializer): class ProductionPlanSerializer(DynamicFieldsSerializerMixin, serializers.ModelSerializer):
order_ = OrderSimpleSerializer(source='order', read_only=True) order_ = OrderSimpleSerializer(source='order', read_only=True)
product_ = MaterialSimpleSerializer(source='product', read_only=True) product_ = MaterialSimpleSerializer(source='product', read_only=True)
class Meta: class Meta:

View File

@ -7,7 +7,7 @@ from apps.em.models import Equipment
from apps.em.serializers import EquipmentSimpleSerializer from apps.em.serializers import EquipmentSimpleSerializer
from apps.inm.models import MaterialBatch from apps.inm.models import MaterialBatch
from apps.inm.serializers import MaterialBatchSerializer from apps.inm.serializers import MaterialBatchSerializer
from apps.mtm.models import Step, SubProduction, SubprodctionMaterial, UsedStep 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, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer, SubProductionProgressSerializer from apps.pm.serializers import GenSubPlanSerializer, PickNeedSerializer, PlanDestorySerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer, SubProductionProgressSerializer
@ -220,8 +220,12 @@ class ResourceViewSet(GenericViewSet):
for i in rdata: for i in rdata:
# 计算输入物料 # 计算输入物料
materials = SubprodctionMaterial.objects.filter(subproduction__product__id=i['id'], materials = SubprodctionMaterial.objects.filter(subproduction__product__id=i['id'],
subproduction__is_deleted=False, is_deleted=False, material__type__in=[3,4], type=1).order_by('material__number')\ subproduction__is_deleted=False, is_deleted=False,
.values('material__id', 'material__name', 'material__number', 'material__type', 'count', 'material__count') material__type__in=[Material.MA_TYPE_MAINSO,
Material.MA_TYPE_HELPSO], type= SubprodctionMaterial.SUB_MA_TYPE_IN).order_by('material__number')\
.values('material__id', 'material__name',
'material__number', 'material__type',
'count', 'material__count', 'material__count_safe')
l_m = list(materials) l_m = list(materials)
for m in l_m: for m in l_m:
if m['material__id'] in res_d_list: if m['material__id'] in res_d_list:
@ -231,7 +235,8 @@ class ResourceViewSet(GenericViewSet):
res_d_list.append(m['material__id']) res_d_list.append(m['material__id'])
res.append({'id':m['material__id'], 'name':m['material__name'], res.append({'id':m['material__id'], 'name':m['material__name'],
'type':m['material__type'], 'number':m['material__number'], 'type':m['material__type'], 'number':m['material__number'],
'count':m['count']*i['count'], 'inv_count':m['material__count']}) 'count':m['count']*i['count'], 'inv_count':m['material__count'],
'count_safe':m['material__count_safe']})
return Response(res) return Response(res)
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=ResourceCalListSerializer) @action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=ResourceCalListSerializer)

View File

@ -4,8 +4,10 @@ from apps.sam.models import Order
from django.db.models import F from django.db.models import F
from datetime import datetime, timedelta from datetime import datetime, timedelta
from utils.mixins import DynamicFieldsFilterMixin
class OrderFilterSet(filters.FilterSet):
class OrderFilterSet(DynamicFieldsFilterMixin, filters.FilterSet):
create_time_start = filters.DateFilter(field_name="create_time", lookup_expr='gte') create_time_start = filters.DateFilter(field_name="create_time", lookup_expr='gte')
create_time_end = filters.DateFilter(field_name="create_time", lookup_expr='lte') create_time_end = filters.DateFilter(field_name="create_time", lookup_expr='lte')
material = filters.NumberFilter(method='filter_material') material = filters.NumberFilter(method='filter_material')
@ -23,13 +25,16 @@ class OrderFilterSet(filters.FilterSet):
plan_order__subplan_plan__progress_subplan__material__id=value).distinct() plan_order__subplan_plan__progress_subplan__material__id=value).distinct()
def filter_tag(self, queryset, name, value): def filter_tag(self, queryset, name, value):
now = datetime.now()
day7_after = now + timedelta(days=7)
if value == 'near_delivery': if value == 'near_delivery':
day7_after = datetime.now() + timedelta(days=7)
queryset = queryset.filter(delivered_count__lt=F('count'), queryset = queryset.filter(delivered_count__lt=F('count'),
delivery_date__lte = datetime.date(day7_after)) delivery_date__lte = datetime.date(day7_after))
elif value == 'out_delivery': elif value == 'out_delivery':
queryset = queryset.filter(delivered_count__lt=F('count'), queryset = queryset.filter(delivered_count__lt=F('count'),
delivery_date__gt = datetime.date(datetime.now())) delivery_date__gt = datetime.date(datetime.now()))
elif value == 'not_done':
queryset = queryset.filter(delivered_count__lt=F('count'))
return queryset return queryset
class ContractFilterSet(filters.FilterSet): class ContractFilterSet(filters.FilterSet):

View File

@ -32,17 +32,10 @@ class Contract(CommonAModel):
""" """
合同信息 合同信息
""" """
state_choices = (
(0, '完好'),
(1, '限用'),
(2, '在修'),
(3, '禁用')
)
name = models.CharField('合同名称', max_length=100) name = models.CharField('合同名称', max_length=100)
number = models.CharField('合同编号', max_length=100, unique=True) number = models.CharField('合同编号', max_length=100, unique=True)
amount = models.IntegerField('合同金额', default=0) amount = models.IntegerField('合同金额', default=0)
invoice = models.IntegerField('开票金额', default=0) invoice = models.IntegerField('开票金额', default=0)
#state = models.CharField('合同状态', choices= state_choices, max_length=20, default=1)
customer = models.ForeignKey(Customer, verbose_name='关联客户', on_delete=models.CASCADE, related_name='contact_customer') customer = models.ForeignKey(Customer, verbose_name='关联客户', on_delete=models.CASCADE, related_name='contact_customer')
sign_date = models.DateField('签订日期') sign_date = models.DateField('签订日期')
description = models.CharField('描述', max_length=200, blank=True, null=True) description = models.CharField('描述', max_length=200, blank=True, null=True)

View File

@ -1,10 +1,9 @@
from django.db import transaction
from rest_framework import exceptions, serializers
from apps.inm.models import IProduct from rest_framework import serializers
from apps.inm.serializers import IProductListSerializer
from .models import Contract, Customer, Order, Sale, SaleProduct from utils.mixins import DynamicFieldsSerializerMixin
from .models import Contract, Customer, Order
from apps.mtm.serializers import MaterialSimpleSerializer from apps.mtm.serializers import MaterialSimpleSerializer
from utils.tools import ranstr from utils.tools import ranstr
@ -44,13 +43,13 @@ class ContractCreateUpdateSerializer(serializers.ModelSerializer):
class OrderCreateUpdateSerializer(serializers.ModelSerializer): class OrderCreateUpdateSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Order model = Order
fields = ['customer', 'contract', 'product', 'count', 'delivery_date'] fields = ['customer', 'contract', 'product', 'count', 'delivery_date', 'need_mtest']
def create(self, validated_data): def create(self, validated_data):
validated_data['number'] = 'DD' + ranstr(7) validated_data['number'] = 'DD' + ranstr(7)
return super().create(validated_data) return super().create(validated_data)
class OrderSerializer(serializers.ModelSerializer): class OrderSerializer(DynamicFieldsSerializerMixin, serializers.ModelSerializer):
contract_ = ContractSimpleSerializer(source='contract', read_only=True) contract_ = ContractSimpleSerializer(source='contract', read_only=True)
customer_ = CustomerSimpleSerializer(source='customer', read_only=True) customer_ = CustomerSimpleSerializer(source='customer', read_only=True)
product_ = MaterialSimpleSerializer(source='product', read_only=True) product_ = MaterialSimpleSerializer(source='product', read_only=True)
@ -63,48 +62,5 @@ class OrderSimpleSerializer(serializers.ModelSerializer):
customer_ = CustomerSimpleSerializer(source='customer', read_only=True) customer_ = CustomerSimpleSerializer(source='customer', read_only=True)
class Meta: class Meta:
model = Order model = Order
fields = ['id', 'number', 'contract_', 'customer_', 'need_mtest'] fields = ['id', 'number', 'contract_', 'customer_', 'need_mtest', 'delivery_date']
class SaleCreateSerializer(serializers.ModelSerializer):
iproducts = serializers.PrimaryKeyRelatedField(queryset=IProduct.objects.all(), many=True)
class Meta:
model = Sale
fields = ['customer', 'order', 'product', 'iproducts']
def validate(self, attrs):
order = attrs.get('order', None)
if order:
if order.customer:
attrs['customer'] = order.customer
attrs['product'] = order.product
for i in attrs['iproducts']:
if i.material != attrs['product']:
raise exceptions.APIException('产品选取错误')
return super().validate(attrs)
class SaleListSerializer(serializers.ModelSerializer):
customer_ = CustomerSimpleSerializer(source='customer', read_only=True)
order_ = OrderSimpleSerializer(source='order', read_only=True)
product_ = MaterialSimpleSerializer(source='product', read_only=True)
class Meta:
model = Sale
fields = '__all__'
class SaleProductListSerializer(serializers.ModelSerializer):
iproduct_ = IProductListSerializer(source='iproduct', read_only=True)
class Meta:
model = SaleProduct
fields = '__all__'
class SaleProductCreateSerializer(serializers.ModelSerializer):
class Meta:
model = SaleProduct
fields = ['sale', 'iproduct']
def create(self, validated_data):
validated_data['number'] = validated_data['iproduct'].number
instance = SaleProduct.objects.create(**validated_data)
instance.sale.count = SaleProduct.objects.filter(sale=instance.sale).count()
instance.sale.save()
return instance

View File

@ -0,0 +1,51 @@
from rest_framework import serializers
from rest_framework import exceptions
from apps.inm.models import IProduct
from apps.inm.serializers import IProductListSerializer
from apps.mtm.serializers import MaterialSimpleSerializer
from apps.sam.models import Sale, SaleProduct
from apps.sam.serializers import CustomerSimpleSerializer, OrderSimpleSerializer
class SaleCreateSerializer(serializers.ModelSerializer):
iproducts = serializers.PrimaryKeyRelatedField(queryset=
IProduct.objects.all(), many=True)
class Meta:
model = Sale
fields = ['customer', 'order', 'product', 'iproducts']
def validate(self, attrs):
order = attrs.get('order', None)
if order:
if order.customer:
attrs['customer'] = order.customer
attrs['product'] = order.product
for i in attrs['iproducts']:
if i.material != attrs['product']:
raise exceptions.APIException('产品选取错误')
return super().validate(attrs)
class SaleListSerializer(serializers.ModelSerializer):
customer_ = CustomerSimpleSerializer(source='customer', read_only=True)
order_ = OrderSimpleSerializer(source='order', read_only=True)
product_ = MaterialSimpleSerializer(source='product', read_only=True)
class Meta:
model = Sale
fields = '__all__'
class SaleProductListSerializer(serializers.ModelSerializer):
iproduct_ = IProductListSerializer(source='iproduct', read_only=True)
class Meta:
model = SaleProduct
fields = '__all__'
class SaleProductCreateSerializer(serializers.ModelSerializer):
class Meta:
model = SaleProduct
fields = ['sale', 'iproduct']
def create(self, validated_data):
validated_data['number'] = validated_data['iproduct'].number
instance = SaleProduct.objects.create(**validated_data)
instance.sale.count = SaleProduct.objects.filter(sale=instance.sale).count()
instance.sale.save()
return instance

View File

@ -1,9 +1,11 @@
from django.db.models import base from django.db.models import base
from rest_framework import urlpatterns from rest_framework import urlpatterns
from apps.sam.views import CustomerViewSet,ContractViewSet,OrderViewSet, SaleProductViewSet, SaleViewSet from apps.sam.views import CustomerViewSet,ContractViewSet, OrderViewSet
from django.urls import path, include from django.urls import path, include
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from apps.sam.views_sale import SaleProductViewSet, SaleViewSet
router = DefaultRouter() router = DefaultRouter()
router.register('customer', CustomerViewSet, basename='customer') router.register('customer', CustomerViewSet, basename='customer')
router.register('contract', ContractViewSet, basename='contract') router.register('contract', ContractViewSet, basename='contract')

View File

@ -1,22 +1,12 @@
from django.db import transaction
from django.db.models.aggregates import Count
from rest_framework import exceptions, serializers
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin
from apps.mtm.models import Material
from apps.inm.models import FIFO, FIFOItem, FIFOItemProduct, IProduct, WareHouse
from apps.inm.signals import update_inm
from apps.sam.filters import ContractFilterSet, OrderFilterSet from apps.sam.filters import ContractFilterSet, OrderFilterSet
from apps.sam.serializers import ContractCreateUpdateSerializer, ContractSerializer, CustomerCreateUpdateSerializer, CustomerSerializer, OrderCreateUpdateSerializer, OrderSerializer, SaleCreateSerializer, SaleListSerializer, SaleProductCreateSerializer, SaleProductListSerializer from apps.sam.serializers import ContractCreateUpdateSerializer, ContractSerializer, \
from apps.sam.models import Contract, Customer, Order, Sale, SaleProduct CustomerCreateUpdateSerializer, CustomerSerializer, OrderCreateUpdateSerializer, OrderSerializer
from rest_framework.viewsets import GenericViewSet, ModelViewSet from apps.sam.models import Contract, Customer, Order
from rest_framework.viewsets import ModelViewSet
from apps.system.mixins import CreateUpdateCustomMixin from apps.system.mixins import CreateUpdateCustomMixin
from django.shortcuts import render
from rest_framework.decorators import action
from django.db.models import F from django.db.models import F
from rest_framework.response import Response from rest_framework.response import Response
from django.utils import timezone from rest_framework.decorators import action
from apps.wf.models import Workflow
# Create your views here. # Create your views here.
class CustomerViewSet(CreateUpdateCustomMixin, ModelViewSet): class CustomerViewSet(CreateUpdateCustomMixin, ModelViewSet):
""" """
@ -81,129 +71,6 @@ class OrderViewSet(CreateUpdateCustomMixin, ModelViewSet):
return Response(serializer.data) return Response(serializer.data)
class SaleViewSet(CreateUpdateCustomMixin, ListModelMixin, RetrieveModelMixin, CreateModelMixin, DestroyModelMixin, GenericViewSet):
"""
销售记录
"""
perms_map = {'*': '*'}
queryset = Sale.objects.select_related('customer', 'order', 'product', 'order__contract').all()
serializer_class = SaleListSerializer
search_fields = ['customer__name', 'order__number']
filterset_fields = ['product', 'order', 'customer']
ordering_fields = ['create_time']
ordering = ['-create_time']
def get_serializer_class(self):
if self.action == 'create':
return SaleCreateSerializer
elif self.action == 'retrieve':
return SaleListSerializer
return super().get_serializer_class()
def create(self, request, *args, **kwargs):
data = request.data
serializer = SaleCreateSerializer(data=data)
serializer.is_valid(raise_exception=True)
vdata = serializer.validated_data
with transaction.atomic():
iproducts = vdata.pop('iproducts')
vdata['count'] = len(iproducts)
sale = Sale.objects.create(**vdata)
i_l = []
for i in iproducts:
i_d ={}
i_d['sale'] = sale
i_d['number'] = i.number
i_d['iproduct'] = i
i_l.append(SaleProduct(**i_d))
SaleProduct.objects.bulk_create(i_l)
return Response()
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=serializers.Serializer)
@transaction.atomic
def audit(self, request, pk=None):
"""
审核
"""
obj = self.get_object()
if obj.is_audited:
raise exceptions.APIException('已审核通过')
# 创建出库记录
fifo = FIFO()
fifo.type = FIFO.FIFO_TYPE_SALE_OUT
fifo.is_audited = True
fifo.auditor = request.user
fifo.inout_date = timezone.now()
fifo.create_by = request.user
fifo.save()
# 创建出库条目
ips = IProduct.objects.filter(sale_iproduct__sale=obj)
items = ips.values('warehouse', 'material', 'batch').annotate(total=Count('id'))
for i in items:
warehouse = WareHouse.objects.get(id=i['warehouse'])
material = Material.objects.get(id=i['material'])
fifoitem = FIFOItem()
fifoitem.need_test = False
fifoitem.warehouse = warehouse
fifoitem.material = material
fifoitem.count = i['total']
fifoitem.batch = i['batch']
fifoitem.fifo = fifo
fifoitem.save()
items_p = ips.filter(warehouse=warehouse, batch=i['batch'])
ipxs = []
for i in items_p:
# 创建出库明细半成品
ip = {}
ip['fifoitem'] = fifoitem
ip['number'] = i.number
ip['material'] = i.material
ip['iproduct'] = i
ipxs.append(FIFOItemProduct(**ip))
FIFOItemProduct.objects.bulk_create(ipxs)
# 更新成品库情况
ips.update(is_saled=True)
# 更新动态产品表情况
from apps.wpm.models import WProduct
WProduct.objects.filter(iproduct_wproduct__sale_iproduct__sale=obj).update(
act_state=WProduct.WPR_ACT_STATE_SELLED)
# 更新库存
update_inm(fifo)
# 变更审核状态
obj.is_audited = True
obj.save()
# 变更订单状态
if obj.order:
order = obj.order
order.delivered_count = order.delivered_count + obj.count
order.save()
return Response()
class SaleProductViewSet(ListModelMixin, DestroyModelMixin, CreateModelMixin, GenericViewSet):
"""
销售记录关联产品
"""
perms_map = {'*': '*'}
queryset = SaleProduct.objects.select_related('iproduct', 'iproduct__material', 'iproduct__warehouse').all()
serializer_class = SaleProductListSerializer
search_fields = []
filterset_fields = ['sale', 'iproduct']
ordering_fields = ['create_time']
ordering = ['id']
def get_serializer_class(self):
if self.action == 'create':
return SaleProductCreateSerializer
return super().get_serializer_class()
def destroy(self, request, *args, **kwargs):
obj = self.get_object()
sale = obj.sale
if sale.is_audited:
raise exceptions.APIException('该销售记录已审核,不可删除产品')
sale.count = SaleProduct.objects.filter(sale=obj.sale).count()
sale.save()
obj.delete()
return Response()

View File

@ -0,0 +1,143 @@
from rest_framework.mixins import ListModelMixin, DestroyModelMixin, CreateModelMixin, RetrieveModelMixin
from rest_framework.viewsets import GenericViewSet
from rest_framework.response import Response
from apps.inm.models import FIFO, FIFOItem, FIFOItemProduct, IProduct, WareHouse
from apps.mtm.models import Material
from apps.sam.models import Sale, SaleProduct
from apps.sam.serializers_sale import SaleCreateSerializer, SaleListSerializer, SaleProductCreateSerializer, SaleProductListSerializer
from rest_framework import exceptions
from django.db import transaction
from rest_framework.decorators import action
from django.utils import timezone
from apps.system.mixins import CreateUpdateModelAMixin
from apps.inm.signals import update_inm
from rest_framework import serializers
from django.db.models import Count
class SaleViewSet(CreateUpdateModelAMixin, ListModelMixin, RetrieveModelMixin, CreateModelMixin, DestroyModelMixin, GenericViewSet):
"""
销售记录
"""
perms_map = {'*': '*'}
queryset = Sale.objects.select_related('customer', 'order', 'product', 'order__contract').all()
serializer_class = SaleListSerializer
search_fields = ['customer__name', 'order__number']
filterset_fields = ['product', 'order', 'customer']
ordering_fields = ['create_time']
ordering = ['-create_time']
def get_serializer_class(self):
if self.action == 'create':
return SaleCreateSerializer
elif self.action == 'retrieve':
return SaleListSerializer
return super().get_serializer_class()
def create(self, request, *args, **kwargs):
data = request.data
serializer = SaleCreateSerializer(data=data)
serializer.is_valid(raise_exception=True)
vdata = serializer.validated_data
with transaction.atomic():
iproducts = vdata.pop('iproducts')
vdata['count'] = len(iproducts)
sale = Sale.objects.create(**vdata)
i_l = []
for i in iproducts:
i_d ={}
i_d['sale'] = sale
i_d['number'] = i.number
i_d['iproduct'] = i
i_l.append(SaleProduct(**i_d))
SaleProduct.objects.bulk_create(i_l)
return Response()
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=serializers.Serializer)
@transaction.atomic
def audit(self, request, pk=None):
"""
审核
"""
obj = self.get_object()
if obj.is_audited:
raise exceptions.APIException('已审核通过')
# 创建出库记录
fifo = FIFO()
fifo.type = FIFO.FIFO_TYPE_SALE_OUT
fifo.is_audited = True
fifo.auditor = request.user
fifo.inout_date = timezone.now()
fifo.create_by = request.user
fifo.save()
# 创建出库条目
ips = IProduct.objects.filter(sale_iproduct__sale=obj)
items = ips.values('warehouse', 'material', 'batch').annotate(total=Count('id'))
for i in items:
warehouse = WareHouse.objects.get(id=i['warehouse'])
material = Material.objects.get(id=i['material'])
fifoitem = FIFOItem()
fifoitem.need_test = False
fifoitem.warehouse = warehouse
fifoitem.material = material
fifoitem.count = i['total']
fifoitem.batch = i['batch']
fifoitem.fifo = fifo
fifoitem.save()
items_p = ips.filter(warehouse=warehouse, batch=i['batch'])
ipxs = []
for i in items_p:
# 创建出库明细半成品
ip = {}
ip['fifoitem'] = fifoitem
ip['number'] = i.number
ip['material'] = i.material
ip['iproduct'] = i
ipxs.append(FIFOItemProduct(**ip))
FIFOItemProduct.objects.bulk_create(ipxs)
# 更新成品库情况
ips.update(is_saled=True)
# 更新动态产品表情况
from apps.wpm.models import WProduct
WProduct.objects.filter(iproduct_wproduct__sale_iproduct__sale=obj).update(
act_state=WProduct.WPR_ACT_STATE_SELLED)
# 更新库存
update_inm(fifo)
# 变更审核状态
obj.is_audited = True
obj.save()
# 变更订单状态
if obj.order:
order = obj.order
order.delivered_count = order.delivered_count + obj.count
order.save()
return Response()
class SaleProductViewSet(ListModelMixin, DestroyModelMixin, CreateModelMixin, GenericViewSet):
"""
销售记录关联产品
"""
perms_map = {'*': '*'}
queryset = SaleProduct.objects.select_related('iproduct', 'iproduct__material', 'iproduct__warehouse').all()
serializer_class = SaleProductListSerializer
search_fields = []
filterset_fields = ['sale', 'iproduct']
ordering_fields = ['create_time']
ordering = ['id']
def get_serializer_class(self):
if self.action == 'create':
return SaleProductCreateSerializer
return super().get_serializer_class()
def destroy(self, request, *args, **kwargs):
obj = self.get_object()
sale = obj.sale
if sale.is_audited:
raise exceptions.APIException('该销售记录已审核,不可删除产品')
sale.count = SaleProduct.objects.filter(sale=obj.sale).count()
sale.save()
obj.delete()
return Response()

View File

@ -0,0 +1,18 @@
# 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

@ -117,6 +117,7 @@ class User(AbstractUser):
'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) 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_name', 'dept', 'roles', 'avatar', 'dept_name', 'dept', 'roles', 'avatar',
'roles_name', 'is_atwork'] 'roles_name', 'is_atwork', 'last_check_time']
@staticmethod @staticmethod
def setup_eager_loading(queryset): def setup_eager_loading(queryset):

View File

@ -41,7 +41,7 @@ class WProductFilterSet(DynamicFieldsFilterMixin, filters.FilterSet):
class Meta: class Meta:
model = WProduct model = WProduct
fields = ['step', 'subproduction_plan', 'material', fields = ['step', 'subproduction_plan', 'material',
'step__process', 'act_state', 'material__type'] 'step__process', 'act_state', 'material__type', 'need_to_order']
def filter_tag(self, queryset, name, value): def filter_tag(self, queryset, name, value):
if value == 'no_scrap': if value == 'no_scrap':

View File

@ -0,0 +1,31 @@
# Generated by Django 3.2.9 on 2022-01-25 03:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wpm', '0051_auto_20220120_1541'),
]
operations = [
migrations.RemoveField(
model_name='wproduct',
name='is_mtested',
),
migrations.RemoveField(
model_name='wproductflow',
name='is_mtested',
),
migrations.AddField(
model_name='wproduct',
name='need_to_order',
field=models.BooleanField(default=False, verbose_name='是否要指派订单'),
),
migrations.AddField(
model_name='wproductflow',
name='need_to_order',
field=models.BooleanField(default=False, verbose_name='是否要指派订单'),
),
]

View File

@ -119,6 +119,7 @@ class WProduct(CommonAModel):
ticket = models.ForeignKey('wf.ticket', verbose_name='当前工单', ticket = models.ForeignKey('wf.ticket', verbose_name='当前工单',
on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_ticket') on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_ticket')
need_to_order = models.BooleanField('是否要指派订单', default=False)
to_order = models.ForeignKey('sam.order', verbose_name='指派的订单', null=True, blank=True, on_delete = models.CASCADE) to_order = models.ForeignKey('sam.order', verbose_name='指派的订单', null=True, blank=True, on_delete = models.CASCADE)
is_mtestok = models.BooleanField('是否军检合格', null=True, blank=True) is_mtestok = models.BooleanField('是否军检合格', null=True, blank=True)
remark_mtest = models.TextField('军检备注', null=True, blank=True) remark_mtest = models.TextField('军检备注', null=True, blank=True)
@ -192,6 +193,8 @@ class WproductFlow(CommonAModel):
on_delete=models.SET_NULL, null=True, blank=True) on_delete=models.SET_NULL, null=True, blank=True)
ticket = models.ForeignKey('wf.ticket', verbose_name='当前工单', ticket = models.ForeignKey('wf.ticket', verbose_name='当前工单',
on_delete=models.SET_NULL, null=True, blank=True) on_delete=models.SET_NULL, null=True, blank=True)
need_to_order = models.BooleanField('是否要指派订单', default=False)
to_order = models.ForeignKey('sam.order', verbose_name='指派的订单', null=True, blank=True, on_delete = models.CASCADE) to_order = models.ForeignKey('sam.order', verbose_name='指派的订单', null=True, blank=True, on_delete = models.CASCADE)
is_mtestok = models.BooleanField('是否军检合格', null=True, blank=True) is_mtestok = models.BooleanField('是否军检合格', null=True, blank=True)
remark_mtest = models.TextField('军检备注', null=True, blank=True) remark_mtest = models.TextField('军检备注', null=True, blank=True)

View File

@ -575,3 +575,6 @@ class WproductMtestSerializer(serializers.ModelSerializer):
class WproductToOrderSerializer(serializers.Serializer): class WproductToOrderSerializer(serializers.Serializer):
wproducts = serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all(), many=True) wproducts = serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all(), many=True)
order = serializers.PrimaryKeyRelatedField(queryset=Order.objects.all()) order = serializers.PrimaryKeyRelatedField(queryset=Order.objects.all())
class WproductNeedToOrderSerializer(serializers.Serializer):
wproducts = serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all(), many=True)

View File

View File

@ -26,7 +26,7 @@ from apps.wpm.serializers import CuttingListSerializer, OperationEquipListSerial
OperationListSerializer, OperationWproductUpdateSerializer, PickHalfsSerializer, \ OperationListSerializer, OperationWproductUpdateSerializer, PickHalfsSerializer, \
PickSerializer, OperationInitSerializer, OperationSubmitSerializer, ScrapSerializer, WMaterialListSerializer, \ PickSerializer, OperationInitSerializer, OperationSubmitSerializer, ScrapSerializer, WMaterialListSerializer, \
WProductCardSerializer, WProductDetailSerializer, WProductListSerializer, \ WProductCardSerializer, WProductDetailSerializer, WProductListSerializer, \
WpmTestFormInitSerializer, WproductMtestSerializer, WproductPutInSerializer, \ WpmTestFormInitSerializer, WproductMtestSerializer, WproductNeedToOrderSerializer, WproductPutInSerializer, \
WproductPutInsSerializer, WproductTicketListSerializer, WproductToOrderSerializer WproductPutInsSerializer, WproductTicketListSerializer, WproductToOrderSerializer
from rest_framework.response import Response from rest_framework.response import Response
@ -399,7 +399,7 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
军检 军检
""" """
obj = self.get_object() obj = self.get_object()
if obj.is_mtestok is None: if obj.is_mtestok is not None:
raise exceptions.APIException('已进行军检') raise exceptions.APIException('已进行军检')
if obj.material.type != Material.MA_TYPE_GOOD: if obj.material.type != Material.MA_TYPE_GOOD:
raise exceptions.APIException('军检必须是成品') raise exceptions.APIException('军检必须是成品')
@ -409,6 +409,7 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
if is_mtestok: if is_mtestok:
WpmServies.update_plan_state_by_mtestok( WpmServies.update_plan_state_by_mtestok(
obj.subproduction_plan.production_plan) obj.subproduction_plan.production_plan)
obj.update_by = request.user
obj.save() obj.save()
change_str = 'mtest_notok' change_str = 'mtest_notok'
if is_mtestok: if is_mtestok:
@ -441,6 +442,24 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
ret.append([str(index + 1), item['step_name'], item['actions']]) ret.append([str(index + 1), item['step_name'], item['actions']])
return Response(ret) return Response(ret)
@action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=WproductNeedToOrderSerializer)
@transaction.atomic
def need_to_order(self, request, pk=None):
"""
设为需要指派订单
"""
serializer = WproductNeedToOrderSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
vdata = serializer.validated_data
wps = WProduct.objects.filter(id__in = [i.id for i in vdata.get('wproducts')])
wps.update()
for i in wps:
i.need_to_order = True
i.update_by = request.user
i.save()
WpmServies.add_wproduct_flow_log(i, change_str='need_to_order')
return Response()
@action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=WproductToOrderSerializer) @action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=WproductToOrderSerializer)
@transaction.atomic @transaction.atomic
def to_order(self, request, pk=None): def to_order(self, request, pk=None):

View File

@ -48,11 +48,13 @@ class FitJSONRenderer(JSONRenderer):
response_body = BaseResponse() response_body = BaseResponse()
response = renderer_context.get("response") response = renderer_context.get("response")
status_code = response.status_code # Http状态异常码 status_code = response.status_code # Http状态异常码
print(status_code)
if status_code >= 400: # 如果http响应异常 if status_code >= 400: # 如果http响应异常
if isinstance(data, dict) and 'code' in data: # 如果自定义了异常码 if isinstance(data, dict) and 'code' in data: # 如果自定义了异常码
response_body = data response_body = data
else: else:
response_body.data = data # data里是详细异常信息 response_body.data = data # data里是详细异常信息
response_body.code = status_code
prefix = "" prefix = ""
if isinstance(data, dict): if isinstance(data, dict):
prefix = list(data.keys())[0] prefix = list(data.keys())[0]