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

This commit is contained in:
shilixia 2022-02-24 08:32:48 +08:00
commit 0e2cc60409
23 changed files with 1097 additions and 105 deletions

View File

@ -105,6 +105,23 @@ export function createConvert(data) {
data data
}) })
} }
//首件检查表初始化
export function firstTestInit(id,data) {
return request({
url: `/pm/subproduction_plan/${id}/first_test_init/`,
method: 'post',
data
})
}
//首件检查责任人审核
export function firstAudit(id,data) {
return request({
url: `/pm/subproduction_plan/${id}/first_audit/`,
method: 'post',
data
})
}
//任务终止 //任务终止
export function planstop(id) { export function planstop(id) {
return request({ return request({

Binary file not shown.

Before

Width:  |  Height:  |  Size: 813 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 KiB

View File

@ -239,13 +239,15 @@
let imag= this.formData.filter(item => { let imag= this.formData.filter(item => {
return item.field_type === 'draw'; return item.field_type === 'draw';
}); });
that.img = new Image(); if(imag.length>0){
that.img.crossOrigin = 'anonymous'; that.img = new Image();
let value = imag[0].field_value?imag[0].field_value:imag[0].draw_template; that.img.crossOrigin = 'anonymous';
that.img = 'http://47.95.0.242:2222'+value; let value = imag[0].field_value?imag[0].field_value:imag[0].draw_template;
setTimeout(function(){ that.img = 'http://47.95.0.242:2222'+value;
that.canvasInit(); setTimeout(function(){
},500); that.canvasInit();
},500);
}
}, },
data(){ data(){
return{ return{
@ -695,7 +697,7 @@
that.field = []; //检查项目 that.field = []; //检查项目
let submit = isSubmit=='1'?false:true; let submit = isSubmit=='1'?false:true;
that.formData.forEach((item) => { that.formData.forEach((item) => {
let field_value = null; let field_value;
if(item.field_type==='int'){ if(item.field_type==='int'){
field_value = parseInt(that.checkForm[item.field_key]) field_value = parseInt(that.checkForm[item.field_key])
}else if(item.field_type==='float'){ }else if(item.field_type==='float'){

View File

@ -0,0 +1,190 @@
<template>
<div class="faceLoginWrap">
<div style="height: 500px;">
<div class="video-box">
<video id="video" width="500" height="500" preload autoplay loop muted></video>
<canvas id="canvas" width="500" height="500"></canvas>
</div>
<canvas id="screenshotCanvas" width="500" height="500"></canvas>
</div>
</div>
</template>
<script>
import tracking from '@/assets/tracking/build/tracking-min.js';
import {faceLogin} from "@/api/testModel";
import '@/assets/tracking/build/data/face-min.js';
import {createrecordform,updaterecordform} from "@/api/mtm";
export default {
props: {
dialogType: {
type:String,
default:"new"
},
recordform: {
type:Object,
default:null
}
},
data() {
return {
video: null,
screenshotCanvas: null,
uploadLock: false // 上传锁
}
},
mounted() {
this.openTheCamera();
},
methods: {
openTheCamera () {
this.$nextTick(function () {
let _this = this;
this.video = document.getElementById('video');
// 旧版本浏览器可能根本不支持mediaDevices我们首先设置一个空对象
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {}
}
// 一些浏览器实现了部分mediaDevices我们不能只分配一个对象
// 使用getUserMedia因为它会覆盖现有的属性
// 这里如果缺少getUserMedia属性就添加它
if (navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = function (constraints) {
// 首先获取现存的getUserMedia(如果存在)
let getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.getUserMedia;
// 有些浏览器不支持会返回错误信息
// 保持接口一致
if (!getUserMedia) {
return Promise.reject(new Error('getUserMedia is not implemented in this browser'))
}
// 否则使用Promise将调用包装到旧的navigator.getUserMedia
return new Promise(function (resolve, reject) {
getUserMedia.call(navigator, constraints, resolve, reject)
})
}
}
let constraints = { audio: false, video: { width: this.videoWidth, height: this.videoHeight, transform: 'scaleX(-1)' } };
navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
// 旧的浏览器可能没有srcObject
if ('srcObject' in _this.video) {
_this.video.srcObject = stream
} else {
// 避免在新的浏览器中使用它因为它正在被弃用
_this.video.src = window.URL.createObjectURL(stream)
}
_this.video.onloadedmetadata = function (e) {
_this.video.play();
};
_this.init();
}).catch(err => {
console.log(err)
})
});
},
// 初始化设置
init() {
// this.video = document.getElementById('video');
this.screenshotCanvas = document.getElementById('screenshotCanvas');
let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');
// 实例化tracker对象
let tracker = new window.tracking.ObjectTracker('face');
tracker.setInitialScale(4);
tracker.setStepSize(2);
tracker.setEdgesDensity(0.1);
//tracker对象和标签关联
window.tracking.track('#video', tracker, {
camera: true
});
let _this = this;
//添加事件
tracker.on('track', function (event) {
// 检测出人脸 绘画人脸位置
context.clearRect(0, 0, canvas.width, canvas.height);
// 给每个人脸绘制对应的框
event.data.forEach(function (rect) {
context.strokeStyle = '#0764B7';
context.strokeRect(rect.x, rect.y, rect.width, rect.height);
// 避免重复发送请求
if(!_this.uploadLock){
_this.uploadLock = true;
// 上传图片
_this.screenshotAndUpload();
}
});
});
},
// 上传图片
screenshotAndUpload() {
let that = this;
// 绘制当前帧图片转换为base64格式
let canvas = this.screenshotCanvas;
let video = this.video;
let ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
let base64Img = canvas.toDataURL('image/jpeg');
let img = base64Img.split(',')[1];
let imgData = {base64:img};
faceLogin(imgData).then((res) => {
if (res.code >= 200) {
let item= {name:res.data.username,token:res.data.access};
that.$emit('func',item);
that.$message.success("身份认证成功!");
}else{
// 打开锁
that.uploadLock = false;
this.$message.error(res.msg);
}
}).catch(()=>{
// 打开锁
that.uploadLock = false;
// this.$message.error('面部识别失败请重新验证');
});
},
closeCamera () {
this.video.srcObject.getTracks()[0].stop();
},
}
}
</script>
<style scoped>
.faceLoginWrap{
padding: 50px;
width: 600px;
height: 600px;
background: #000000;
margin:50px auto 0 auto;
box-sizing: border-box;
}
#screenshotCanvas {
display: none;
}
.video-box {
margin: auto;
position: relative;
width: 500px;
height: 500px;
background: #000000;
text-align: center;
}
#video {
object-fit: fill;
-webkit-border-radius: 250px;
-moz-border-radius: 250px;
border-radius: 250px;
}
video, canvas {
position: absolute;
top: 0;
left: 0;
}
</style>

View File

@ -1,16 +1,16 @@
<template> <template>
<div style="width: 100%;height: 620px;"> <div class="faceLoginWrap">
<div> <div style="height: 500px;">
<div class="video-box"> <div class="video-box">
<video id="video" width="749" height="620" preload autoplay loop muted></video> <video id="video" width="500" height="500" preload autoplay loop muted></video>
<canvas id="canvas" width="749" height="620"></canvas> <canvas id="canvas" width="500" height="500"></canvas>
</div> </div>
<canvas id="screenshotCanvas" width="749" height="620"></canvas> <canvas id="screenshotCanvas" width="500" height="500"></canvas>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { clockRecord } from '@/api/hrm' import {clockRecord} from '@/api/hrm'
import tracking from '@/assets/tracking/build/tracking-min.js'; import tracking from '@/assets/tracking/build/tracking-min.js';
import '@/assets/tracking/build/data/face-min.js'; import '@/assets/tracking/build/data/face-min.js';
@ -26,7 +26,7 @@
this.openTheCamera(); this.openTheCamera();
}, },
methods: { methods: {
openTheCamera () { openTheCamera() {
this.$nextTick(function () { this.$nextTick(function () {
let _this = this; let _this = this;
this.video = document.getElementById('video'); this.video = document.getElementById('video');
@ -52,7 +52,10 @@
}) })
} }
} }
let constraints = { audio: false, video: { width: this.videoWidth, height: this.videoHeight, transform: 'scaleX(-1)' } }; let constraints = {
audio: false,
video: {width: this.videoWidth, height: this.videoHeight, transform: 'scaleX(-1)'}
};
navigator.mediaDevices.getUserMedia(constraints).then(function (stream) { navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
// 旧的浏览器可能没有srcObject // 旧的浏览器可能没有srcObject
if ('srcObject' in _this.video) { if ('srcObject' in _this.video) {
@ -96,7 +99,7 @@
context.strokeRect(rect.x, rect.y, rect.width, rect.height); context.strokeRect(rect.x, rect.y, rect.width, rect.height);
// window.plot(rect.x, rect.y, rect.width, rect.height+20); // window.plot(rect.x, rect.y, rect.width, rect.height+20);
// 避免重复发送请求 // 避免重复发送请求
if(!_this.uploadLock){ if (!_this.uploadLock) {
_this.uploadLock = true; _this.uploadLock = true;
// 上传图片 // 上传图片
_this.screenshotAndUpload(); _this.screenshotAndUpload();
@ -115,31 +118,40 @@
ctx.drawImage(video, 0, 0, canvas.width, canvas.height); ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
let base64Img = canvas.toDataURL('image/jpeg'); let base64Img = canvas.toDataURL('image/jpeg');
let img = base64Img.split(',')[1]; let img = base64Img.split(',')[1];
let imgData = {base64:img}; let imgData = {base64: img};
clockRecord(imgData).then((res) => { clockRecord(imgData).then((res) => {
if (res.code === 200&&res.data.id) { if (res.code === 200 && res.data.id) {
this.$message.success(res.data.name+'签到成功!'); this.$message.success(res.data.name + '签到成功!');
setTimeout(()=>{ setTimeout(() => {
that.uploadLock = false; that.uploadLock = false;
},3000) }, 3000)
}else{ } else {
// 打开锁 // 打开锁
that.uploadLock = false; that.uploadLock = false;
this.$message.error(res.msg); this.$message.error(res.msg);
} }
}).catch(()=>{ }).catch(() => {
// 打开锁 // 打开锁
that.uploadLock = false; that.uploadLock = false;
// this.$message.error('面部识别失败请重新验证'); // this.$message.error('面部识别失败请重新验证');
}); });
}, },
closeCamera () { closeCamera() {
this.video.srcObject.getTracks()[0].stop(); this.video.srcObject.getTracks()[0].stop();
}, },
} }
} }
</script> </script>
<style scoped> <style scoped>
.faceLoginWrap{
padding: 50px;
width: 600px;
height: 600px;
background: #000000;
margin:50px auto 0 auto;
box-sizing: border-box;
}
#screenshotCanvas { #screenshotCanvas {
display: none; display: none;
} }
@ -147,8 +159,13 @@
.video-box { .video-box {
margin: auto; margin: auto;
position: relative; position: relative;
/*width: 90%; }
height: 90%;*/
#video {
object-fit: fill;
-webkit-border-radius: 250px;
-moz-border-radius: 250px;
border-radius: 250px;
} }
video, canvas { video, canvas {

View File

@ -226,8 +226,7 @@ export const asyncRoutes = [
name: 'operation', name: 'operation',
component: () => import('@/views/wpm/operation'), component: () => import('@/views/wpm/operation'),
meta: { title: '车间操作', icon: 'workshopOperation', perms: ['index_manage'] } meta: { title: '车间操作', icon: 'workshopOperation', perms: ['index_manage'] }
} },
,
{ {
path: 'operationdo/:id', path: 'operationdo/:id',
name: 'operationdo', name: 'operationdo',
@ -240,13 +239,18 @@ export const asyncRoutes = [
name: 'need', name: 'need',
component: () => import('@/views/wpm/need'), component: () => import('@/views/wpm/need'),
meta: { title: '过程检验', icon: 'processTest', perms: ['index_manage'] } meta: { title: '过程检验', icon: 'processTest', perms: ['index_manage'] }
} },
,
{ {
path: 'productjy', path: 'productjy',
name: 'productjy', name: 'productjy',
component: () => import('@/views/wpm/productjy'), component: () => import('@/views/wpm/productjy'),
meta: { title: '成品检验', icon: 'finishedCheck', perms: ['index_manage'] } meta: { title: '成品检验', icon: 'finishedCheck', perms: ['index_manage'] }
},
{
path: 'firstCheck',
name: 'firstCheck',
component: () => import('@/views/wpm/firstCheck'),
meta: { title: '首件确认', icon: 'finishedCheck', perms: ['index_manage'] }
} }
] ]

View File

@ -20,7 +20,7 @@ service.interceptors.request.use(
// please modify it according to the actual situation // please modify it according to the actual situation
config.headers['Authorization'] = 'Bearer ' + getToken() config.headers['Authorization'] = 'Bearer ' + getToken()
} }
let data = config.params; let data = config.data;
/*debugger; /*debugger;
console.log(data)*/ console.log(data)*/
if(data){ if(data){

View File

@ -1,8 +1,8 @@
<template> <template>
<div class="login"> <div class="login">
<div class="login-form" style="display: flex;justify-content: space-between"> <div class="login-form">
<div style="width: 250px;border-right: 1px dashed #409EFF;"> <div class="faceLoginBtnWrap">
<img src="./../../assets/face.png" style="margin-top: 65px;" @click="takePhoto()"> <img class="faceLoginBtn" src="./../../assets/face.png" @click="takePhoto()">
</div> </div>
<div style="width: 360px;"> <div style="width: 360px;">
<h3 class="title">航玻生产管理系统</h3> <h3 class="title">航玻生产管理系统</h3>
@ -245,7 +245,7 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
height: 100%; height: 100%;
background-image: url("../../assets/beijing.jpg"); background-image: url("../../assets/bg-login.png");
background-size: cover; background-size: cover;
} }
.title { .title {
@ -263,6 +263,10 @@
background: #ffffff; background: #ffffff;
width: 700px; width: 700px;
padding: 25px; padding: 25px;
display: flex;
justify-content: space-between;
box-shadow: 0 0 10px 5px #eeeeee;
.el-input { .el-input {
height: 45px; height: 45px;
input { input {
@ -275,6 +279,15 @@
margin-left: 2px; margin-left: 2px;
} }
} }
.faceLoginBtnWrap{
width: 250px;
border-right: 1px dashed #409EFF;
text-align: center;
}
.faceLoginBtn{
width: 150px;
margin-top: 90px;
}
#passwordInput{ #passwordInput{
padding-right: 35px; padding-right: 35px;
} }
@ -309,6 +322,6 @@
} }
.testTracking{ .testTracking{
width:100%; width:100%;
height: 620px; height: 500px;
} }
</style> </style>

View File

@ -0,0 +1,565 @@
<template>
<div class="app-container">
<el-card>
<div class="tabButtonWrap">
<div
v-for="(item,$index) in processOption"
:key="item.id"
class="tabButton"
:class="{activeTab:activeIndex===$index}"
@click="changeIndex(item,$index)"
>
{{item.name}}
</div>
</div>
<el-table
:data="subPlanList"
fit
style="width: 100%"
height="100"
stripe
border
v-el-height-adaptive-table="{bottomOffset: 50}"
>
<el-table-column type="index" width="50"/>
<el-table-column label="子计划编号" min-width="70px" show-overflow-tooltip>
<template slot-scope="scope">{{scope.row.number}}</template>
</el-table-column>
<el-table-column label="产品名称" min-width="120px" show-overflow-tooltip>
<template slot-scope="scope">{{ scope.row.product_.name }}</template>
</el-table-column>
<el-table-column label="产品型号">
<template slot-scope="scope">{{ scope.row.product_.specification }}</template>
</el-table-column>
<el-table-column label="子工序" min-width="120px" show-overflow-tooltip>
<template slot-scope="scope" v-if="scope.row.steps">
<el-tag v-for="item in scope.row.steps"
:key="item.number"
:label="item.name"
:value="item.number">{{item.name}}
</el-tag>
</template>
</el-table-column>
<el-table-column label="生产车间">
<template slot-scope="scope">{{ scope.row.workshop_.name }}</template>
</el-table-column>
<el-table-column label="生产数量" prop="count">
</el-table-column>
<el-table-column label="开工时间" prop="start_date">
</el-table-column>
<el-table-column label="完工时间" prop="end_date">
</el-table-column>
<el-table-column label="检验状态">
<template slot-scope="scope">
<span v-if="scope.row.leader_1!==null&&scope.row.leader_2!==null&&scope.row.leader_3!==null">已完成</span>
<span v-else>未完成</span>
</template>
</el-table-column>
<el-table-column
align="center"
label="操作"
width="120px"
fixed="right"
>
<template slot-scope="scope">
<el-link
v-if="scope.row.first_test===null"
type="primary"
@click="handleTest(scope)"
>
首件检验
</el-link>
<el-link
v-else-if="scope.row.first_test!==null&&!scope.row.first_test_.is_submited"
type="primary"
@click="handleTestContinue(scope)"
>
检验
</el-link>
<el-link
v-else-if="scope.row.first_test_.is_submited&&(scope.row.leader_1===null||scope.row.leader_2===null||scope.row.leader_3===null)"
type="primary"
@click="handleSelectclick(scope,'0')"
>
首件审批
</el-link>
<el-link
v-else-if="scope.row.leader_1!==null&&scope.row.leader_2!==null&&scope.row.leader_3!==null"
type="primary"
@click="handleSelectclick(scope,'1')"
>
查看
</el-link>
</template>
</el-table-column>
</el-table>
<pagination
:total="count"
:page.sync="listQuery.page"
:limit.sync="listQuery.page_size"
@pagination="getsList"
/>
</el-card>
<!--物料检查表&&-->
<el-dialog title="首件确认检查表" :close-on-click-modal="false" :visible.sync="listVisible">
<el-select style="width: 100%" v-model="recordForm" placeholder="请选择" @change="recordFormChange">
<el-option
v-for="item in recordFormList"
:key="item.name"
:label="item.name"
:value="item.id"
>
</el-option>
</el-select>
<div slot="footer" class="dialog-footer">
<el-button @click="listVisible = false">
</el-button>
<el-button type="primary" @click="selectedRecordForm()">填写检查项目</el-button>
</div>
</el-dialog>
<!--检查表显示-->
<el-dialog
width="60%"
:title="formName"
:visible.sync="recordVisible"
:close-on-click-modal="false"
@close="recordCancel"
>
<customForm
v-if="recordVisible"
:results="fieldList"
:hasPicture="hasPicture"
:formID="recordForm"
:wproduct="recordFormQuery.material"
:recordId="recordId"
:isDisabled="isDisabled"
:isMidTesting="is_midtesting"
@recordSubmit="recordSubmit"
@recordSave="recordSave"
@recordCancel="recordCancel"
/>
</el-dialog>
<!--已完成检查表查后的审核-->
<el-dialog
title="首件确认审核"
:visible.sync="reviewVisible"
:close-on-click-modal="false"
>
<el-row>
<el-col v-for="item in fieldList" :key="item.id+item.id" :span="12">
<div class="items" v-if="item.field_type!=='draw'&&item.field_value!==null&&item.field_value!==''">
<span class="itemLabel">{{item.field_name}}</span>
<span>{{item.field_value}}</span>
</div>
</el-col>
<el-col :span="12" v-if="achieve">
<div class="items">
<span class="itemLabel">工序负责人</span>
<span>{{leader_1}}</span>
</div>
</el-col>
<el-col :span="12" v-if="achieve">
<div class="items">
<span class="itemLabel">技术负责人</span>
<span>{{leader_2}}</span>
</div>
</el-col>
<el-col :span="12" v-if="achieve">
<div class="items">
<span class="itemLabel">总检</span>
<span>{{leader_3}}</span>
</div>
</el-col>
<el-col :span="12" v-if="achieve">
<div class="items">
<span class="itemLabel">首件检查时间</span>
<span>{{update_time}}</span>
</div>
</el-col>
<el-col :span="12" v-if="achieve">
<div class="items">
<span class="itemLabel">首件审批时间</span>
<span>{{first_sign_time}}</span>
</div>
</el-col>
<el-col v-for="item in fieldList" :key="item.id" :span="24">
<div class="items" v-if="item.field_type==='draw'" style="height: 400px">
<span class="itemLabel">{{item.field_name}}</span>
<img style="width: 45%;vertical-align: text-top;" :src="'http://47.95.0.242:2222'+item.field_value"/>
</div>
</el-col>
</el-row>
<el-row class="reviewWrap" v-if="!achieve">
<div class="reviewBlock">
<span>工序负责人:</span>
<div class="directorName">{{leader_1}}</div>
<span class="reviewBtn" @click="directorConfirm('leader_1')">人脸验证</span>
</div>
<div class="reviewBlock">
<span>技术负责人:</span>
<div class="directorName">{{leader_2}}</div>
<span class="reviewBtn" @click="directorConfirm('leader_2')">人脸验证</span>
</div>
<div class="reviewBlock">
<span>总检:</span>
<div class="directorName">{{leader_3}}</div>
<span class="reviewBtn" @click="directorConfirm('leader_3')">人脸验证</span>
</div>
</el-row>
</el-dialog>
<el-dialog :visible.sync="limitedPhoto" @close="closeCamera" id="loginFaceWrap">
<div style="font-size: 28px;color: #333333;text-align: center;font-weight: bold;">审核人员确认</div>
<div class="testTracking">
<faceLogin v-if="limitedPhoto" ref="faceTracking" name="faceLogin" @func="getMsgFormSon"></faceLogin>
</div>
</el-dialog>
</div>
</template>
<script>
import checkPermission from "@/utils/permission";
import Pagination from "@/components/Pagination";
import customForm from '@/components/customForm/index';
import faceLogin from '@/components/faceLogin/review.vue';
import {getProcessList,getrecordformList} from "@/api/mtm";
import {getsubproductionplanList,firstTestInit,firstAudit} from "@/api/pm";
import {getTestRecordItem,putTestRecordItem,subTestRecordItem} from "@/api/qm";
export default {
name: "firstCheck",
components: {Pagination,faceLogin,customForm},
data() {
return {
count: 0,
activeIndex: 0,
fieldList: [],
subPlanList: [],
processOption: [],
recordFormList: [],
listQuery:{
page:1,
page_size:20,
process: null,
},
recordFormQuery:{
type:50,
enabled:true,
material:null
},
planId:null,
leader:null,
recordId: null,
leader_1: null,
leader_2: null,
leader_3: null,
recordForm:null,
achieve:false,
hasPicture:false,
isDisabled: false,
listLoading:false,
listVisible:false,
limitedPhoto:false,
reviewVisible:false,
recordVisible:false,
is_midtesting:false,
formName:'首件确认检查表',
update_time:'',
first_sign_time:'',
}
},
methods: {
checkPermission,
//工序渲染
getProcessList() {
let that = this;
getProcessList({page: 0}).then((response) => {
if (response.data) {
that.processOption = response.data;
that.listQuery.process = response.data[0].id;
that.getTableData();
}
});
},
//工序转换
changeIndex(item,index) {
this.activeIndex = index;
this.listQuery.process = item.id;
this.getTableData();
},
//获取table数据
getTableData() {
this.listLoading = true;
this.listQuery.production_plan = this.id;
getsubproductionplanList(this.listQuery).then((response) => {
if (response.data) {
this.subPlanList = response.data.results;
this.count = response.data.count;
}
this.listLoading = false;
});
},
//分页
getsList(){},
//第一次点击首件检验
handleTest(scope){
let that = this;
that.planId = scope.row.id;//子生产计划ID
that.recordFormQuery.material = scope.row.product;
that.recordForm = null;
getrecordformList(that.recordFormQuery).then((response) => {
if (response.data) {
that.recordformList = response.data.results;
if (that.recordformList.length === 1) {
that.recordForm = that.recordformList[0].id;
that.formName = that.recordformList[0].name;
that.selectedRecordForm();
} else {
//弹出列表选择框
that.listVisible = true;
}
}
});
},
//首件审批
handleSelectclick(scope,index){
let that = this;
this.reviewVisible = true;
that.planId = scope.row.id;
that.leader_1 = scope.row.leader_1_?scope.row.leader_1_.name:null;
that.leader_2 = scope.row.leader_2_?scope.row.leader_2_.name:null;
that.leader_3 = scope.row.leader_3_?scope.row.leader_3_.name:null;
that.first_sign_time = scope.row.first_sign_time?scope.row.first_sign_time:'';
getTestRecordItem(scope.row.first_test).then((res) => {
that.formName = res.data.form_.name;
that.update_time = res.data.update_time;
let fieldList = res.data.record_data;
that.fieldList = [...fieldList];
let arr = fieldList.filter(item => {
return item.field_type === 'draw'
});
if (arr.length > 0) {
that.hasPicture = true;
}
that.$nextTick(() => {
if(index==='1'){
this.achieve = true;
}else{
this.achieve = false;
}
that.reviewVisible = true;
});
})
},
//选择物料检查表
recordFormChange() {
let that = this;
let arr = this.recordFormList.filter(item => {
return item.id === that.recordForm;
});
that.formName = arr[0].name;
},
//根据选择的表渲染检查项目
selectedRecordForm() {
let that = this;
this.listVisible = false;
that.fieldList = [];
if (that.recordForm != "") {
firstTestInit(that.planId,{ form: that.recordForm}).then((response) => {
if (response.code===200) {
that.hasPicture = false;
that.recordId = response.data.id;//记录id
that.formName = response.data.form_.name;//引用的检查表id
let fieldList = response.data.record_data;
that.fieldList = [...fieldList];
let arr = fieldList.filter(item => {
return item.field_type === 'draw'
});
if (arr.length > 0) {
that.hasPicture = true;
}
that.$nextTick(() => {
that.recordVisible = true;
});
}
});
} else this.$message.error("请选择检查表!");
},
//第一次保存提交检查项目
recordCancel() {
this.recordVisible = false;
this.listVisible = false;
this.getTableData();
},
/*关闭相机*/
closeCamera () {
this.$refs.faceTracking.closeCamera();
// this.thisVideo.srcObject.getTracks()[0].stop();
},
//保存首件检查
recordSave(value) {
let that = this;
let id = value.id;
let params = {};
params.record_data = value.record_data;
params.is_testok = value.is_testok;
putTestRecordItem(id, params).then((res) => {
if (res.code >= 200) {
that.recordVisible = false;
that.getTableData();
} else {
that.$message.error(res.msg)
}
}).catch((err) => {
// console.error(err);
that.$message.error(err)
});
},
//提交首件检查
recordSubmit(value) {
let that = this;
let id = value.id;
let params = {};
params.record_data = value.record_data;
params.is_testok = value.is_testok;
putTestRecordItem(id, params).then((res) => {
if (res.code >= 200) {
subTestRecordItem(id, params).then((res) => {
if (res.code >= 200) {
that.recordVisible = false;
that.getTableData();
}
});
} else {
that.$message.error(res.msg)
}
}).catch((err) => {
that.$message.error(err);
});
},
//再次点击首件检验
handleTestContinue(scope) {
let that = this;
that.fieldList = [];
that.recordVisible = false;
that.planId = scope.row.id;
that.recordId = scope.row.first_test;
that.recordForm = scope.row.first_test_.form;
getTestRecordItem(scope.row.first_test).then((res) => {
that.formName = res.data.form_.name;
let fieldList = res.data.record_data;
that.fieldList = [...fieldList];
let arr = fieldList.filter(item => {
return item.field_type === 'draw'
});
if (arr.length > 0) {
that.hasPicture = true;
}
that.$nextTick(() => {
that.recordVisible = true;
});
})
/*getrffieldList({form: this.recordform,enabled:true, page: 1, page_size: 100}).then((response) => {
if (response.data) {
that.hasPicture = false;
let fieldList = response.data.results;
that.fieldList = [...fieldList];
let arr = fieldList.filter(item => {
return item.field_type === 'draw'
});
if (arr.length > 0) {
that.hasPicture = true;
}
}
});*/
},
//点击人脸验证
directorConfirm(index){
this.leader = index;
this.limitedPhoto = true;
},
//获取人脸数据
getMsgFormSon(data){
let that =this;
if(that.leader==='leader_1'){
that.leader_1=data.name;
}else if(that.leader==='leader_2'){
that.leader_2=data.name;
}else if(that.leader==='leader_3'){
that.leader_3=data.name;
}
firstAudit(that.planId,{leader:that.leader ,token : data.token}).then(res=>{
if(res.code===200){
this.limitedPhoto = false;
if(that.leader_1!==null&&that.leader_2!==null&&that.leader_3!==null){
this.reviewVisible = false;
}
that.getTableData();
}
})
},
},
mounted() {
this.getProcessList();
}
}
</script>
<style scoped>
.tabButtonWrap{
display: flex;
margin-bottom: 2px;
border-bottom: 1px solid #EBEEF5;
}
.tabButton{
height: 40px;
width: 100px;
color: #555555;
line-height: 40px;
text-align: center;
border: 1px solid #EBEEF5;
border-radius: 10px 10px 0 0;
}
.activeTab {
color: #FFFFFF;
font-weight: bold;
background: #409EFF;
}
.reviewWrap{
display: flex;
font-size: 16px;
justify-content: flex-end;
}
.reviewBlock{
display: flex;
flex-direction: column;
margin-right: 40px
}
.directorName{
height: 30px;
}
.reviewBtn{
width: 80px;
height: 35px;
font-size: 15px;
line-height: 33px;
text-align: center;
border: 1px solid #409eff;
border-radius: 5px;
/*background: #409eff;*/
color: #409eff;
}
.items {
height: 35px;
line-height: 35px;
padding-left: 20px;
}
.itemLabel {
font-size: 14px;
color: #606266;
font-weight: 600;
}
</style>

View File

@ -30,7 +30,7 @@ class HRMService:
update_all_user_facedata_cache() update_all_user_facedata_cache()
try: try:
results = face_recognition.compare_faces(face_datas, results = face_recognition.compare_faces(face_datas,
unknown_face_encoding, tolerance=0.5) unknown_face_encoding, tolerance=0.48)
except: except:
return None, '人脸匹配失败' return None, '人脸匹配失败'
for index, value in enumerate(results): for index, value in enumerate(results):

View File

@ -29,4 +29,4 @@ class IProductFilterSet(DynamicFieldsFilterMixin, filters.FilterSet):
class Meta: class Meta:
model = IProduct model = IProduct
fields = ['material', 'warehouse', 'batch', 'order', 'material__type', 'update_time_start', 'update_time_end', fields = ['material', 'warehouse', 'batch', 'order', 'material__type', 'update_time_start', 'update_time_end',
'to_order', 'need_to_order'] 'to_order', 'need_to_order', 'state']

View File

@ -96,6 +96,13 @@ class FIFOItemCreateSerializer(serializers.ModelSerializer):
raise ValidationError('非采购订单') raise ValidationError('非采购订单')
return super().create(validated_data) return super().create(validated_data)
def validate_batch(self, value):
if value == '':
return value
elif len(value) > 6:
return value
raise ValidationError('批次号错误')
class FIFOItemUpdateSerializer(serializers.ModelSerializer): class FIFOItemUpdateSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = FIFOItem model = FIFOItem
@ -123,6 +130,13 @@ class FIFODetailInPurSerializer(serializers.ModelSerializer):
model = FIFOItem model = FIFOItem
fields = ['material', 'count', 'batch', 'details', 'warehouse'] fields = ['material', 'count', 'batch', 'details', 'warehouse']
def validate_batch(self, value):
if value == '':
return value
elif len(value) > 6:
return value
raise ValidationError('批次号错误')
class MaterialBatchQuerySerializer(serializers.Serializer): class MaterialBatchQuerySerializer(serializers.Serializer):
warehouse = serializers.IntegerField(label="仓库ID", required=False) warehouse = serializers.IntegerField(label="仓库ID", required=False)

View File

@ -1,5 +1,10 @@
from itertools import count
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
from apps.inm.models import FIFOItemProduct, IProduct, Inventory, MaterialBatch, FIFO, FIFOItem from apps.inm.models import FIFOItemProduct, IProduct, Inventory, MaterialBatch, FIFO, FIFOItem, WareHouse
from apps.mtm.models import Material
from apps.sam.models import SalePack, SaleProduct
from django.db.models import Count
from django.db.models.aggregates import Sum
class InmService: class InmService:
@classmethod @classmethod
@ -53,18 +58,19 @@ class InmService:
o1 = Inventory.objects.get(material=material, warehouse=warehouse) o1 = Inventory.objects.get(material=material, warehouse=warehouse)
temp_count = o1.count - i.count temp_count = o1.count - i.count
if temp_count < 0: if temp_count < 0:
raise ValidationError('库存不足,操作失败') raise ValidationError('仓库库存不足,操作失败')
o1.count = temp_count o1.count = temp_count
o1.save() o1.save()
print(i.batch)
o2 = MaterialBatch.objects.get(material=material, warehouse=warehouse, batch=i.batch) o2 = MaterialBatch.objects.get(material=material, warehouse=warehouse, batch=i.batch)
temp_count = o2.count - i.count temp_count = o2.count - i.count
if temp_count < 0: if temp_count < 0:
raise ValidationError('库存不足,操作失败') raise ValidationError('批次库存不足,操作失败')
o2.count = temp_count o2.count = temp_count
o2.save() o2.save()
temp_count = material.count - i.count temp_count = material.count - i.count
if temp_count < 0: if temp_count < 0:
raise ValidationError('库存不足,操作失败') raise ValidationError('物料库存不足,操作失败')
material.count = temp_count material.count = temp_count
material.save() material.save()
@ -73,3 +79,63 @@ class InmService:
# 生产领料的情况直接从IProduct中删除 # 生产领料的情况直接从IProduct中删除
numbers = FIFOItemProduct.objects.filter(fifoitem=i).values_list('number', flat=True) numbers = FIFOItemProduct.objects.filter(fifoitem=i).values_list('number', flat=True)
IProduct.objects.filter(number__in=numbers).delete() IProduct.objects.filter(number__in=numbers).delete()
# 销售的话已经处理了
# elif instance.type == FIFO.FIFO_TYPE_SALE_OUT:
# ips = FIFOItemProduct.objects.filter(fifoitem=i).values_list('iproduct', flat=True)
# IProduct.objects.filter(id__in=ips).update(state=IProduct.SALED)
@classmethod
def sale_out_audit(cls, fifo:FIFO):
sale = fifo.sale
saleps = SaleProduct.objects.filter(sale=sale, packnum__isnull=True, remark__isnull=True)
if saleps.exists():
raise ValidationError('存在未装箱的产品')
# 创建出库条目
ips = IProduct.objects.filter(sale_iproduct__sale=sale,
sale_iproduct__packnum__isnull=False)
ips.update(state=IProduct.SALED)
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)
# 装箱附件处理
# ml = SalePack.objects.filter(sale_product__iproduct = ips
# ).values('packitem__material').annotate(count=Sum('count'))
# for i in ml:
# material = Material.objects.get(id=i['material'])
# 更新动态产品表情况
from apps.wpm.models import WProduct
WProduct.objects.filter(id__in=ips.values_list('wproduct', flat=True)).update(
act_state=WProduct.WPR_ACT_STATE_SELLED)
# 变更销售记录实际发货数
sale.count_real = ips.count()
sale.save()
# 变更订单状态
order = sale.order
if order:
order.delivered_count = IProduct.objects.filter(sale_iproduct__sale__order=order
, sale_iproduct__packnum__isnull=False).count()
order.save()

View File

@ -184,24 +184,27 @@ class FIFOViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
return Response() return Response()
@action(methods=['post'], detail=True, perms_map={'post': 'fifo_audit'}, serializer_class=serializers.Serializer) @action(methods=['post'], detail=True, perms_map={'post': 'fifo_audit'}, serializer_class=serializers.Serializer)
@transaction.atomic
def audit(self, request, pk=None): def audit(self, request, pk=None):
""" """
审核通过 审核通过
""" """
obj = self.get_object() obj = self.get_object()
if not FIFOItem.objects.filter(fifo=obj).exists():
raise ValidationError('出入库条目为空')
for i in FIFOItem.objects.filter(fifo=obj, need_test=True):
if not i.is_testok:
raise APIException('未检验通过, 不可审核')
if obj.is_audited: if obj.is_audited:
raise APIException('该入库记录已审核通过') raise APIException('该入库记录已审核通过')
with transaction.atomic(): if obj.type == FIFO.FIFO_TYPE_SALE_OUT: # 如果是销售提货,需额外处理
obj.is_audited = True InmService.sale_out_audit(obj)
obj.auditor = request.user else:
obj.inout_date = timezone.now() # 也是审核日期 if not FIFOItem.objects.filter(fifo=obj).exists():
obj.save() raise ValidationError('出入库条目为空')
InmService.update_inm(obj) # 更新库存 for i in FIFOItem.objects.filter(fifo=obj, need_test=True):
if not i.is_testok:
raise APIException('未检验通过, 不可审核')
obj.is_audited = True
obj.auditor = request.user
obj.inout_date = timezone.now() # 也是审核日期
obj.save()
InmService.update_inm(obj) # 更新库存
return Response() return Response()

View File

@ -24,6 +24,8 @@ class MaterialDetailSerializer(serializers.ModelSerializer):
objs = Process.objects.filter(subproduction_process__product=obj, subproduction_process__is_deleted=False, is_deleted=False).distinct().order_by('number') objs = Process.objects.filter(subproduction_process__product=obj, subproduction_process__is_deleted=False, is_deleted=False).distinct().order_by('number')
return ProcessSimpleSerializer(instance=objs, many=True).data return ProcessSimpleSerializer(instance=objs, many=True).data
class PackItemSerializer(serializers.ModelSerializer): class PackItemSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = PackItem model = PackItem
@ -42,7 +44,13 @@ class PackItemUpdateSerializer(serializers.ModelSerializer):
class MaterialSimpleSerializer(serializers.ModelSerializer): class MaterialSimpleSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Material model = Material
fields = ['id', 'name', 'number', 'unit','specification', 'type'] fields = ['id', 'name', 'number', 'unit','specification', 'type', 'count', 'count_safe']
class PackItemDetailSerializer(serializers.ModelSerializer):
material_ = MaterialSimpleSerializer(source='material', read_only=True)
class Meta:
model = PackItem
fields = '__all__'
class ProcessSerializer(serializers.ModelSerializer): class ProcessSerializer(serializers.ModelSerializer):
instruction_ = FileSimpleSerializer(source='instruction', read_only=True) instruction_ = FileSimpleSerializer(source='instruction', read_only=True)

View File

@ -272,6 +272,9 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=FirstTestAuditSerializer) @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=FirstTestAuditSerializer)
@transaction.atomic @transaction.atomic
def first_audit(self, request, pk=None): def first_audit(self, request, pk=None):
"""
首件审核
"""
obj = self.get_object() obj = self.get_object()
if obj.leader_1 and obj.leader_2 and obj.leader_3: if obj.leader_1 and obj.leader_2 and obj.leader_3:
raise ValidationError('首件确认已完成') raise ValidationError('首件确认已完成')

View File

@ -97,6 +97,8 @@ class TestRecordDetailSerializer(serializers.ModelSerializer):
# record_data = TestRecordItemSerializer(source='item_test_record', read_only=True, many=True) # record_data = TestRecordItemSerializer(source='item_test_record', read_only=True, many=True)
record_data = serializers.SerializerMethodField() record_data = serializers.SerializerMethodField()
origin_test_ = TestRecordDetailBaseSerializer(source='origin_test', read_only=True) origin_test_ = TestRecordDetailBaseSerializer(source='origin_test', read_only=True)
create_by_ = UserSimpleSerializer(source='create_by', read_only=True)
update_by_ = UserSimpleSerializer(source='update_by', read_only=True)
class Meta: class Meta:
model = TestRecord model = TestRecord
fields = '__all__' fields = '__all__'

View File

@ -0,0 +1,36 @@
# Generated by Django 3.2.9 on 2022-02-22 07:30
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('mtm', '0049_auto_20220222_0944'),
('sam', '0013_auto_20220222_0941'),
]
operations = [
migrations.AddField(
model_name='saleproduct',
name='packnum',
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='装箱单号'),
),
migrations.CreateModel(
name='SalePack',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('count', models.PositiveSmallIntegerField(verbose_name='打包数量')),
('packitem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.packitem', verbose_name='打包项目')),
('sale_product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sam.saleproduct', verbose_name='关联销售产品')),
],
options={
'abstract': False,
},
),
]

View File

@ -94,7 +94,6 @@ class Sale(CommonADModel):
receiver_address = models.CharField('收获地址', null=True, blank=True, max_length=200) receiver_address = models.CharField('收获地址', null=True, blank=True, max_length=200)
remark = models.CharField('备注', null=True, blank=True, max_length=200) remark = models.CharField('备注', null=True, blank=True, max_length=200)
class SaleProduct(BaseModel): class SaleProduct(BaseModel):
""" """
具体产品 具体产品
@ -117,6 +116,7 @@ class SalePack(BaseModel):
on_delete=models.CASCADE) on_delete=models.CASCADE)
packitem = models.ForeignKey(PackItem, verbose_name='打包项目', packitem = models.ForeignKey(PackItem, verbose_name='打包项目',
on_delete=models.CASCADE) on_delete=models.CASCADE)
count = models.PositiveSmallIntegerField('打包数量')

View File

@ -21,7 +21,7 @@ class CustomerCreateUpdateSerializer(serializers.ModelSerializer):
class CustomerSimpleSerializer(serializers.ModelSerializer): class CustomerSimpleSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Customer model = Customer
fields = ['id', 'name'] fields = ['id', 'name', 'address', 'contact', 'contact_phone']
class ContractSerializer(serializers.ModelSerializer): class ContractSerializer(serializers.ModelSerializer):
customer_ = CustomerSimpleSerializer(source='customer', read_only=True) customer_ = CustomerSimpleSerializer(source='customer', read_only=True)

View File

@ -2,8 +2,9 @@ from rest_framework import serializers
from rest_framework import exceptions from rest_framework import exceptions
from apps.inm.models import IProduct from apps.inm.models import IProduct
from apps.inm.serializers import IProductListSerializer from apps.inm.serializers import IProductListSerializer
from apps.mtm.serializers import MaterialSimpleSerializer from apps.mtm.models import Material, PackItem
from apps.sam.models import Sale, SaleProduct from apps.mtm.serializers import MaterialSimpleSerializer, PackItemDetailSerializer
from apps.sam.models import Sale, SalePack, SaleProduct
from apps.sam.serializers import CustomerSimpleSerializer, OrderSimpleSerializer from apps.sam.serializers import CustomerSimpleSerializer, OrderSimpleSerializer
from django.db import transaction from django.db import transaction
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
@ -61,3 +62,38 @@ class SaleProductCreateSerializer(serializers.ModelSerializer):
if sale.count+order.delivered_count>order.count: if sale.count+order.delivered_count>order.count:
raise exceptions.APIException('超过订单所需数量') raise exceptions.APIException('超过订单所需数量')
return instance return instance
class SPackItemSerializer(serializers.ModelSerializer):
name = serializers.CharField(source='packitem.name', read_only=True)
specification = serializers.CharField(source='packitem.specification', read_only=True)
unit = serializers.CharField(source='packitem.unit', read_only=True)
material_ = MaterialSimpleSerializer(source='packitem.material', read_only=True)
class Meta:
model = SalePack
fields = '__all__'
class SaleProductPackDetailSerializer(serializers.ModelSerializer):
detail = serializers.SerializerMethodField()
class Meta:
model = SaleProduct
fields = ['packnum', 'detail', 'remark']
def get_detail(self, obj):
return SPackItemSerializer(instance=SalePack.objects.filter(sale_product=obj)
, many=True).data
class SPackItemCreateSerializer(serializers.Serializer):
id = serializers.PrimaryKeyRelatedField(queryset=SalePack.objects.all())
count = serializers.IntegerField()
class SaleProductPackSerializer(serializers.ModelSerializer):
detail = SPackItemCreateSerializer(many=True)
packnum = serializers.CharField(min_length=6)
class Meta:
model = SaleProduct
fields = ['packnum', 'detail', 'remark']
class SRemarkItemCreateSerializer(serializers.Serializer):
remark = serializers.CharField(min_length=6)

View File

@ -1,11 +1,12 @@
from rest_framework.mixins import ListModelMixin, DestroyModelMixin, CreateModelMixin, RetrieveModelMixin from rest_framework.mixins import ListModelMixin, DestroyModelMixin, CreateModelMixin, RetrieveModelMixin
from rest_framework.viewsets import GenericViewSet from rest_framework.viewsets import GenericViewSet
from rest_framework.response import Response from rest_framework.response import Response
from apps.inm.models import FIFO, FIFOItem, FIFOItemProduct, IProduct, WareHouse from apps.inm.models import FIFO, FIFOItem, FIFOItemProduct, IProduct, WareHouse
from apps.inm.services import InmService from apps.inm.services import InmService
from apps.mtm.models import Material from apps.mtm.models import Material, PackItem
from apps.sam.models import Sale, SaleProduct from apps.sam.models import Sale, SalePack, SaleProduct
from apps.sam.serializers_sale import SaleCreateSerializer, SaleListSerializer, SaleProductCreateSerializer, SaleProductListSerializer from apps.sam.serializers_sale import SRemarkItemCreateSerializer, SaleCreateSerializer, SaleListSerializer, SaleProductCreateSerializer, SaleProductListSerializer, SaleProductPackDetailSerializer, SaleProductPackSerializer
from rest_framework import exceptions from rest_framework import exceptions
from django.db import transaction from django.db import transaction
from rest_framework.decorators import action from rest_framework.decorators import action
@ -41,7 +42,8 @@ class SaleViewSet(CreateUpdateModelAMixin, ListModelMixin, RetrieveModelMixin, C
if obj.is_audited: if obj.is_audited:
raise exceptions.APIException('该销售记录已审核,不可删除') raise exceptions.APIException('该销售记录已审核,不可删除')
obj.delete() obj.delete()
IProduct.objects.filter(sale_iproduct__sale=obj).update() IProduct.objects.filter(sale_iproduct__sale=obj).update(state=IProduct.SALE_OK)
return Response()
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
data = request.data data = request.data
@ -88,46 +90,10 @@ class SaleViewSet(CreateUpdateModelAMixin, ListModelMixin, RetrieveModelMixin, C
fifo.create_by = request.user fifo.create_by = request.user
fifo.number = 'CK' + ranstr(7) fifo.number = 'CK' + ranstr(7)
fifo.save() 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)
# 更新动态产品表情况
# from apps.wpm.models import WProduct
# WProduct.objects.filter(iproduct_wproduct__sale_iproduct__sale=obj).update(
# act_state=WProduct.WPR_ACT_STATE_SELLED)
# 更新库存
# InmService.update_inm(fifo)
# 变更销售提货审核状态
obj.is_audited = True obj.is_audited = True
obj.save() obj.save()
# 变更订单状态
# if obj.order:
# order = obj.order
# order.delivered_count = order.delivered_count + obj.count
# order.save()
return Response() return Response()
@ -158,3 +124,53 @@ class SaleProductViewSet(ListModelMixin, DestroyModelMixin, CreateModelMixin, Ge
sale.count = SaleProduct.objects.filter(sale=obj.sale).count() sale.count = SaleProduct.objects.filter(sale=obj.sale).count()
sale.save() sale.save()
return Response() return Response()
def create(self, request, *args, **kwargs):
obj = self.get_object()
sale = obj.sale
if sale.is_audited:
raise exceptions.APIException('该销售记录已审核,不可添加产品')
return super().create(request, *args, **kwargs)
@action(methods=['get', 'post'], detail=True, perms_map={'post':'sale_pack', 'get':'*'}, serializer_class=SaleProductPackSerializer)
@transaction.atomic
def pack(self, request, pk=None):
"""
打包装箱
"""
obj = self.get_object()
if request.method == 'GET':
for i in PackItem.objects.filter(product=obj.iproduct.material, is_deleted=False):
SalePack.objects.get_or_create(sale_product=obj, packitem=i,
defaults={
"sale_product":obj,
"packitem":i,
"count":i.count
})
return Response(SaleProductPackDetailSerializer(instance=obj).data)
elif request.method == 'POST':
serializer = SaleProductPackSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
vdata = serializer.validated_data
obj.packnum = vdata['packnum']
obj.remark = vdata.get('remark', '')
for i in vdata['detail']:
pi = i['id']
pi.count = i['count']
pi.save()
obj.save()
return Response()
@action(methods=['post'], detail=True, perms_map={'post':'sale_pack'}, serializer_class=SRemarkItemCreateSerializer)
@transaction.atomic
def remark(self, request, pk=None):
"""
不装箱备注
"""
obj = self.get_object()
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
vdata = serializer.validated_data
obj.remark = vdata['remark']
obj.save()
return Response()