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

This commit is contained in:
shijing 2021-11-01 15:38:23 +08:00
commit 7be9e553f1
44 changed files with 1677 additions and 252 deletions

View File

@ -38,22 +38,34 @@ export function getInventoryList(query) {
}
export function createInventory(data) {
return request({
url: '/inm/inventory/',
url: '/inm/fifo/in_pur/',
method: 'post',
data
})
}
export function updateInventory(id, data) {
//仓库对应的物料
export function getfifodetailList(query) {
return request({
url: `/inm/inventory/${id}/`,
method: 'put',
data
url: '/inm/fifodetail/',
method: 'get',
params: query
})
}
export function deleteInventory(id, data) {
//出入库记录
export function getfifoList(query) {
return request({
url: `/inm/inventory/${id}/`,
method: 'delete',
data
url: '/inm/fifo/',
method: 'get',
params: query
})
}
//物料批次
export function getmaterialbatchList(query) {
return request({
url: '/inm/materialbatch/',
method: 'get',
params: query
})
}

View File

@ -72,3 +72,12 @@ export function issuesubplan(id) {
})
}
//领料需求单,库存数量
export function createPick_need(id, data) {
return request({
url: `/pm/subproduction_plan/${id}/pick_need/`,
method: 'post',
data
})
}

View File

@ -303,32 +303,34 @@ export const asyncRoutes = [
meta: { title: '仓库', icon: 'example', perms: ['index_manage'] }
},
{
path: 'materialbatch/:id',
name: 'MaterialBatch',
component: () => import('@/views/inm/materialbatch'),
path: 'inventory/:id',
name: 'inventory',
component: () => import('@/views/inm/inventory'),
meta: { title: '仓库物料', perms: ['vendor_manage'] },
hidden: true
}
,
{
path: 'warehouse',
name: 'warehouse',
component: () => import('@/views/inm/warehouse'),
meta: { title: '物料库存', icon: 'example', perms: ['index_manage'] }
path: 'materialbatch',
name: 'materialbatch',
component: () => import('@/views/inm/materialbatch'),
meta: { title: '物料批次', icon: 'example', perms: ['index_manage'] }
},
{
path: 'warehouse',
name: 'warehouse',
component: () => import('@/views/inm/warehouse'),
meta: { title: '半成品库存', icon: 'example', perms: ['index_manage'] }
}
,
path: 'fifo',
name: 'fifo',
component: () => import('@/views/inm/fifo'),
meta: { title: '出入库记录', icon: 'example', perms: ['index_manage'] }
},
{
path: 'warehouse',
name: 'warehouse',
component: () => import('@/views/inm/warehouse'),
meta: { title: '成品库存', icon: 'example', perms: ['index_manage'] }
path: 'fifodetail/:id',
name: 'fifodetail',
component: () => import('@/views/inm/fifodetail'),
meta: { title: '仓库物料', perms: ['vendor_manage'] },
hidden: true
}
]
},

View File

@ -0,0 +1,333 @@
<template>
<div class="app-container">
<el-card>
<div>
<el-input
v-model="listQuery.search"
placeholder="仓库名称/仓库编号"
style="width: 300px"
class="filter-item"
@keyup.enter.native="handleFilter"
/>
<el-button
class="filter-item"
type="primary"
icon="el-icon-search"
@click="handleFilter"
>搜索</el-button
>
<el-button
class="filter-item"
type="primary"
icon="el-icon-refresh-left"
@click="resetFilter"
>重置</el-button
>
</div>
</el-card>
<el-card style="margin-top: 10px">
<div style="margin-top: 10px">
<el-button type="primary" icon="el-icon-plus" @click="handleCreate"
>新增物料</el-button
>
</div>
<el-table
v-loading="listLoading"
:data="fifoList.results"
border
fit
stripe
highlight-current-row
max-height="600"
>
<el-table-column type="index" width="50" />
<el-table-column label="仓库">
<template slot-scope="scope">{{ scope.row.warehouse_.name }}</template>
</el-table-column>
<el-table-column label="出库入人员">
<template slot-scope="scope">{{ scope.row.operator_.username }}</template>
</el-table-column>
<el-table-column label="出入库时间">
<template slot-scope="scope">{{ scope.row.inout_date }}</template>
</el-table-column>
<el-table-column label="出入库类型">
<template slot-scope="scope">{{ types_[scope.row.type] }}</template>
</el-table-column>
<el-table-column
align="center"
label="操作"
width="220px"
>
<template slot-scope="scope">
<el-link
v-if="checkPermission(['warehouse_update'])"
@click="handleDetail(scope)"
>查看详情</el-link
>
<el-link
v-if="checkPermission(['warehouse_delete'])"
type="danger"
@click="handleDelete(scope)"
>删除</el-link
>
</template>
</el-table-column>
</el-table>
<pagination
v-show="fifoList.count > 0"
:total="fifoList.count"
:page.sync="listQuery.page"
:limit.sync="listQuery.page_size"
@pagination="getList"
/>
</el-card>
<el-dialog
:visible.sync="dialogVisible"
:title="dialogType === 'edit' ? '编辑仓库' : '新增仓库'"
>
<el-form
ref="Form"
:model="inventory"
label-width="80px"
label-position="right"
:rules="rule1"
>
<el-form-item label="仓库" prop="warehouse">
<el-select v-model="inventory.warehouse" placeholder="请选择" filterable >
<el-option v-for="item in warehouseData" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="操作人" prop="operator">
<el-select v-model="inventory.operator" placeholder="请选择" filterable >
<el-option v-for="item in userData" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="入库时间" prop="inout_date">
<el-date-picker
v-model="inventory.inout_date"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
style="width:100%"
>
</el-date-picker>
</el-form-item>
<div v-for="(item,index) in inventory.details" :key="index">
<el-row>
<el-col :span="8" style="margin-right:10px;">
<el-form-item class="material" label="物料:" :prop="'details.'+index+'.material'" >
<el-select v-model="item.material" size="small" >
<el-option v-for="item in materialoptions"
:key="item.value"
:value="item.value"
:label="item.label">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8" style="margin-right:10px;">
<el-form-item class="count" label="数量" :prop="'details.'+index+'.count'" >
<el-input-number v-model="item.count" :min="0"></el-input-number>
</el-form-item>
</el-col>
<el-col :span="6" style="margin-right:10px;">
<el-form-item class="batch" label="批次" :prop="'details.'+index+'.batch'" >
<el-input v-model="item.batch" placeholder="批次号" ></el-input>
</el-form-item>
</el-col>
<!-- 删除按钮 -->
<el-col :span="1" >
<el-tooltip class="item" effect="dark" content="删除" placement="top">
<el-button @click="deleteItem(index)" style='border:none;background:none;padding:0px;margin-top:4px;'>
<i class="el-icon-remove-outline" style='font-size:20px;'></i>
</el-button>
</el-tooltip>
</el-col>
</el-row>
</div>
<div>
<el-tooltip class="item" effect="dark" content="添加条件" placement="top">
<el-button @click="addConditions" style='cursor:pointer;width:95%;color:#fe000c;border:1px dashed #fe000c;height:40px;padding:0px;margin-top:10px;margin-left:20px;font-size:26px'>
+
</el-button>
</el-tooltip>
</div>
</el-form>
<div style="text-align: right">
<el-button type="danger" @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirm('Form')">确认</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {getfifoList,createInventory,getWarehouseList} from "@/api/inm";
import checkPermission from "@/utils/permission";
import { getMaterialList} from "@/api/mtm";
import { getUserList} from "@/api/user";
import { genTree } from "@/utils";
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
const defaulteinventory = {
details:[
{
material:'', // 物料
count:'',//数量
batch:'',//批次
}
],
};
export default {
components: { Pagination },
data() {
return {
inventory: defaulteinventory,
fifoList: {
count: 0,
},
types_:{
1:'生产领料',
2:'销售提货',
3:'采购入库',
4:'生产入库',
},
listQuery: {
page: 1,
page_size: 20,
},
warehouseData:[],
materialoptions:[],
userData:[],
listLoading: true,
dialogVisible: false,
dialogType: "new",
rule1: {
name: [{ required: true, message: "请输入", trigger: "blur" }],
},
};
},
computed: {},
watch: {},
created() {
this.getlists()
this.getList();
this.getUser();
this.getmaterialList();
},
methods: {
checkPermission,
getList() {
this.listLoading = true;
getfifoList(this.listQuery).then((response) => {
if (response.data) {
this.fifoList = response.data;
}
this.listLoading = false;
});
},
//出入库详情
handleDetail(scope){
this.$router.push({name: "fifodetail", params: { id: scope.row.id }, })
},
getlists(){
getWarehouseList({page:0}).then((response) => {
if (response.data) {
this.warehouseData = genTree(response.data);
}
this.listLoading = false;
});
},
getUser() {
getUserList({page:0}).then(res => {
if (res.data) {
this.userData = genTree(res.data);
}
})
},
addConditions () {
if(this.inventory.details.length<=10) {
this.inventory.details.push({
material:'', // 物料
count:'', //数量
batch:'', // 批次
});
} else {
this.$message('最多可添加十项条件');
}
},
deleteItem (index) {
this.inventory.details.splice(index, 1)
},
//物料
getmaterialList() {
this.listLoading = true;
getMaterialList({pageoff:true}).then((response) => {
if (response.data) {
this.materialoptions = genTree(response.data);
}
this.listLoading = false;
});
},
handleCreate()
{
this.inventory.details=[
{
material:'', // 物料
count:'',//数量
batch:'',//批次
}
];
this.inventory = Object.assign({}, defaulteinventory);
this.dialogType = "new";
this.dialogVisible = true;
this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});
},
handleFilter() {
this.listQuery.page = 1;
this.getList();
},
resetFilter() {
this.listQuery = {
page: 1,
page_size: 20,
}
this.getList();
},
async confirm(form) {
console.log(this.inventory)
createInventory(this.inventory).then((res) => {
if (res.code >= 200) {
this.getList();
this.dialogVisible = false;
this.$message.success("成功");
}
});
},
},
};
</script>

View File

@ -0,0 +1,131 @@
<template>
<div class="app-container">
<el-card>
<div>
<el-input
v-model="listQuery.search"
placeholder="仓库名称/仓库编号"
style="width: 300px"
class="filter-item"
@keyup.enter.native="handleFilter"
/>
<el-button
class="filter-item"
type="primary"
icon="el-icon-search"
@click="handleFilter"
>搜索</el-button
>
<el-button
class="filter-item"
type="primary"
icon="el-icon-refresh-left"
@click="resetFilter"
>重置</el-button
>
</div>
</el-card>
<el-card style="margin-top: 10px">
<el-table
v-loading="listLoading"
:data="fifodetailList.results"
border
fit
stripe
highlight-current-row
max-height="600"
>
<el-table-column type="index" width="50" />
<el-table-column label="物料名称">
<template slot-scope="scope">{{ scope.row.material_.name }}</template>
</el-table-column>
<el-table-column label="物料批次">
<template slot-scope="scope">{{ scope.row.batch }}</template>
</el-table-column>
<el-table-column label="物料编号">
<template slot-scope="scope">{{ scope.row.material_.number }}</template>
</el-table-column>
<el-table-column label="物料单位">
<template slot-scope="scope">{{ scope.row.material_.unit }}</template>
</el-table-column>
<el-table-column label="物料型号">
<template slot-scope="scope">{{ scope.row.material_.specification }}</template>
</el-table-column>
<el-table-column label="入库数量">
<template slot-scope="scope">{{ scope.row.count }}</template>
</el-table-column>
</el-table>
<pagination
v-show="fifodetailList.count > 0"
:total="fifodetailList.count"
:page.sync="listQuery.page"
:limit.sync="listQuery.page_size"
@pagination="getList"
/>
</el-card>
</div>
</template>
<script>
import { getfifodetailList } from "@/api/inm";
import checkPermission from "@/utils/permission";
import { getMaterialList} from "@/api/mtm";
import { getUserList} from "@/api/user";
import { genTree } from "@/utils";
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
export default {
components: { Pagination },
data() {
return {
InventoryList: {
count: 0,
},
listQuery: {
page: 1,
page_size: 20,
},
listLoading: true,
dialogVisible: false,
dialogType: "new",
rule1: {
name: [{ required: true, message: "请输入", trigger: "blur" }],
},
};
},
computed: {},
watch: {},
created() {
this.getList();
},
methods: {
checkPermission,
getList() {
this.listLoading = true;
this.listQuery.fifo=this.$route.params.id;
getfifodetailList(this.listQuery).then((response) => {
if (response.data) {
this.fifodetailList = response.data;
}
this.listLoading = false;
});
},
},
};
</script>

View File

@ -0,0 +1,137 @@
<template>
<div class="app-container">
<el-card>
<div>
<el-input
v-model="listQuery.search"
placeholder="仓库名称/仓库编号"
style="width: 300px"
class="filter-item"
@keyup.enter.native="handleFilter"
/>
<el-button
class="filter-item"
type="primary"
icon="el-icon-search"
@click="handleFilter"
>搜索</el-button
>
<el-button
class="filter-item"
type="primary"
icon="el-icon-refresh-left"
@click="resetFilter"
>重置</el-button
>
</div>
</el-card>
<el-card style="margin-top: 10px">
<el-table
v-loading="listLoading"
:data="InventoryList.results"
border
fit
stripe
highlight-current-row
max-height="600"
>
<el-table-column type="index" width="50" />
<el-table-column label="物料名称">
<template slot-scope="scope">{{ scope.row.material_.name }}</template>
</el-table-column>
<el-table-column label="物料编号">
<template slot-scope="scope">{{ scope.row.material_.number }}</template>
</el-table-column>
<el-table-column label="物料单位">
<template slot-scope="scope">{{ scope.row.material_.unit }}</template>
</el-table-column>
<el-table-column label="物料型号">
<template slot-scope="scope">{{ scope.row.material_.specification }}</template>
</el-table-column>
<el-table-column label="物料存量">
<template slot-scope="scope">{{ scope.row.count }}</template>
</el-table-column>
</el-table>
<pagination
v-show="InventoryList.count > 0"
:total="InventoryList.count"
:page.sync="listQuery.page"
:limit.sync="listQuery.page_size"
@pagination="getList"
/>
</el-card>
</div>
</template>
<script>
import { getInventoryList,getfifoList,getfifodetailList,createInventory,getWarehouseList } from "@/api/inm";
import checkPermission from "@/utils/permission";
import { getMaterialList} from "@/api/mtm";
import { getUserList} from "@/api/user";
import { genTree } from "@/utils";
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
export default {
components: { Pagination },
data() {
return {
InventoryList: {
count: 0,
},
listQuery: {
page: 1,
page_size: 20,
},
listLoading: true,
dialogVisible: false,
dialogType: "new",
rule1: {
name: [{ required: true, message: "请输入", trigger: "blur" }],
},
};
},
computed: {},
watch: {},
created() {
this.getList();
},
methods: {
checkPermission,
getList() {
this.listLoading = true;
if(this.$route.params.id!="")
{
this.listQuery.warehouse=this.$route.params.id;
getInventoryList(this.listQuery).then((response) => {
if (response.data) {
this.InventoryList = response.data;
}
this.listLoading = false;
});
}
else
{
getInventoryList(this.listQuery).then((response) => {
if (response.data) {
this.InventoryList = response.data;
}
this.listLoading = false;
});
}
},
},
};
</script>

View File

@ -26,52 +26,46 @@
</div>
</el-card>
<el-card style="margin-top: 10px">
<el-table
v-loading="listLoading"
:data="warehouseList.results"
:data="InventoryList.results"
border
fit
stripe
highlight-current-row
max-height="600"
>
<el-table-column type="index" width="50" />
<el-table-column label="物料名称">
<template slot-scope="scope">{{ scope.row.name }}</template>
</el-table-column>
<el-table-column label="物料批次">
<template slot-scope="scope">{{ scope.row.number }}</template>
<template slot-scope="scope">{{ scope.row.batch }}</template>
</el-table-column>
<el-table-column label="入库数量">
<template slot-scope="scope">{{ scope.row.place }}</template>
<el-table-column label="物料名称">
<template slot-scope="scope">{{ scope.row.material_.name }}</template>
</el-table-column>
<el-table-column label="物料有效期">
<template slot-scope="scope">{{ scope.row.place }}</template>
<el-table-column label="物料编号">
<template slot-scope="scope">{{ scope.row.material_.number }}</template>
</el-table-column>
<el-table-column label="入库时间">
<template slot-scope="scope">{{ scope.row.create_time }}</template>
<el-table-column label="仓库名称">
<template slot-scope="scope">{{ scope.row.warehouse_.name }}</template>
</el-table-column>
<el-table-column label="仓库编号">
<template slot-scope="scope">{{ scope.row.warehouse_.number }}</template>
</el-table-column>
<el-table-column label="物料总存量">
<template slot-scope="scope">{{ scope.row.count }}</template>
</el-table-column>
<el-table-column
align="center"
label="操作"
width="220px"
>
<template slot-scope="scope">
<el-link
v-if="checkPermission(['warehouse_delete'])"
type="danger"
@click="handleDelete(scope)"
>删除</el-link
>
</template>
</el-table-column>
</el-table>
<pagination
v-show="warehouseList.count > 0"
:total="warehouseList.count"
v-show="InventoryList.count > 0"
:total="InventoryList.count"
:page.sync="listQuery.page"
:limit.sync="listQuery.page_size"
@pagination="getList"
@ -81,69 +75,57 @@
</div>
</template>
<script>
import { getWarehouseList, deleteWarehouse } from "@/api/inm";
import { getInventoryList,getfifoList,getmaterialbatchList,createInventory,getWarehouseList } from "@/api/inm";
import checkPermission from "@/utils/permission";
import { getMaterialList} from "@/api/mtm";
import { getUserList} from "@/api/user";
import { genTree } from "@/utils";
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
const defaultewarehouse = {
};
export default {
components: { Pagination },
data() {
return {
warehouse: defaultewarehouse,
warehouseList: {
InventoryList: {
count: 0,
},
options: [{
value: 0,
label: '运转正常'
}, {
value: 1,
label: '停用'
}, {
value: 2,
label: '报废'
}],
listQuery: {
page: 1,
page_size: 20,
},
keeperOptions:[],
depOptions:[],
listLoading: true,
dialogVisible: false,
dialogType: "new",
rule1: {
name: [{ required: true, message: "请输入", trigger: "blur" }],
number: [{ required: true, message: "请输入", trigger: "blur" }],
place: [{ required: true, message: "请输入", trigger: "blur" }],
},
};
},
computed: {},
watch: {},
created() {
this.getList();
},
methods: {
checkPermission,
//设备列表
getList() {
this.listLoading = true;
getWarehouseList(this.listQuery).then((response) => {
getmaterialbatchList(this.listQuery).then((response) => {
if (response.data) {
this.warehouseList = response.data;
this.InventoryList = response.data;
}
this.listLoading = false;
});
},
handleMaterial(scope){
alert(1)
},
handleFilter() {
this.listQuery.page = 1;
this.getList();
@ -156,21 +138,7 @@ export default {
this.getList();
},
handleDelete(scope) {
this.$confirm("确认删除?", "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
await deleteWarehouse(scope.row.id);
this.getList();
this.$message.success("成功");
})
.catch((err) => {
console.error(err);
});
},
},

View File

@ -179,7 +179,7 @@ export default {
},
//跳转到该仓库的物料表
handleMaterial(scope){
this.$router.push({name: "MaterialBatch", params: { id: scope.row.id }, })
this.$router.push({name: "inventory", params: { id: scope.row.id }, })
},
handleFilter() {
this.listQuery.page = 1;

View File

@ -4,7 +4,7 @@
<div>
<el-input
v-model="listQuery.search"
placeholder="物料名称/物料编号"
placeholder="物料名称/物料编号/物料类型"
style="width: 300px"
class="filter-item"
@keyup.enter.native="handleFilter"
@ -50,27 +50,7 @@
</template>
</el-table-column>
<el-table-column label="物料类别" :filters="[{
value: 1,
text: '成品'
}, {
value: 2,
text: '半成品'
}, {
value: 3,
text: '主要原料'
}, {
value: 4,
text: '辅助原料'
}, {
value: 5,
text: '加工工具'
}, {
value: 6,
text: '辅助工具'
}]"
:filter-method="filterTag"
filter-placement="bottom-end">
<el-table-column label="物料类别" >
<template slot-scope="scope"> {{options_[scope.row.type]}}</template>
</el-table-column>
@ -204,9 +184,9 @@ export default {
"1":'成品',
"2":'半成品',
"3":'主要原料',
"4":'辅助',
"4":'辅助',
"5":'加工工具',
"6":'辅助工',
"6":'辅助工',
},
options: [{
@ -220,13 +200,13 @@ export default {
label: '主要原料'
}, {
value: 4,
label: '辅助'
label: '辅助'
}, {
value: 5,
label: '加工工具'
}, {
value: 6,
label: '辅助工'
label: '辅助工'
}],
unitoptions:[
{
@ -235,6 +215,16 @@ export default {
}, {
value: '',
label: ''
},
{
value: '',
label: ''
}, {
value: 'm2',
label: 'm2'
}, {
value: '',
label: ''
},
],
listQuery: {

View File

@ -156,7 +156,7 @@
</el-form>
<div style="text-align: right">
<el-button type="danger" @click="dialogVisible1 = false">取消</el-button>
<el-button type="danger" @click="dialogVisiblesp = false">取消</el-button>
<el-button type="primary" @click="subproducationfirm('Formsp')">确认</el-button>
</div>
</el-dialog>
@ -807,7 +807,7 @@ export default {
this.$refs["Form"].clearValidate();
});
},
handleDelete(scope) {
handleinputDelete(scope) {
this.$confirm("确认删除?", "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",

View File

@ -32,6 +32,9 @@
:label="item.name"
:value="item.number">{{item.name}}</el-tag>
</template>
</el-table-column>
<el-table-column label="排序">
<template slot-scope="scope">{{ scope.row.sort }}</template>
</el-table-column>
<el-table-column
align="center"

View File

@ -11,12 +11,12 @@
>计算物料</el-button>
<el-table
:data="orderList.results"
border
ref="multipleTable"
border
fit
stripe
style="width: 100%"
@selection-change="handleSelectionChange">
>
<el-table-column
type="selection"
width="55">
@ -41,7 +41,7 @@
<template slot-scope="scope" >
<el-form :model="scope.row">
<el-form-item size="mini">
<el-input-number v-model="scope.row.pgcount" :min="0"></el-input-number>
<el-input-number v-model="scope.row.pgcount" :min="0" :value="0"></el-input-number>
</el-form-item>
</el-form>
</template>
@ -234,29 +234,20 @@ export default {
this.listLoading = false;
});
},
handleSelectionChange(rows) {
//物料计算
handlecount()
{
console.log()
let _this=this
_this.mutipID=[]
row.forEach((item) => {
this.$refs.multipleTable.selection.forEach((item) => {
_this.mutipID.push({
"id":item.product_.id,
"count": item.pgcount
});
});
setTimeout(() => {
rows.forEach(row => {
this.$refs.multipleTable.toggleRowSelection(row);
});
}, 1)
},
//物料计算
handlecount()
{
createresource(this.mutipID).then((res) => {
if (res.code >= 200) {

View File

@ -27,6 +27,7 @@
<el-table-column label="工序编号">
<template slot-scope="scope">{{ scope.row.process_.number }}</template>
</el-table-column>
<el-table-column label="子工序">
<template slot-scope="scope" v-if="scope.row.steps">
<el-tag v-for="item in scope.row.steps"
@ -43,11 +44,27 @@
<el-table-column label="完工时间">
<template slot-scope="scope">{{ scope.row.end_date }}</template>
</el-table-column>
<el-table-column label="状态">
<template slot-scope="scope">{{ state_[scope.row.state] }}</template>
</el-table-column>
<el-table-column label="创建时间">
<template slot-scope="scope">{{ scope.row.create_time }}</template>
</el-table-column>
<el-table-column
align="center"
label="操作"
width="220px"
>
<template slot-scope="scope">
<el-link
v-if="checkPermission(['warehouse_update'])"
@click="handleNeed(scope)"
>领料</el-link
>
</template>
</el-table-column>
</el-table>
<pagination
v-show="subproductionplanList.count > 0"
@ -57,11 +74,34 @@
@pagination="getspList"
/>
</el-card>
<el-dialog
:visible.sync="dialogVisible"
:title="dialogType === 'edit' ? '编辑仓库进行领料' : '选择仓库进行领料'"
>
<el-form
ref="Form"
:model="warehouse"
label-width="80px"
label-position="right"
:rules="rule1"
>
<el-form-item label="选择仓库" prop="warehouse">
<el-select v-model="inventory.warehouse" placeholder="请选择" filterable >
<el-option v-for="item in warehouseData" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-form>
<div style="text-align: right">
<el-button type="danger" @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirm('Form')">确认</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getsubproductionplanList } from "@/api/pm";
import { getsubproductionplanList,createPick_need } from "@/api/pm";
import checkPermission from "@/utils/permission";
@ -79,14 +119,24 @@ export default {
page: 1,
page_size: 20,
},
listLoading: true
warehouseData:[],
state_:{
0:'制定中',
1:'已下达',
2:'已接受',
3:'生产中',
4:'已完成'},
listLoading: true,
listLoading: true,
dialogVisible: false,
dialogType: "new",
};
},
computed: {},
watch: {},
created() {
this.getspList();
this.getlists();
},
methods: {
checkPermission,
@ -101,6 +151,17 @@ export default {
this.listLoading = false;
});
},
getlists(){
getWarehouseList({page:0}).then((response) => {
if (response.data) {
this.warehouseData = genTree(response.data);
}
this.listLoading = false;
});
},
handleNeed(scope){
createPick_need()
}
},
};

View File

@ -4,4 +4,5 @@ class InmConfig(AppConfig):
name = 'apps.inm'
verbose_name = '库存管理'
def ready(self):
import apps.inm.signals

View File

@ -0,0 +1,9 @@
from django_filters import rest_framework as filters
from apps.mtm.models import Material
from .models import MaterialBatch
class MbFilterSet(filters.FilterSet):
material = filters.ModelMultipleChoiceFilter(field_name="material", queryset=Material.objects.all())
class Meta:
model = MaterialBatch
fields = ['material', 'warehouse']

View File

@ -0,0 +1,84 @@
# Generated by Django 3.2.6 on 2021-10-25 07:33
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('mtm', '0025_outputmaterial_is_main'),
('pm', '0007_auto_20211025_1533'),
('inm', '0004_auto_20210929_0842'),
]
operations = [
migrations.AddField(
model_name='fifo',
name='inout_date',
field=models.DateField(default=django.utils.timezone.now, verbose_name='出入库日期'),
preserve_default=False,
),
migrations.AddField(
model_name='fifo',
name='operator',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='system.user', verbose_name='操作人'),
preserve_default=False,
),
migrations.AddField(
model_name='fifo',
name='subproduction_plan',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='pm.subproductionplan', verbose_name='关联子生产计划'),
),
migrations.AddField(
model_name='fifo',
name='warehouse',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='inm.warehouse', verbose_name='仓库'),
preserve_default=False,
),
migrations.AlterField(
model_name='materialbatch',
name='batch',
field=models.CharField(blank=True, max_length=100, null=True, unique=True, verbose_name='批次号'),
),
migrations.CreateModel(
name='IProduct',
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='删除标记')),
('state', models.IntegerField(default=1, verbose_name='物品状态')),
('number', models.CharField(blank=True, max_length=50, null=True, unique=True, verbose_name='物品编号')),
('batch', models.CharField(blank=True, max_length=100, null=True, verbose_name='所属批次号')),
('fifos', models.JSONField(blank=True, default=list, verbose_name='关联出入库记录')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='iproduct_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('material', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='物料类型')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='iproduct_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
('warehouse', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inm.warehouse', verbose_name='所在仓库')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='FIFODetail',
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.IntegerField(default=0, verbose_name='数量')),
('batch', models.CharField(blank=True, max_length=100, null=True, verbose_name='批次号')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='fifodetail_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('material', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='物料类型')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='fifodetail_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.6 on 2021-10-27 01:41
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('inm', '0005_auto_20211025_1533'),
]
operations = [
migrations.AddField(
model_name='fifodetail',
name='fifo',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='inm.fifo', verbose_name='关联出入库'),
preserve_default=False,
),
]

View File

@ -0,0 +1,34 @@
# Generated by Django 3.2.6 on 2021-10-28 05:31
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inm', '0006_auto_20211027_0941'),
]
operations = [
migrations.RemoveField(
model_name='fifodetail',
name='create_by',
),
migrations.RemoveField(
model_name='fifodetail',
name='update_by',
),
migrations.RemoveField(
model_name='iproduct',
name='create_by',
),
migrations.RemoveField(
model_name='iproduct',
name='update_by',
),
migrations.AddField(
model_name='fifo',
name='remark',
field=models.CharField(default='', max_length=1000, verbose_name='备注'),
),
]

View File

@ -41,7 +41,7 @@ class MaterialBatch(BaseModel):
material = models.ForeignKey(Material, on_delete=models.CASCADE, verbose_name='物料信息')
warehouse = models.ForeignKey(WareHouse, on_delete=models.CASCADE, verbose_name='所在仓库')
count = models.IntegerField('存量', default=0)
batch = models.CharField('批次号', max_length=100, null=True, blank=True)
batch = models.CharField('批次号', max_length=100, null=True, blank=True, unique=True)
expiration_date = models.DateField('有效期', null=True, blank=True)
class Meta:
verbose_name = '库存表'
@ -60,22 +60,38 @@ class FIFO(CommonAModel):
(4, '生产入库')
)
type = models.IntegerField('出入库类型', default=1)
warehouse = models.ForeignKey(WareHouse, on_delete=models.CASCADE, verbose_name='仓库')
operator = models.ForeignKey(User, verbose_name='操作人', on_delete=models.CASCADE)
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.DO_NOTHING, null=True, blank=True)
inout_date = models.DateField('出入库日期')
remark = models.CharField('备注', max_length=1000, default='')
# class FIFODetail(CommonAModel):
# """
# 领料详细记录
# """
class Product(CommonAModel):
class FIFODetail(BaseModel):
"""
具体产品
出入库详细记录
"""
material = models.ForeignKey(Material, verbose_name='物料类型', on_delete=models.CASCADE)
count = models.IntegerField('数量', default=0)
batch = models.CharField('批次号', max_length=100, null=True, blank=True)
fifo = models.ForeignKey(FIFO, verbose_name='关联出入库', on_delete=models.CASCADE)
class IProduct(BaseModel):
"""
具体产品条目
"""
inm_product_state_choices = (
(1, '可用'),
(2, '锁定'),
(3, '已消耗')
)
state = models.IntegerField('物品状态', default=1)
number = models.CharField('物品编号', unique=True, null=True, blank=True, max_length=50)
material = models.ForeignKey(Material, verbose_name='物料类型', on_delete=models.CASCADE)
warehouse = models.ForeignKey(WareHouse, on_delete=models.CASCADE, verbose_name='所在仓库')
batch = models.CharField('所属批次号', max_length=100, null=True, blank=True)
wproduct = models.ForeignKey('wpm.wproduct', on_delete=models.CASCADE, verbose_name='关联的动态产品', db_constraint=False, null=True, blank=True)
fifos = models.JSONField('关联出入库记录', default=list, blank=True)

View File

@ -1,6 +1,6 @@
from rest_framework import serializers
from apps.inm.models import WareHouse,Inventory
from apps.inm.models import FIFO, FIFODetail, IProduct, MaterialBatch, WareHouse,Inventory
from apps.system.serializers import UserSimpleSerializer
from apps.mtm.serializers import MaterialSimpleSerializer
@ -23,12 +23,95 @@ class WareHouseSimpleSerializer(serializers.ModelSerializer):
class InventorySerializer(serializers. ModelSerializer):
material_= MaterialSimpleSerializer(source='material', read_only=True)
warehouse_ = WareHouseSimpleSerializer(source='material', read_only=True)
warehouse_ = WareHouseSimpleSerializer(source='warehouse', read_only=True)
class Meta:
model = Inventory
fields = '__all__'
class InventoryCreateUpdateSerializer(serializers.ModelSerializer):
class MaterialBatchSerializer(serializers. ModelSerializer):
material_= MaterialSimpleSerializer(source='material', read_only=True)
warehouse_ = WareHouseSimpleSerializer(source='warehouse', read_only=True)
class Meta:
model = Inventory
fields = ['material', 'count', 'warehouse']
model = MaterialBatch
fields = '__all__'
class FIFOListSerializer(serializers.ModelSerializer):
warehouse_ = WareHouseSimpleSerializer(source='warehouse', read_only=True)
operator_ = UserSimpleSerializer(source='operator', read_only=True)
class Meta:
model=FIFO
fields = '__all__'
class FIFODetailSerializer(serializers.ModelSerializer):
material_= MaterialSimpleSerializer(source='material', read_only=True)
class Meta:
model=FIFODetail
fields = '__all__'
class IProductInPurSerializer(serializers.ModelSerializer):
class Meta:
model = IProduct
fields = ['number']
class FIFODetailInPurSerializer(serializers.ModelSerializer):
details = IProductInPurSerializer(many=True, required=False)
class Meta:
model = FIFODetail
fields = ['material', 'count', 'batch', 'details']
class MaterialBatchQuerySerializer(serializers.Serializer):
warehouse = serializers.IntegerField(label="仓库ID", required=False)
materials = serializers.ListField(child=serializers.IntegerField(label="物料ID"), required=False)
class FIFOInPurSerializer(serializers.ModelSerializer):
"""
采购入库序列化
"""
details = FIFODetailInPurSerializer(many=True)
class Meta:
model = FIFO
fields = ['warehouse', 'operator', 'details', 'inout_date']
def create(self, validated_data):
details = validated_data.pop('details')
if len(details)>0:
pass
else:
raise serializers.ValidationError('没有入库内容')
for i in details:
# 校验批次
try:
obj = MaterialBatch.objects.get(batch=i['batch'])
if obj.warehouse != validated_data['warehouse']:
raise serializers.ValidationError('批次号{}在其他仓库已存在'.format(i['batch']))
except:
pass
# 创建采购入库
validated_data['type'] = 3
obj = FIFO(**validated_data)
obj.save()
for i in details:
if 'details' in i:
p_details = i.pop('details')
if len(p_details) != i['count']:
raise serializers.ValidationError('数目对不上')
else:
i['fifo'] = obj
fifod = FIFODetail.objects.create(**i)
p_list = []
for x in p_details:
x['state'] = 1
x['material'] = i['material']
x['warehouse'] = validated_data['warehouse']
x['batch'] = i['batch']
x['fifos'] = [fifod.id]
p_list.append(IProduct(**x))
IProduct.objects.bulk_create(p_list)
else:
i['fifo'] = obj
FIFODetail.objects.create(**i)
return obj

View File

@ -0,0 +1,35 @@
from django.db.models.signals import post_save
from django.dispatch import receiver
from apps.inm.models import FIFODetail, Inventory, MaterialBatch
@receiver(post_save, sender=FIFODetail)
def update_by_fifodetail(sender, instance, created, **kwargs):
if created:
fifo = instance.fifo
material = instance.material
warehouse = fifo.warehouse
if fifo.type in [3]: # 采购入库
# 更新相关表
o1, _ = Inventory.objects.get_or_create(material=material, warehouse=warehouse, \
defaults={'material':material, 'warehouse':warehouse, 'count':0})
o1.count = o1.count + instance.count
o1.save()
o2, _ = MaterialBatch.objects.get_or_create(material=material, warehouse=warehouse, batch=instance.batch,\
defaults={'material':material, 'warehouse':warehouse, 'count':0, 'batch':instance.batch})
o2.count = o2.count + instance.count
o2.save()
material.count = material.count + instance.count
material.save()
elif fifo.type in [1]: # 生产领料
# 更新相关表
o1 = Inventory.objects.get(material=material, warehouse=warehouse)
o1.count = o1.count - instance.count
o1.save()
o2 = MaterialBatch.objects.get(material=material, warehouse=warehouse, batch=instance.batch)
o2.count = o2.count - instance.count
o2.save()
material.count = material.count - instance.count
material.save()

View File

@ -1,12 +1,15 @@
from django.db.models import base
from rest_framework import urlpatterns
from apps.inm.views import WarehouseViewSet,InventoryViewSet
from apps.inm.views import FIFODetailViewSet, FIFOViewSet, MaterialBatchViewSet, WarehouseViewSet,InventoryViewSet
from django.urls import path, include
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register('warehouse', WarehouseViewSet, basename='warehouse')
router.register('inventory', InventoryViewSet, basename='inventory')
router.register('materialbatch', MaterialBatchViewSet, basename='materialbatch')
router.register('fifo', FIFOViewSet, basename='fifo'),
router.register('fifodetail', FIFODetailViewSet, basename='fifodetail')
urlpatterns = [
path('', include(router.urls)),
]

View File

@ -1,11 +1,14 @@
from django.shortcuts import render
from rest_framework.mixins import ListModelMixin
from rest_framework import serializers
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from apps.inm.filters import MbFilterSet
from apps.inm.models import WareHouse,Inventory
from apps.inm.serializers import WareHouseSerializer, WareHouseCreateUpdateSerializer,InventorySerializer,InventoryCreateUpdateSerializer
from apps.inm.models import FIFO, FIFODetail, MaterialBatch, WareHouse,Inventory
from apps.inm.serializers import FIFODetailSerializer, FIFOInPurSerializer, FIFOListSerializer, MaterialBatchQuerySerializer, MaterialBatchSerializer, WareHouseSerializer, WareHouseCreateUpdateSerializer,InventorySerializer
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
from rest_framework.decorators import action
from rest_framework.response import Response
# Create your views here.
class WarehouseViewSet(CreateUpdateModelAMixin, ModelViewSet):
@ -27,11 +30,70 @@ class WarehouseViewSet(CreateUpdateModelAMixin, ModelViewSet):
class InventoryViewSet(ListModelMixin, GenericViewSet):
"""
物料基本信息-增删改查
仓库物料表
"""
perms_map = {'*': '*'}
queryset = Inventory.objects.select_related('create_by').all()
queryset = Inventory.objects.select_related('material', 'warehouse').all()
serializer_class = InventorySerializer
filterset_fields = []
filterset_fields = ['material', 'warehouse']
search_fields = []
ordering_fields = ['create_time']
ordering = ['-create_time']
class MaterialBatchViewSet(ListModelMixin, GenericViewSet):
perms_map = {'*': '*'}
queryset = MaterialBatch.objects.select_related('material', 'warehouse').all()
serializer_class = MaterialBatchSerializer
# filterset_fields = ['material', 'warehouse']
filterset_class = MbFilterSet
search_fields = []
ordering_fields = ['create_time']
ordering = ['-create_time']
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=MaterialBatchQuerySerializer)
def query(self, request, pk=None):
"""
复杂查询
"""
data = request.data
serializer = MaterialBatchQuerySerializer(data=data)
serializer.is_valid(raise_exception=True)
queryset = self.queryset.filter(warehouse__id=data['warehouse'], material__id__in=data['materials'])
return Response(MaterialBatchSerializer(instance=queryset, many=True).data)
class FIFODetailViewSet(ListModelMixin, GenericViewSet):
"""
出入库记录详情表
"""
perms_map = {'*': '*'}
queryset = FIFODetail.objects.select_related('material', 'fifo').all()
serializer_class = FIFODetailSerializer
filterset_fields = ['material', 'fifo']
search_fields = []
ordering_fields = ['create_time']
ordering = ['-create_time']
class FIFOViewSet(ListModelMixin, GenericViewSet):
"""
出入库记录
"""
perms_map = {'*': '*'}
queryset = FIFO.objects.select_related('warehouse', 'operator')
serializer_class = FIFOListSerializer
filterset_fields = ['warehouse', 'type']
def get_serializer_class(self):
if self.action == 'list':
return FIFOListSerializer
return super().get_serializer_class()
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=FIFOInPurSerializer)
def in_pur(self, request, pk=None):
"""
采购入库
"""
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save(create_by=request.user)
return Response()

View File

@ -0,0 +1,86 @@
# Generated by Django 3.2.6 on 2021-11-01 07:22
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('mtm', '0025_outputmaterial_is_main'),
]
operations = [
migrations.CreateModel(
name='SubplanMaterial',
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='删除标记')),
('is_main', models.BooleanField(default=False, verbose_name='是否主产出')),
('count', models.FloatField(default=1, verbose_name='消耗量/产出量')),
('type', models.IntegerField(default=1, verbose_name='物料应用类型')),
('sort', models.IntegerField(default=1, verbose_name='排序号')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='subplanmaterial_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
],
options={
'abstract': False,
},
),
migrations.RemoveField(
model_name='outputmaterial',
name='create_by',
),
migrations.RemoveField(
model_name='outputmaterial',
name='material',
),
migrations.RemoveField(
model_name='outputmaterial',
name='subproduction',
),
migrations.RemoveField(
model_name='outputmaterial',
name='update_by',
),
migrations.AlterField(
model_name='material',
name='name',
field=models.CharField(max_length=100, verbose_name='物料名称'),
),
migrations.AlterField(
model_name='material',
name='type',
field=models.CharField(choices=[(1, '成品'), (2, '半成品'), (3, '主要原料'), (4, '辅助材料'), (5, '加工工具'), (6, '辅助工装')], default=1, max_length=20, verbose_name='物料类型'),
),
migrations.AlterField(
model_name='material',
name='unit',
field=models.CharField(choices=[('', ''), ('', ''), ('', ''), ('m2', 'm2'), ('', '')], default='', max_length=10, verbose_name='基准计量单位'),
),
migrations.DeleteModel(
name='InputMaterial',
),
migrations.DeleteModel(
name='OutputMaterial',
),
migrations.AddField(
model_name='subplanmaterial',
name='material',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='subplan_material', to='mtm.material', verbose_name='物料'),
),
migrations.AddField(
model_name='subplanmaterial',
name='subproduction',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.subproduction', verbose_name='关联生产分解'),
),
migrations.AddField(
model_name='subplanmaterial',
name='update_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='subplanmaterial_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
),
]

View File

@ -16,15 +16,18 @@ class Material(CommonAModel):
(1, '成品'),
(2, '半成品'),
(3, '主要原料'),
(4, '辅助') ,
(4, '辅助') ,
(5, '加工工具'),
(6, '辅助工')
(6, '辅助工')
)
unit_choices =(
('', ''),
('', '')
('', ''),
('', ''),
('m2', 'm2'),
('', '')
)
name = models.CharField('物料名称', max_length=100, unique=True)
name = models.CharField('物料名称', max_length=100)
number = models.CharField('编号', max_length=100, unique=True)
specification = models.CharField('型号', max_length=100, null=True, blank=True)
type = models.CharField('物料类型', choices= type_choices, max_length=20, default=1)
@ -156,35 +159,22 @@ class SubProduction(CommonAModel):
verbose_name = '产品生产工序'
verbose_name_plural = verbose_name
class InputMaterial(CommonAModel):
class SubplanMaterial(CommonAModel):
"""
输入物料
输入/输出物料/工具工装
"""
material = models.ForeignKey(Material, verbose_name='输入物料', on_delete=models.CASCADE, related_name='inputmaterial')
count = models.FloatField('消耗量', default=1)
type_choices=(
(1, '输入物料'),
(2, '输出物料'),
(3, '工具工装')
)
material = models.ForeignKey(Material, verbose_name='物料', on_delete=models.CASCADE, related_name='subplan_material')
is_main = models.BooleanField('是否主产出', default=False) # 以该产品完成度计算进度
count = models.FloatField('消耗量/产出量', default=1)
subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE)
type = models.IntegerField('物料应用类型', default=1)
sort = models.IntegerField('排序号', default=1)
class Meta:
verbose_name = '输入物料'
verbose_name_plural = verbose_name
class OutputMaterial(CommonAModel):
"""
输出物料
"""
material = models.ForeignKey(Material, verbose_name='输出物料', on_delete=models.CASCADE, related_name='outputmaterial')
is_main = models.BooleanField('是否主产出', default=True) # 以该产品完成度计算进度
count = models.FloatField('产出量', default=1)
subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE)
sort = models.IntegerField('排序号', default=1)
class Meta:
verbose_name = '输出物料'
verbose_name_plural = verbose_name
class UsedStep(CommonAModel):
"""

View File

@ -1,7 +1,7 @@
from apps.em.serializers import EquipmentSimpleSerializer
from rest_framework import serializers
from rest_framework.exceptions import ParseError, ValidationError
from .models import InputMaterial, Material, OutputMaterial, Process, RecordForm, RecordFormField, Step, TechDoc, UsedStep, SubProduction
from .models import Material, Process, RecordForm, RecordFormField, Step, SubplanMaterial, TechDoc, UsedStep, SubProduction
from apps.system.serializers import FileSimpleSerializer, OrganizationSimpleSerializer
@ -19,7 +19,7 @@ class MaterialDetailSerializer(serializers.ModelSerializer):
def get_processes_(self, obj):
# steps = UsedStep.objects.filter(subproduction__product=obj).values_list('step', flat=True)
# objs = Process.objects.filter(step_process__id__in=steps).distinct().order_by('number')
objs = Process.objects.filter(subproduction_process__product=obj, subproduction_process__is_deleted=False, is_deleted=False).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
@ -51,7 +51,7 @@ class StepSimpleSerializer(serializers.ModelSerializer):
fields = ['id', 'name', 'sort']
class StepDetailSerializer(serializers.ModelSerializer):
equipments_ = EquipmentSimpleSerializer(source='equipments', many=True)
equipments_ = EquipmentSimpleSerializer(source='equipments', many=True, required=False)
class Meta:
model = Step
fields = '__all__'
@ -67,51 +67,59 @@ class SubProductionSerializer(serializers.ModelSerializer):
model = SubProduction
fields = '__all__'
class InputMaterialListSerializer(serializers.ModelSerializer):
class OtherMaterialSerializer(serializers.ModelSerializer):
class Meta:
model = SubplanMaterial
fields = ['sort', 'material', 'subproduction']
def create(self, validated_data):
if SubplanMaterial.objects.filter(material=validated_data['material'], subproduction=validated_data['subproduction'], is_deleted=False, type=3).exists():
raise ValidationError('该物料已存在')
validated_data['type']=3
return super().create(validated_data)
class SubplanMaterialListSerializer(serializers.ModelSerializer):
material_ = MaterialSimpleSerializer(source='material', read_only=True)
class Meta:
model = InputMaterial
model = SubplanMaterial
fields = '__all__'
class OutputMaterialListSerializer(serializers.ModelSerializer):
material_ = MaterialSimpleSerializer(source='material', read_only=True)
class Meta:
model = OutputMaterial
fields = '__all__'
class InputMaterialSerializer(serializers.ModelSerializer):
class Meta:
model = InputMaterial
model = SubplanMaterial
fields = ['count', 'sort', 'material', 'subproduction']
def create(self, validated_data):
if InputMaterial.objects.filter(material=validated_data['material'], subproduction=validated_data['subproduction'], is_deleted=False).exists():
if SubplanMaterial.objects.filter(material=validated_data['material'], subproduction=validated_data['subproduction'], is_deleted=False, type=1).exists():
raise ValidationError('该物料已存在')
validated_data['type']=1
return super().create(validated_data)
class InputMaterialUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = InputMaterial
model = SubplanMaterial
fields = ['count', 'sort']
class OutputMaterialSerializer(serializers.ModelSerializer):
class Meta:
model = OutputMaterial
model = SubplanMaterial
fields = ['count', 'sort', 'material', 'subproduction', 'is_main']
def create(self, validated_data):
if OutputMaterial.objects.filter(subproduction=validated_data['subproduction'], is_deleted=False, is_main=True).exists():
if SubplanMaterial.objects.filter(subproduction=validated_data['subproduction'], is_deleted=False, is_main=True, type=2).exists():
raise ValidationError('主产出只能有1个')
if OutputMaterial.objects.filter(material=validated_data['material'], subproduction=validated_data['subproduction'], is_deleted=False).exists():
if SubplanMaterial.objects.filter(material=validated_data['material'], subproduction=validated_data['subproduction'], is_deleted=False, type=2).exists():
raise ValidationError('该物料已存在')
validated_data['type']=2
return super().create(validated_data)
class OutputMaterialUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = OutputMaterial
model = SubplanMaterial
fields = ['count', 'sort', 'is_main']
class UsedStepCreateSerializer(serializers.ModelSerializer):
"""
产品生产子工序创建

View File

@ -1,6 +1,6 @@
from django.db.models import base
from rest_framework import urlpatterns
from apps.mtm.views import InputMaterialViewSet, MaterialViewSet, OutputMaterialViewSet, ProcessViewSet, RecordFormFieldViewSet, RecordFormViewSet, StepViewSet, SubProductionViewSet, TechDocViewSet, UsedStepViewSet
from apps.mtm.views import InputMaterialViewSet, MaterialViewSet, OtherMaterialViewSet, OutputMaterialViewSet, ProcessViewSet, RecordFormFieldViewSet, RecordFormViewSet, StepViewSet, SubProductionViewSet, TechDocViewSet, UsedStepViewSet
from django.urls import path, include
from rest_framework.routers import DefaultRouter
@ -11,6 +11,7 @@ router.register('step', StepViewSet, basename='step')
router.register('subproducation', SubProductionViewSet, basename='subproducation')
router.register('inputmaterial', InputMaterialViewSet, basename='inputmaterial')
router.register('outputmaterial', OutputMaterialViewSet, basename='outputmaterial')
router.register('othermaterial', OtherMaterialViewSet, basename='othermaterial')
router.register('usedstep', UsedStepViewSet, basename='usedstep')
router.register('recordform', RecordFormViewSet, basename='recordform')
router.register('recordform-field', RecordFormFieldViewSet, basename='recordform-field')

View File

@ -2,8 +2,8 @@ from django.shortcuts import render
from rest_framework.viewsets import ModelViewSet, GenericViewSet
from rest_framework.mixins import CreateModelMixin, ListModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin
from apps.mtm.models import InputMaterial, Material, OutputMaterial, Process, RecordForm, RecordFormField, Step, TechDoc, UsedStep, SubProduction
from apps.mtm.serializers import InputMaterialListSerializer, InputMaterialSerializer, InputMaterialUpdateSerializer, MaterialDetailSerializer, MaterialSerializer, MaterialSimpleSerializer, OutputMaterialListSerializer, OutputMaterialSerializer, OutputMaterialUpdateSerializer, ProcessSerializer, RecordFormCreateSerializer, RecordFormFieldCreateSerializer, RecordFormFieldSerializer, RecordFormFieldUpdateSerializer, RecordFormSerializer, RecordFormUpdateSerializer, StepDetailSerializer, StepSerializer, SubProductionSerializer, TechDocCreateSerializer, TechDocListSerializer, TechDocUpdateSerializer, UsedStepCreateSerializer, UsedStepListSerializer, UsedStepUpdateSerializer
from apps.mtm.models import Material, Process, RecordForm, RecordFormField, Step, SubplanMaterial, TechDoc, UsedStep, SubProduction
from apps.mtm.serializers import InputMaterialSerializer, InputMaterialUpdateSerializer, MaterialDetailSerializer, MaterialSerializer, MaterialSimpleSerializer, OtherMaterialSerializer, OutputMaterialSerializer, OutputMaterialUpdateSerializer, ProcessSerializer, RecordFormCreateSerializer, RecordFormFieldCreateSerializer, RecordFormFieldSerializer, RecordFormFieldUpdateSerializer, RecordFormSerializer, RecordFormUpdateSerializer, StepDetailSerializer, StepSerializer, SubProductionSerializer, SubplanMaterialListSerializer, TechDocCreateSerializer, TechDocListSerializer, TechDocUpdateSerializer, UsedStepCreateSerializer, UsedStepListSerializer, UsedStepUpdateSerializer
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
from rest_framework.decorators import action
from rest_framework.response import Response
@ -85,14 +85,14 @@ class InputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet):
输入物料-增删改查
"""
perms_map = {'*':'*'}
queryset = InputMaterial.objects.select_related('material').all()
queryset = SubplanMaterial.objects.select_related('material').filter(type=1)
serializer_class = InputMaterialSerializer
filterset_fields = ['subproduction']
ordering = ['sort', '-create_time']
def get_serializer_class(self):
if self.action == 'list':
return InputMaterialListSerializer
return SubplanMaterialListSerializer
elif self.action == 'update':
return InputMaterialUpdateSerializer
return InputMaterialSerializer
@ -102,18 +102,33 @@ class OutputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet):
输出物料-增删改查
"""
perms_map = {'*':'*'}
queryset = OutputMaterial.objects.select_related('material').all()
queryset = SubplanMaterial.objects.select_related('material').filter(type=2)
serializer_class = OutputMaterialSerializer
filterset_fields = ['subproduction']
ordering = ['sort', '-create_time']
def get_serializer_class(self):
if self.action == 'list':
return OutputMaterialListSerializer
return SubplanMaterialListSerializer
elif self.action == 'update':
return OutputMaterialUpdateSerializer
return OutputMaterialSerializer
class OtherMaterialViewSet(CreateUpdateModelAMixin, ListModelMixin, DestroyModelMixin, CreateModelMixin, GenericViewSet):
"""
其他物料-增删改查
"""
perms_map = {'*':'*'}
queryset = SubplanMaterial.objects.select_related('material').filter(type=3)
serializer_class = OutputMaterialSerializer
filterset_fields = ['subproduction']
ordering = ['sort', '-create_time']
def get_serializer_class(self):
if self.action == 'list':
return SubplanMaterialListSerializer
return OtherMaterialSerializer
class UsedStepViewSet(OptimizationMixin, CreateModelMixin, DestroyModelMixin, ListModelMixin, UpdateModelMixin, GenericViewSet):
"""
产品生产子工序表

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.6 on 2021-10-25 07:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pm', '0006_alter_subproductionprogress_count_real'),
]
operations = [
migrations.AddField(
model_name='subproductionplan',
name='end_date_real',
field=models.DateField(blank=True, null=True, verbose_name='实际完工日期'),
),
migrations.AddField(
model_name='subproductionplan',
name='start_date_real',
field=models.DateField(blank=True, null=True, verbose_name='实际开工日期'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-10-28 06:47
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pm', '0007_auto_20211025_1533'),
]
operations = [
migrations.AddField(
model_name='subproductionprogress',
name='count_current',
field=models.IntegerField(default=0, verbose_name='当前数量'),
),
]

View File

@ -0,0 +1,27 @@
# Generated by Django 3.2.6 on 2021-10-29 02:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pm', '0008_subproductionprogress_count_current'),
]
operations = [
migrations.RemoveField(
model_name='subproductionprogress',
name='count_current',
),
migrations.AddField(
model_name='subproductionplan',
name='is_picked',
field=models.BooleanField(default=False, verbose_name='是否已领料'),
),
migrations.AlterField(
model_name='subproductionprogress',
name='count',
field=models.FloatField(verbose_name='应出入数'),
),
]

View File

@ -1,3 +1,4 @@
from io import open_code
from apps.system.models import CommonAModel, Organization
from django.db import models
from django.contrib.auth.models import AbstractUser
@ -48,20 +49,22 @@ class SubProductionPlan(CommonAModel):
state = models.IntegerField('状态', default=0)
start_date_real = models.DateField('实际开工日期', null=True, blank=True)
end_date_real = models.DateField('实际完工日期', null=True, blank=True)
is_picked = models.BooleanField('是否已领料', default=False)
class Meta:
verbose_name = '子生产计划'
verbose_name_plural = verbose_name
class SubProductionProgress(BaseModel):
"""
子计划生产进度统计表
子计划生产进度统计表/物料消耗
"""
type_choices=(
(1, '输入物料'),
(2, '输出物料')
(2, '输出物料'),
(3, '工具工装')
)
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.CASCADE, related_name='progress_subplan')
material = models.ForeignKey(Material, verbose_name='关联物料', on_delete=models.CASCADE)
type = models.IntegerField('物料应用类型', default=1)
count = models.IntegerField('应出入数')
count = models.FloatField('应出入数')
count_real = models.IntegerField('实际出入数', default=0)

View File

@ -44,3 +44,9 @@ class SubProductionProgressSerializer(serializers.ModelSerializer):
class Meta:
model = SubProductionProgress
fields = '__all__'
class PickNeedSerializer(serializers.Serializer):
warehouse = serializers.IntegerField(label="仓库ID")
class PlanDestorySerializer(serializers.Serializer):
ids = serializers.ListField(child=serializers.IntegerField(), label='主计划ID列表')

View File

@ -3,9 +3,11 @@ from rest_framework import serializers
from rest_framework.views import APIView
from apps.em.models import Equipment
from apps.em.serializers import EquipmentSerializer
from apps.mtm.models import InputMaterial, OutputMaterial, Step, SubProduction, UsedStep
from apps.inm.models import MaterialBatch
from apps.inm.serializers import MaterialBatchSerializer
from apps.mtm.models import Step, SubProduction, UsedStep
from apps.system.mixins import CreateUpdateModelAMixin
from apps.pm.serializers import GenSubPlanSerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer, SubProductionProgressSerializer
from apps.pm.serializers import GenSubPlanSerializer, PickNeedSerializer, PlanDestorySerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer, SubProductionProgressSerializer
from rest_framework.mixins import CreateModelMixin, ListModelMixin, UpdateModelMixin
from apps.pm.models import ProductionPlan, SubProductionProgress, SubProductionPlan
from rest_framework.viewsets import GenericViewSet, ModelViewSet
@ -61,6 +63,14 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel
updateOrderPlanedCount(instance.order)
return Response()
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=PlanDestorySerializer)
def destory(self, request, pk=None):
"""
批量物理删除
"""
ProductionPlan.objects.filter(id__in=request.data.get('ids', [])).delete(soft=False)
return Response()
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=GenSubPlanSerializer)
def gen_subplan(self, request, pk=None):
"""
@ -77,10 +87,8 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel
start_date=production_plan.start_date, end_date=production_plan.end_date,
workshop=i.process.workshop, process=i.process, create_by=request.user,
steps = list(steps))
for m in InputMaterial.objects.filter(subproduction=i, is_deleted=False).order_by('sort'):
SubProductionProgress.objects.create(material=m.material, type=1, count=m.count, subproduction_plan=instance)
for m in OutputMaterial.objects.filter(subproduction=i, is_deleted=False).order_by('sort'):
SubProductionProgress.objects.create(material=m.material, type=2, count=m.count, subproduction_plan=instance)
for m in SubProduction.objects.filter(subproduction=i, is_deleted=False).order_by('sort'):
SubProductionProgress.objects.create(material=m.material, type=m.type, count=m.count*production_plan.count, subproduction_plan=instance)
production_plan.is_planed=True
production_plan.save()
return Response()
@ -92,7 +100,8 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo
perms_map = {'*': '*'}
queryset = SubProductionPlan.objects.select_related('process', 'workshop')
search_fields = []
filterset_fields = ['production_plan']
serializer_class = SubProductionPlanListSerializer
filterset_fields = ['production_plan', 'process', 'state']
ordering_fields = ['process__number']
ordering = ['process__number']
@ -101,7 +110,7 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo
return SubProductionPlanListSerializer
elif self.action == 'update':
return SubProductionPlanUpdateSerializer
return SubProductionPlanListSerializer
return super().get_serializer_class()
@action(methods=['get'], detail=True, perms_map={'get':'*'}, serializer_class=SubProductionProgressSerializer)
def progress(self, request, pk=None):
@ -137,6 +146,33 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo
return Response()
raise APIException('计划状态有误')
@action(methods=['get'], detail=True, perms_map={'get':'*'}, serializer_class=serializers.Serializer)
def pick_need_(self, request, pk=None):
"""
领料需求清单
"""
obj = self.get_object()
instance = SubProductionProgress.objects.filter(subproduction_plan=obj, type=1)
serializer = SubProductionProgressSerializer(instance=instance, many=True)
return Response(serializer.data)
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=PickNeedSerializer)
def pick_need(self, request, pk=None):
"""
领料需求清单/库存数
"""
obj = self.get_object()
instance = SubProductionProgress.objects.filter(subproduction_plan=obj, type=1)
serializer = SubProductionProgressSerializer(instance=instance, many=True)
need = serializer.data
materials = []
for i in need:
materials.append(i['material'])
objs = MaterialBatch.objects.filter(warehouse=request.data['warehouse'], material__id__in=materials)
have = MaterialBatchSerializer(instance=objs, many=True).data
return Response({'need':need, 'have':have})
class ResourceViewSet(GenericViewSet):
perms_map = {'*': '*'}

View File

@ -1,4 +1,5 @@
from django.db.models import query
from rest_framework.utils import serializer_helpers
from rest_framework.views import APIView
from apps.system.models import User
from apps.wf.filters import TicketFilterSet

View File

@ -0,0 +1,85 @@
# Generated by Django 3.2.6 on 2021-10-29 02:19
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
('pm', '0009_auto_20211029_1017'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('mtm', '0025_outputmaterial_is_main'),
]
operations = [
migrations.CreateModel(
name='WProduct',
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='删除标记')),
('number', models.CharField(blank=True, max_length=50, null=True, unique=True, verbose_name='物品编号')),
('act_state', models.IntegerField(default=0, verbose_name='进行状态')),
('remark', models.CharField(blank=True, max_length=200, null=True, verbose_name='备注')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproduct_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('m_state', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='所属物料状态')),
('p_state', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.step', verbose_name='所在步骤')),
('parent', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.CASCADE, to='wpm.wproduct', verbose_name='上一级')),
('subproduction_plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pm.subproductionplan', verbose_name='关联子生产计划')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproduct_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='WProductForm',
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='删除标记')),
('data', models.JSONField(blank=True, default=dict, verbose_name='记录的数据')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproductform_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('record_form', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.recordform', verbose_name='所用表格')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproductform_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='WProductFlow',
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='删除标记')),
('wproduct', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.wproduct', verbose_name='产品')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='WMaterial',
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='删除标记')),
('batch', models.CharField(blank=True, max_length=100, null=True, verbose_name='批次号')),
('count', models.IntegerField(default=0, verbose_name='当前数量')),
('material', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='关联物料')),
],
options={
'abstract': False,
},
),
]

View File

@ -0,0 +1,28 @@
# Generated by Django 3.2.6 on 2021-10-29 05:36
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('system', '0003_auto_20210812_0909'),
('mtm', '0025_outputmaterial_is_main'),
('wpm', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='wmaterial',
name='process',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='mtm.process', verbose_name='关联大工序'),
preserve_default=False,
),
migrations.AddField(
model_name='wmaterial',
name='workshop',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='system.organization', verbose_name='生产车间'),
preserve_default=False,
),
]

View File

@ -2,14 +2,25 @@ from django.db import models
from django.db.models.base import Model
import django.utils.timezone as timezone
from django.db.models.query import QuerySet
from apps.pm.models import ProductionPlan, SubProductionPlan
from apps.system.models import CommonAModel, CommonBModel, Organization, User, Dict, File
from utils.model import SoftModel, BaseModel
from simple_history.models import HistoricalRecords
from apps.mtm.models import Material, Step, RecordForm
from apps.mtm.models import Material, Process, Step, RecordForm
class Product(CommonAModel):
class WMaterial(BaseModel):
"""
产品(所有生产过程中出现过的)
车间生产物料
"""
workshop = models.ForeignKey(Organization, verbose_name='生产车间', on_delete=models.CASCADE)
process = models.ForeignKey(Process, verbose_name='关联大工序', on_delete=models.CASCADE)
material = models.ForeignKey(Material, verbose_name='关联物料', on_delete=models.CASCADE)
batch = models.CharField('批次号', max_length=100, null=True, blank=True)
count = models.IntegerField('当前数量', default=0)
class WProduct(CommonAModel):
"""
半成品/成品
"""
act_state_choices=(
(0, '待执行'),
@ -22,8 +33,10 @@ class Product(CommonAModel):
act_state = models.IntegerField('进行状态', default=0)
parent = models.ForeignKey('self', verbose_name='上一级', on_delete=models.CASCADE, db_constraint=False)
remark = models.CharField('备注', max_length=200, null=True, blank=True)
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='当前子生产计划', on_delete=models.CASCADE)
production_plan = models.ForeignKey(ProductionPlan, verbose_name='关联主生产计划', on_delete=models.CASCADE)
class ProductForm(CommonAModel):
class WProductForm(CommonAModel):
"""
记录表格
"""
@ -31,8 +44,8 @@ class ProductForm(CommonAModel):
data = models.JSONField('记录的数据', default=dict, blank=True)
class ProductFlow(BaseModel):
class WProductFlow(BaseModel):
"""
产品流转日志
"""
product = models.ForeignKey(Product, verbose_name='产品', on_delete=models.CASCADE)
wproduct = models.ForeignKey(WProduct, verbose_name='产品', on_delete=models.CASCADE)

View File

@ -1,2 +1,71 @@
from rest_framework import serializers
from rest_framework.serializers import ModelSerializer
from apps.inm.models import FIFO, FIFODetail, MaterialBatch, WareHouse
from apps.mtm.models import Material
from apps.mtm.serializers import MaterialSimpleSerializer
from apps.pm.models import SubProductionPlan, SubProductionProgress
from django.utils import timezone
from apps.wpm.models import WMaterial
class PickDetailSerializer(serializers.Serializer):
material = serializers.PrimaryKeyRelatedField(queryset=Material.objects.all(), label="物料ID")
batch = serializers.CharField(label='物料批次')
pick_count = serializers.IntegerField(label="领料数量")
class PickSerializer(serializers.Serializer):
subproduction_plan=serializers.PrimaryKeyRelatedField(queryset=SubProductionPlan.objects.all(), label="子计划ID")
warehouse = serializers.PrimaryKeyRelatedField(queryset=WareHouse.objects.all(), label="仓库ID")
picks = PickDetailSerializer(many=True)
def create(self, validated_data):
picks = validated_data.pop('picks')
sp = validated_data['subproduction_plan']
if sp.is_picked:
raise serializers.ValidationError('该子计划已领料')
for i in picks:
try:
instance = MaterialBatch.objects.get(material=i['material'], batch=i['batch'])
if instance.count < i['pick_count']:
raise serializers.ValidationError('物料不足')
except:
raise serializers.ValidationError('物料不存在')
# 创建出库记录
operator = self.context['request'].user
validated_data['create_by'] = operator
validated_data['operator'] = operator
validated_data['type'] = 1
validated_data['inout_date'] = timezone.now()
fifo = FIFO.objects.create(validated_data)
for i in picks:
# 更新出库详情
i['fifo'] = fifo
FIFODetail.objects.create(**i)
# 更新车间物料
wm = WMaterial.objects.get_or_create(material=i['material'], batch=i['batch'], \
process=validated_data['process'],defaults={
'material':i['material'],
'batch':i['batch'],
'process':validated_data['process'],
'workshop':validated_data['workshop'],
'count':0
})
wm.count = wm.count + i['pick_count']
wm.save()
# 更新子计划进度
spp = SubProductionProgress.objects.get(material=i['material'], subproduction_plan=sp, type=1)
spp.count_real = spp.count_real + i['pick_count']
spp.save()
sp.is_picked=True
sp.save()
return fifo
class WMaterialListSerializer(serializers.ModelSerializer):
"""
车间物料
"""
material_ = MaterialSimpleSerializer(source='material', read_only=True)
class Meta:
model = Material
fields = '__all__'

View File

@ -3,8 +3,10 @@ from rest_framework import urlpatterns
from django.urls import path, include
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
from apps.wpm.views import WMaterialViewSet
router = DefaultRouter()
router.register('wmaterial', WMaterialViewSet, basename='wmaterial')
urlpatterns = [
path('', include(router.urls)),
]

View File

@ -1,6 +1,34 @@
from django.shortcuts import render
from rest_framework.viewsets import ModelViewSet
from rest_framework.generics import CreateAPIView, GenericAPIView
from rest_framework.mixins import ListModelMixin
from rest_framework.utils.field_mapping import get_relation_kwargs
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from apps.pm.serializers import SubProductionPlanUpdateSerializer
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
from rest_framework.decorators import action
from apps.wpm.models import WMaterial
from apps.wpm.serializers import PickSerializer, WMaterialListSerializer
from rest_framework.response import Response
# Create your views here.
class WMaterialViewSet(CreateUpdateModelAMixin, ListModelMixin, GenericViewSet):
"""
车间物料表
"""
perms_map={'*':'*'}
queryset = WMaterial.objects.select_related('material').all()
serializer_class = WMaterialListSerializer
filterset_fields = ['material', 'process', 'workshop']
ordering_fields = ['material__number']
ordering = ['material__number']
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=PickSerializer)
def pick(self, request, pk=None):
"""
领料
"""
serializer= PickSerializer(data=request.data, context={'request': request})
serializer.is_valid(raise_exception=True)
serializer.save()
return Response()

View File

@ -57,7 +57,7 @@ INSTALLED_APPS = [
'apps.sam',
'apps.qm',
'apps.pm',
# 'apps.wpm'
'apps.wpm'
]
MIDDLEWARE = [

View File

@ -68,6 +68,8 @@ urlpatterns = [
path('api/sam/', include('apps.sam.urls')),
path('api/qm/', include('apps.qm.urls')),
path('api/pm/', include('apps.pm.urls')),
path('api/wpm/', include('apps.wpm.urls')),
# 工具
path('api/utils/signature/', GenSignature.as_view()),