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{
overflow: visible;
}
#warningTabs .el-tabs__item{
padding: 0 10px!important;
}
</style>

View File

@ -19,14 +19,12 @@
@expand-change="handlerExpand"
: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 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 label="产品型号" prop="productNum">
</el-table-column>
<el-table-column label="生产数量" prop="per">
</el-table-column>
</el-table>
</div>
</template>

View File

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

View File

@ -86,22 +86,11 @@
</div>
</div>
<div id="dashboardMiddle" class="dashboardMiddle">
<el-row>
<el-col :span="15">
<div class="dashboardCardPadding">
<div style="width: 65%;float: left;">
<div class="CardTitleWrap">
<span class="verticalLine"></span><span class="dashboardCardTitle">任务进度</span>
</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>
<gantt
v-if="proList.length>0"
@ -110,45 +99,37 @@
:ganttHeight="ganttHeight"
></gantt>
</div>
</el-col>
<el-col :span="1" style="height: 1px"></el-col>
<el-col :span="8">
<div class="dashboardCardHand">
<div class="dashboardCardFilter">
<el-date-picker
v-model="chartDate"
type="monthrange"
start-placeholder="开始日期"
end-placeholder="结束日期"
range-separator=""
format="yyyy 年 MM 月"
value-format="yyyy-MM"
@change="searchTimeChange('1')"
>
</el-date-picker>
<div class="convenientWrap">
<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 style="width: 34%;position:relative;float: right" >
<div class="CardTitleWrap">
<span class="verticalLine"></span><span class="dashboardCardTitle">成品率</span>
</div>
<div class="dashboardCardHand" style="position: absolute;top: 0;right: 10px;">
<el-date-picker
v-model="chartDate"
type="monthrange"
start-placeholder="开始日期"
end-placeholder="结束日期"
range-separator=""
format="yyyy 年 MM 月"
value-format="yyyy-MM"
@change="searchTimeChange('1')"
>
</el-date-picker>
</div>
<div id="chartColumn" style="width:100%;" :style="{height:ganttHeight+'px'}"></div>
</el-col>
</el-row>
</div>
</div>
</div>
<el-row :gutter="5">
<el-col :span="8">
<div class="dashboardBottomRow">
<div class="dashboardCardHand">
<div class="CardTitleWrap">
<div class="CardTitleWrap" style="border-bottom: 0">
<span class="verticalLine"></span><span class="dashboardCardTitle">生产设备</span>
<span @click="refreshBottomTabel('1')">
<el-icon class="el-icon-refresh refreshIcon"></el-icon>
</span>
</div>
<div class="block">
<el-pagination
@ -183,7 +164,7 @@
<el-table-column label="设备状态" prop="material_">
<!--type=1生产设备-->
<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>
@ -207,14 +188,14 @@
</div>
</template>
</el-table-column>
<el-table-column label="下次校准日期" prop="model">
<!-- <el-table-column label="下次校准日期" prop="model">
<template slot-scope="scope">
<el-tag v-if="scope.row.next_check_date" :type="setClass(scope.row.next_check_date)">
{{scope.row.next_check_date}}
</el-tag>
<div v-else></div>
</template>
</el-table-column>
</el-table-column>-->
</el-table>
</div>
</div>
@ -222,8 +203,11 @@
<el-col :span="8">
<div class="dashboardBottomRow">
<div class="dashboardCardHand">
<div class="CardTitleWrap">
<div class="CardTitleWrap" style="border-bottom: 0">
<span class="verticalLine"></span><span class="dashboardCardTitle">人员到岗</span>
<span @click="refreshBottomTabel('2')">
<el-icon class="el-icon-refresh refreshIcon"></el-icon>
</span>
</div>
<div class="block">
<el-pagination
@ -274,8 +258,11 @@
<el-col :span="8">
<div class="dashboardBottomRow">
<div class="dashboardCardHand">
<div class="CardTitleWrap">
<div class="CardTitleWrap" style="border-bottom: 0">
<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 class="block">
<el-pagination
@ -293,13 +280,13 @@
</div>
<div class="dashboardCardPadding">
<el-tabs type="card" :style="{height:cardTabelHeight+'px'}" v-model="activeName"
@tab-click="activeNameClick">
@tab-click="activeNameClick" id="warningTabs">
<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 in warningList" :key="item.id">
<li class="listItem" v-for="(item,$index) in warningList" :key="$index">
<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>
</li>
</ul>
@ -307,9 +294,9 @@
<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 in warningList" :key="item.id">
<li class="listItem" v-for="(item,$index) in warningList" :key="$index">
<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>
</li>
</ul>
@ -317,13 +304,39 @@
<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 in warningList" :key="item.id">
<li class="listItem" v-for="(item,$index) in warningList" :key="$index">
<div class="itemText">
<span>{{item.name}}({{item.number}})</span><span>{{item.delivery_date}}到期</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-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>
</div>
</div>
@ -331,7 +344,6 @@
</el-row>
</div>
</template>
<script>
import echarts from 'echarts'
import {mapGetters} from 'vuex';
@ -544,11 +556,9 @@
this.drawChart();
}
});
//获取库存警告
getMaterialList({page: 1, page_size:that.warningPageSize,tag: 'low_inm'}).then((response) => {
if (response.data) {
debugger;
that.warningList = response.data.results;
that.warningTotal = response.data.count;
}
@ -558,6 +568,7 @@
getEquipmentList() {
let that = this;
this.listLoadingEm = true;
that.equipmentPage = 1;
getpEquipmentList({page: that.equipmentPage, page_size: that.equipmentPageSize}).then((response) => {
if (response.data) {
that.equipmentList = response.data.results;
@ -583,6 +594,7 @@
getUserList() {
let that = this;
that.listLoadingUser = true;
that.userPage =1;
getUserList({page: that.userPage, page_size: that.userPageSize, fields: 'id,name,dept_name,is_atwork'}).then((response) => {
if (response.data) {
that.userList = response.data.results;
@ -633,40 +645,6 @@
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() {
let that = this;
@ -794,60 +772,6 @@
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) {
let that = this;
@ -878,33 +802,93 @@
}
},
//提示
activeNameClick(tab) {
debugger;
debugger;
activeNameClick() {
let that = this;
that.warningPage = 1;
that.warningList = [];
if (tab.label === '库存警告') {
if (that.activeName === '库存警告') {
getMaterialList({page: 1, page_size:that.warningPageSize, tag: 'low_inm'}).then((response) => {
if (response.data) {
that.warningList = response.data.results;
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) => {
if (response.data) {
that.warningList = response.data.results;
that.warningTotal = response.data.count;
}
});
} else if (tab.label === '过期提醒') {
} else if (that.activeName === '过期提醒') {
getmaterialbatchList({page: 1, page_size:that.warningPageSize, tag: 'expired'}).then((response) => {
if (response.data) {
that.warningList = response.data.results;
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) {
@ -912,7 +896,30 @@
this.warningPage = 1;
},
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() {
let that = this;
@ -986,13 +993,23 @@
}
})
},
refreshBottomTabel(index){
if(index==='1') {//生产设备
this.getEquipmentList();//设备列表
}else if(index==='2'){//人员到岗
this.getUserList();//用户列表
}else{//提醒
this.activeNameClick();
}
},
},
mounted() {
let hei = document.getElementsByClassName('app-main')[0].clientHeight;
let heig = document.getElementsByClassName('dashboardTopCard')[0].clientHeight;
let domHeight = ((hei - heig - 140) / 2);
this.cardTabelHeight = domHeight-35;
this.ganttHeight = domHeight - 10;
this.cardTabelHeight = domHeight-37;
this.ganttHeight = domHeight+35;
document.getElementById('chartColumn').style.height = domHeight + 'px';
this.getUserList();//用户列表
this.getEquipmentList();//设备列表
@ -1015,12 +1032,14 @@
border-radius: 5px;
margin-bottom: 5px;
background: #ffffff;
overflow: hidden;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
}
.dashboardBottomRow {
margin-bottom: 0;
}
.dashboardCardPadding {
overflow: hidden;
padding: 5px 20px 20px 20px;
}
/**/
@ -1130,7 +1149,9 @@
/*成品率筛选条件*/
.dashboardCardHand {
display: flex;
padding-left: 1%;
justify-content: space-between;
border-bottom: 1px solid #f5f5f5;
.dashboardCardFilter {
display: flex;
.convenientWrap {
@ -1161,6 +1182,12 @@
}
}
}
.refreshIcon{
color: #409EFF;
font-weight: bold;
font-size: 18px;
cursor: pointer;
}
.anim {
transition: all 0.5s;
margin-top: -35px; //高度等于行高
@ -1175,13 +1202,19 @@
.listItem {
height: 40px;
line-height: 40px;
font-size: 16px;
font-size: 12px;
.itemText {
display: flex;
justify-content: space-between;
}
}
}
.nearStatus{
color: #e6a23c;
}
.outStatus{
color: #f56c6c;
}
#chartColumn > div {
height: 100% !important;
canvas {

View File

@ -2,7 +2,7 @@ from django.db.models import base
from rest_framework import urlpatterns
from django.urls import path, include
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 = [
path('cleandata/', CleanDataView.as_view()),
@ -11,6 +11,7 @@ urlpatterns = [
path('update_last_result/', UpdateLastTestResult.as_view()),
path('update_fifoitem/', UpdateFIFOItem.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

@ -99,4 +99,10 @@ class UpdateEquipState(APIView):
def post(self, request, format=None):
update_equip_state_by_next_check_date()
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.serializers import ModelSerializer
from rest_framework import exceptions
from utils.mixins import DynamicFieldsSerializerMixin
from .models import Equipment, ECheckRecord
from apps.system.serializers import OrganizationSimpleSerializer, UserSimpleSerializer
class EquipmentListSerializer(ModelSerializer):
class EquipmentListSerializer(DynamicFieldsSerializerMixin, ModelSerializer):
keeper_ = UserSimpleSerializer(source='keeper', read_only=True)
step_ = serializers.SerializerMethodField()
class Meta:
@ -37,7 +39,7 @@ class EquipmentCreateUpdateSerializer(ModelSerializer):
class EquipmentSimpleSerializer(ModelSerializer):
class Meta:
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 import serializers, status
from rest_framework.response import Response
from apps.em.filters import EquipFilterSet
from apps.em.models import Equipment, ECheckRecord
from apps.em.serializers import DaqCreateSerializer, EChcekRecordCreateSerializer, ECheckRecordListSerializer, \
EquipmentCreateUpdateSerializer, EquipmentListSerializer
@ -26,7 +27,7 @@ class EquipmentViewSet(CreateUpdateModelAMixin, OptimizationMixin, ModelViewSet)
queryset = Equipment.objects.all()
serializer_class = EquipmentListSerializer
search_fields = ['number', 'name','description']
filterset_fields = ['keeper', 'type']
filterset_class = EquipFilterSet
ordering_fields = ['create_time']
ordering = ['-create_time']

View File

@ -1,9 +1,16 @@
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):
create_time_start = filters.DateFilter(field_name="create_time", lookup_expr='gte')
create_time_end = filters.DateFilter(field_name="create_time", lookup_expr='lte')
class Meta:
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 = (
(1, '在职'),
(2, '离职'),
(JOB_ON, '在职'),
(JOB_OFF, '离职'),
)
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='employee_user')
number = models.CharField('人员编号', max_length=50,null=True, blank=True, unique=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='')
signature = models.CharField('签名图片', max_length=200, null=True, blank=True)
birthdate = models.DateField('出生年月', null=True, blank=True)
academic = models.CharField('学历', max_length=50, null=True, blank=True)
jobstate = models.IntegerField('在职状态', choices=jobstate_choices, default=1)
job = models.ForeignKey(Position, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='岗位')
birthday = models.DateField('出生年月', null=True, blank=True)
qualification = models.CharField('学历', max_length=50, null=True, blank=True)
job_state = models.IntegerField('在职状态', choices=jobstate_choices, default=1)
face_data = models.JSONField('人脸识别数据', null=True, blank=True)
class Meta:
verbose_name = '员工补充信息'

View File

@ -1,11 +1,15 @@
from apps.system.models import User
from rest_framework.serializers import ModelSerializer
from rest_framework import serializers
from utils.mixins import DynamicFieldsSerializerMixin
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
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:
model = Employee
exclude = ['face_data']

View File

@ -28,8 +28,11 @@ class HRMService:
face_users = cache.get('face_users')
if face_datas is None:
update_all_user_facedata_cache()
results = face_recognition.compare_faces(face_datas, unknown_face_encoding, tolerance=0.5)
try:
results = face_recognition.compare_faces(face_datas,
unknown_face_encoding, tolerance=0.5)
except:
return None, '识别失败'
for index, value in enumerate(results):
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
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.viewsets import ModelViewSet, GenericViewSet
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.tasks import update_all_user_facedata_cache
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
@ -30,6 +30,7 @@ class EmployeeViewSet(CreateUpdateModelAMixin, OptimizationMixin, UpdateModelMix
"""
perms_map = {'get': '*', 'put': 'employee_update'}
queryset = Employee.objects.all()
filterset_class = EmployeeFilterSet
serializer_class = EmployeeSerializer
ordering = ['-pk']
@ -91,6 +92,7 @@ class ClockRecordViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
ins.save()
# 设为在岗
user.is_atwork = True
user.last_check_time = now
user.save()
return Response(UserSimpleSerializer(instance=user).data)
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 .models import IProduct, MaterialBatch
from django.utils import timezone
from utils.mixins import DynamicFieldsFilterMixin
class MbFilterSet(filters.FilterSet):
class MbFilterSet(DynamicFieldsFilterMixin, filters.FilterSet):
material = filters.ModelMultipleChoiceFilter(field_name="material", queryset=Material.objects.all())
tag = filters.CharFilter(method="filter_tag")
@ -20,12 +20,14 @@ class MbFilterSet(filters.FilterSet):
return queryset
class IProductFilterSet(filters.FilterSet):
class IProductFilterSet(DynamicFieldsFilterMixin, filters.FilterSet):
order = filters.NumberFilter(field_name="wproduct__subproduction_plan__production_plan__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_end = filters.DateFilter(field_name="update_time", lookup_expr='lte')
class Meta:
model = IProduct
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.qm.models import TestRecord, TestRecordItem
from apps.sam.serializers import OrderSimpleSerializer
from apps.system.serializers import FileSimpleSerializer, UserSimpleSerializer
from apps.mtm.serializers import MaterialSimpleSerializer
from django.db import transaction
from utils.mixins import DynamicFieldsSerializerMixin
@ -39,7 +42,7 @@ class InventorySerializer(serializers.ModelSerializer):
fields = '__all__'
class MaterialBatchSerializer(serializers.ModelSerializer):
class MaterialBatchSerializer(DynamicFieldsSerializerMixin, serializers.ModelSerializer):
material_ = MaterialSimpleSerializer(source='material', read_only=True)
warehouse_ = WareHouseSimpleSerializer(source='warehouse', read_only=True)
@ -48,12 +51,15 @@ class MaterialBatchSerializer(serializers.ModelSerializer):
fields = '__all__'
class IProductListSerializer(serializers.ModelSerializer):
class IProductListSerializer(DynamicFieldsSerializerMixin, serializers.ModelSerializer):
material_ = MaterialSimpleSerializer(source='material', 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)
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:
model = IProduct
fields = '__all__'

View File

@ -187,7 +187,9 @@ class IProductViewSet(ListModelMixin, GenericViewSet):
"""
perms_map = {'*': '*'}
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
filterset_class = IProductFilterSet
search_fields = []

View File

@ -1,6 +1,7 @@
from django.db.models.expressions import F
from django_filters import rest_framework as filters
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):
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')
class Meta:
model = Material

View File

@ -1,11 +1,13 @@
from apps.em.serializers import EquipmentSimpleSerializer
from rest_framework import serializers
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 apps.system.serializers import FileSimpleSerializer, OrganizationSimpleSerializer
class MaterialSerializer(serializers.ModelSerializer):
class MaterialSerializer(DynamicFieldsSerializerMixin, serializers.ModelSerializer):
class Meta:
model = Material
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.pm.models import ProductionPlan, SubProductionProgress
from apps.wpm.models import Operation, WProduct
from datetime import *
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_end = filters.DateFilter(field_name="create_time", lookup_expr='lte')
tag = filters.CharFilter(method='filter_tag')
@ -15,10 +18,19 @@ class PlanFilterSet(filters.FilterSet):
fields = ['product', 'order', 'create_time_start', 'create_time_end']
def filter_tag(self, queryset, name, value):
now = datetime.now()
day7_after = now + timedelta(days=7)
if value == 'planed':
queryset = queryset.filter(is_planed=True)
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
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.mtm.serializers import MaterialSimpleSerializer, ProcessSimpleSerializer, SubProductionSimpleSerializer
from apps.system.serializers import OrganizationSimpleSerializer
from utils.mixins import DynamicFieldsSerializerMixin
class ProductionPlanCreateFromOrderSerializer(serializers.ModelSerializer):
@ -10,7 +11,7 @@ class ProductionPlanCreateFromOrderSerializer(serializers.ModelSerializer):
model = ProductionPlan
fields = ['order', 'count', 'start_date', 'end_date']
class ProductionPlanSerializer(serializers.ModelSerializer):
class ProductionPlanSerializer(DynamicFieldsSerializerMixin, serializers.ModelSerializer):
order_ = OrderSimpleSerializer(source='order', read_only=True)
product_ = MaterialSimpleSerializer(source='product', read_only=True)
class Meta:

View File

@ -7,7 +7,7 @@ from apps.em.models import Equipment
from apps.em.serializers import EquipmentSimpleSerializer
from apps.inm.models import MaterialBatch
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.system.mixins import CreateUpdateModelAMixin
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:
# 计算输入物料
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')\
.values('material__id', 'material__name', 'material__number', 'material__type', 'count', 'material__count')
subproduction__is_deleted=False, is_deleted=False,
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)
for m in l_m:
if m['material__id'] in res_d_list:
@ -231,7 +235,8 @@ class ResourceViewSet(GenericViewSet):
res_d_list.append(m['material__id'])
res.append({'id':m['material__id'], 'name':m['material__name'],
'type':m['material__type'], 'number':m['material__number'],
'count':m['count']*i['count'], 'inv_count':m['material__count']})
'count':m['count']*i['count'], 'inv_count':m['material__count'],
'count_safe':m['material__count_safe']})
return Response(res)
@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 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_end = filters.DateFilter(field_name="create_time", lookup_expr='lte')
material = filters.NumberFilter(method='filter_material')
@ -23,13 +25,16 @@ class OrderFilterSet(filters.FilterSet):
plan_order__subplan_plan__progress_subplan__material__id=value).distinct()
def filter_tag(self, queryset, name, value):
now = datetime.now()
day7_after = now + timedelta(days=7)
if value == 'near_delivery':
day7_after = datetime.now() + timedelta(days=7)
queryset = queryset.filter(delivered_count__lt=F('count'),
delivery_date__lte = datetime.date(day7_after))
elif value == 'out_delivery':
queryset = queryset.filter(delivered_count__lt=F('count'),
delivery_date__gt = datetime.date(datetime.now()))
elif value == 'not_done':
queryset = queryset.filter(delivered_count__lt=F('count'))
return queryset
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)
number = models.CharField('合同编号', max_length=100, unique=True)
amount = 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')
sign_date = models.DateField('签订日期')
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 apps.inm.serializers import IProductListSerializer
from rest_framework import serializers
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 utils.tools import ranstr
@ -44,13 +43,13 @@ class ContractCreateUpdateSerializer(serializers.ModelSerializer):
class OrderCreateUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ['customer', 'contract', 'product', 'count', 'delivery_date']
fields = ['customer', 'contract', 'product', 'count', 'delivery_date', 'need_mtest']
def create(self, validated_data):
validated_data['number'] = 'DD' + ranstr(7)
return super().create(validated_data)
class OrderSerializer(serializers.ModelSerializer):
class OrderSerializer(DynamicFieldsSerializerMixin, serializers.ModelSerializer):
contract_ = ContractSimpleSerializer(source='contract', read_only=True)
customer_ = CustomerSimpleSerializer(source='customer', 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)
class Meta:
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 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 rest_framework.routers import DefaultRouter
from apps.sam.views_sale import SaleProductViewSet, SaleViewSet
router = DefaultRouter()
router.register('customer', CustomerViewSet, basename='customer')
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.serializers import ContractCreateUpdateSerializer, ContractSerializer, CustomerCreateUpdateSerializer, CustomerSerializer, OrderCreateUpdateSerializer, OrderSerializer, SaleCreateSerializer, SaleListSerializer, SaleProductCreateSerializer, SaleProductListSerializer
from apps.sam.models import Contract, Customer, Order, Sale, SaleProduct
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from apps.sam.serializers import ContractCreateUpdateSerializer, ContractSerializer, \
CustomerCreateUpdateSerializer, CustomerSerializer, OrderCreateUpdateSerializer, OrderSerializer
from apps.sam.models import Contract, Customer, Order
from rest_framework.viewsets import ModelViewSet
from apps.system.mixins import CreateUpdateCustomMixin
from django.shortcuts import render
from rest_framework.decorators import action
from django.db.models import F
from rest_framework.response import Response
from django.utils import timezone
from apps.wf.models import Workflow
from rest_framework.decorators import action
# Create your views here.
class CustomerViewSet(CreateUpdateCustomMixin, ModelViewSet):
"""
@ -81,129 +71,6 @@ class OrderViewSet(CreateUpdateCustomMixin, ModelViewSet):
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='上级主管')
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:
verbose_name = '用户信息'

View File

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

View File

@ -41,7 +41,7 @@ class WProductFilterSet(DynamicFieldsFilterMixin, filters.FilterSet):
class Meta:
model = WProduct
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):
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='当前工单',
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)
is_mtestok = models.BooleanField('是否军检合格', 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)
ticket = models.ForeignKey('wf.ticket', verbose_name='当前工单',
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)
is_mtestok = models.BooleanField('是否军检合格', null=True, blank=True)
remark_mtest = models.TextField('军检备注', null=True, blank=True)

View File

@ -574,4 +574,7 @@ class WproductMtestSerializer(serializers.ModelSerializer):
class WproductToOrderSerializer(serializers.Serializer):
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, \
PickSerializer, OperationInitSerializer, OperationSubmitSerializer, ScrapSerializer, WMaterialListSerializer, \
WProductCardSerializer, WProductDetailSerializer, WProductListSerializer, \
WpmTestFormInitSerializer, WproductMtestSerializer, WproductPutInSerializer, \
WpmTestFormInitSerializer, WproductMtestSerializer, WproductNeedToOrderSerializer, WproductPutInSerializer, \
WproductPutInsSerializer, WproductTicketListSerializer, WproductToOrderSerializer
from rest_framework.response import Response
@ -399,7 +399,7 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
军检
"""
obj = self.get_object()
if obj.is_mtestok is None:
if obj.is_mtestok is not None:
raise exceptions.APIException('已进行军检')
if obj.material.type != Material.MA_TYPE_GOOD:
raise exceptions.APIException('军检必须是成品')
@ -409,6 +409,7 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
if is_mtestok:
WpmServies.update_plan_state_by_mtestok(
obj.subproduction_plan.production_plan)
obj.update_by = request.user
obj.save()
change_str = 'mtest_notok'
if is_mtestok:
@ -441,6 +442,24 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
ret.append([str(index + 1), item['step_name'], item['actions']])
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)
@transaction.atomic
def to_order(self, request, pk=None):

View File

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