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

This commit is contained in:
shijing 2021-09-29 09:39:54 +08:00
commit 12c0ad0267
34 changed files with 939 additions and 34 deletions

View File

@ -2,8 +2,8 @@
ENV = 'development'
# base api
VUE_APP_BASE_API = 'http://127.0.0.1:8000/api'
#VUE_APP_BASE_API = 'http://47.95.0.242:2222/api'
#VUE_APP_BASE_API = 'http://127.0.0.1:8000/api'
VUE_APP_BASE_API = 'http://47.95.0.242:2222/api'
# vue-cli uses the VUE_CLI_BABEL_TRANSPILE_MODULES environment variable,

View File

@ -28,3 +28,32 @@ export function deleteWarehouse(id, data) {
data
})
}
//物料
export function getInventoryList(query) {
return request({
url: '/inm/inventory/',
method: 'get',
params: query
})
}
export function createInventory(data) {
return request({
url: '/inm/inventory/',
method: 'post',
data
})
}
export function updateInventory(id, data) {
return request({
url: `/inm/inventory/${id}/`,
method: 'put',
data
})
}
export function deleteInventory(id, data) {
return request({
url: `/inm/inventory/${id}/`,
method: 'delete',
data
})
}

View File

@ -93,6 +93,14 @@ export const asyncRoutes = [
name: 'material',
component: () => import('@/views/mtm/material'),
meta: { title: '物料清单', icon: 'example', perms: ['vendor_manage'] }
}
,
{
path: 'material/:id',
name: 'MaterialDO',
component: () => import('@/views/mtm/materialdo.vue'),
meta: { title: '绑定检查表', perms: ['vendor_manage'] },
hidden: true
},
{
path: 'process',
@ -225,12 +233,26 @@ export const asyncRoutes = [
name: 'warehouse',
component: () => import('@/views/inm/warehouse'),
meta: { title: '仓库', icon: 'example', perms: ['index_manage'] }
}
,
{
path: 'warehouse',
name: 'warehouse',
component: () => import('@/views/inm/warehouse'),
meta: { title: '物料库存', icon: 'example', perms: ['index_manage'] }
},
{
path: 'warehouse',
name: 'warehouse',
component: () => import('@/views/inm/warehouse'),
meta: { title: '仓库', icon: 'example', perms: ['index_manage'] }
meta: { title: '半成品库存', icon: 'example', perms: ['index_manage'] }
}
,
{
path: 'warehouse',
name: 'warehouse',
component: () => import('@/views/inm/warehouse'),
meta: { title: '成品库存', icon: 'example', perms: ['index_manage'] }
}
]
},
@ -253,7 +275,7 @@ export const asyncRoutes = [
path: 'vendor',
name: 'vendor',
component: () => import('@/views/procurement/vendor'),
meta: { title: '供应商', icon: 'example', perms: ['vendor_manage'] }
meta: { title: '采购订单', icon: 'example', perms: ['vendor_manage'] }
}
]
},

View File

@ -79,6 +79,16 @@
</el-table-column>
<el-table-column label="存放位置">
<template slot-scope="scope">{{ scope.row.place }}</template>
</el-table-column>
<el-table-column label="绑定工序">
<template slot-scope="scope" v-if="scope.row.step_">
<el-tag v-for="item in scope.row.step_"
: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.description }}</template>
@ -248,7 +258,7 @@
<el-form-item label="生产厂家" prop="factory">
<el-input v-model="equipment.factory" placeholder="生产厂商及国别" />
</el-form-item>
<el-form-item label="备注" prop="description">
<el-input
type="textarea"

View File

@ -87,6 +87,16 @@
</el-table-column>
<el-table-column label="存放位置">
<template slot-scope="scope">{{ scope.row.place }}</template>
</el-table-column>
<el-table-column label="绑定工序">
<template slot-scope="scope" v-if="scope.row.step_">
<el-tag v-for="item in scope.row.step_"
: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.description }}</template>

View File

@ -198,6 +198,7 @@ export default {
page: 1,
page_size: 20,
},
equipmentoptions:[],
state_:{
0:'合格',
1:'准用',

View File

@ -74,7 +74,11 @@
width="220px"
>
<template slot-scope="scope">
<el-link
v-if="checkPermission(['material_update'])"
@click="handlebind(scope)"
>检查表</el-link
>
<el-link
v-if="checkPermission(['material_update'])"
@click="handleEdit(scope)"
@ -263,7 +267,13 @@ export default {
this.processOptions = res.data.results;
});
},
//绑定工序
handlebind(scope)
{
this.$router.push({name: "MaterialDO", params: { id: scope.row.id }, })
}
,
handleFilter() {
this.listQuery.page = 1;
this.getList();

View File

@ -0,0 +1,479 @@
<template>
<div class="app-container">
<el-tabs type="border-card">
<el-tab-pane label="过程记录">
<el-row :gutter="24">
<el-col :span="6" >
<el-card style="margin-top: 10px">
<el-button type="primary" icon="el-icon-plus" @click="handleCreate"
>新增</el-button>
<el-table
:data="recordformList.results"
border
fit
stripe
highlight-current-row
height="100"
v-el-height-adaptive-table="{bottomOffset: 50}"
@current-change="handleCurrentChange"
>
<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
align="center"
label="操作"
>
<template slot-scope="scope">
<el-link
v-if="checkPermission(['material_update'])"
@click="handleEdit(scope)"
>编辑</el-link
>
<el-link
v-if="checkPermission(['material_delete'])"
type="danger"
@click="handleDelete(scope)"
>删除</el-link
>
</template>
</el-table-column>
</el-table>
<el-dialog
:visible.sync="dialogVisible"
:title="dialogType === 'edit' ? '编辑记录表格' : '新增记录表格'"
>
<el-form
ref="Forms"
:model="recordform"
label-width="80px"
label-position="right"
>
<el-form-item label="表格名称" prop="name">
<el-input v-model="recordform.name" placeholder="表格名称" />
</el-form-item>
</el-form>
<div style="text-align: right">
<el-button type="danger" @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="recordformconfirm('Forms')">确认</el-button>
</div>
</el-dialog>
</el-card>
</el-col>
<el-col :span="18" >
<el-card class="box-card">
<el-button type="primary" icon="el-icon-plus" @click="handlefieldCreate"
>新增</el-button>
<el-table
:data="fieldList.results"
border
fit
stripe
highlight-current-row
height="100"
v-el-height-adaptive-table="{bottomOffset: 50}"
>
<el-table-column type="index" width="50" />
<el-table-column label="字段类型">
<template slot-scope="scope">{{ options_[scope.row.field_type] }}</template>
</el-table-column>
<el-table-column label="字段名称">
<template slot-scope="scope">{{ scope.row.field_name }}</template>
</el-table-column>
<el-table-column label="字段标识">
<template slot-scope="scope">{{ scope.row.field_key }}</template>
</el-table-column>
<el-table-column label="布尔类型显示名">
<template slot-scope="scope">{{ scope.row.boolean_field_display }}</template>
</el-table-column>
<el-table-column label="选项显示名">
<template slot-scope="scope">{{ scope.row.field_choice }}</template>
</el-table-column>
<el-table-column
align="center"
label="操作"
>
<template slot-scope="scope">
<el-link
v-if="checkPermission(['material_update'])"
@click="handlefieldEdit(scope)"
>编辑</el-link
>
<el-link
v-if="checkPermission(['material_delete'])"
type="danger"
@click="handlefieldDelete(scope)"
>删除</el-link
>
</template>
</el-table-column>
</el-table>
<el-dialog
:visible.sync="dialogVisible1"
:title="dialogType1 === 'edit' ? '编辑表格字段' : '新增表格字段'"
>
<el-form
ref="Form"
:model="field"
label-width="80px"
label-position="right"
>
<el-form-item label="字段类型" prop="field_type">
<el-select style="width: 100%" v-model="field.field_type" placeholder="请选择">
<el-option
v-for="item in fieldtypeoptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="字段标识" prop="field_key">
<el-input v-model="field.field_key" placeholder="字段标识" />
</el-form-item>
<el-form-item label="字段名称" prop="field_name">
<el-input v-model="field.field_name" placeholder="字段名称" />
</el-form-item>
<el-form-item label="布尔类型显示名" prop="boolean_field_display">
<vue-json-editor
v-model="field.boolean_field_display"
:showBtns="false"
:mode="'code'"
lang="zh"
/>
</el-form-item>
<el-form-item label="选项" prop="field_choice">
<vue-json-editor
v-model="field.field_choice"
:showBtns="false"
:mode="'code'"
lang="zh"
/>
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input v-model="field.sort" placeholder="排序" />
</el-form-item>
</el-form>
<div style="text-align: right">
<el-button type="danger" @click="dialogVisible1 = false">取消</el-button>
<el-button type="primary" @click="fieldconfirm('Form')">确认</el-button>
</div>
</el-dialog>
</el-card>
</el-col>
</el-row>
</el-tab-pane>
</el-tabs>
</el-card>
</div>
</template>
<script>
import { getStep,getrecordformList,createrecordform,updaterecordform,deleterecordform,getrffieldList,createrffield,updaterffield,
deleterffield} from "@/api/mtm";
import vueJsonEditor from 'vue-json-editor'
import checkPermission from "@/utils/permission";
const defaultrecordform = {
};
const defaultfield = {
};
export default {
components: { vueJsonEditor },
data() {
return {
recordform: defaultrecordform,
field: defaultfield,
dialogType: "new",
dialogVisible:false,
dialogType1: "new",
dialogVisible1:false,
listQueryrecordform: {
page: 1,
page_size: 20,
},
recordformList: {
count: 0,
},
fieldList: {
count: 0,
},
listQueryfield: {
page: 1,
page_size: 20,
},
options_: {
'string':'字符串',
'int':'整型',
'float': '浮点',
'boolean':'布尔',
'date': '日期',
'datetime': '日期时间',
'radio': '单选',
'checkbox': '多选',
'select': '单选下拉',
'selects': '多选下拉',
'textarea': '文本域'
},
fieldtypeoptions: [{
value: 'string',
label: '字符串'
},
{
value: 'int',
label: '整型'
},
{
value: 'float',
label: '浮点'
},
{
value: 'boolean',
label: '布尔'
},
{
value: 'date',
label: '日期'
},
{
value: 'datetime',
label: '日期时间'
},
{
value: 'radio',
label: '单选'
},
{
value: 'checkbox',
label: '多选'
},
{
value: 'select',
label: '单选下拉'
},
{
value: 'selects',
label: '多选下拉'
},
{
value: 'textarea',
label: '文本域'
}],
};
},
computed: {},
watch: {
},
created() {
this.recordformLists();
this.material = this.$route.params.id;
},
methods: {
checkPermission,
handleCurrentChange(row){
this.formID=row.id;
this.fieldLists();
},
recordformLists()
{
this.listQueryrecordform.material = this.material;
console.log(this.materialid)
this.listQueryrecordform.type = 2;
getrecordformList(this.listQueryrecordform).then((response) => {
if (response.data) {
this.recordformList = response.data;
}
});
},
fieldLists()
{
this.listQueryfield.form=this.formID
getrffieldList(this.listQueryfield).then((response) => {
if (response.data) {
this.fieldList = response.data;
}
});
},
handleCreate() {
this.recordform = Object.assign({}, defaultrecordform);
this.dialogType = "new";
this.dialogVisible = true;
this.$nextTick(() => {
this.$refs["Forms"].clearValidate();
});
},
handlefieldCreate() {
this.field = Object.assign({}, defaultfield);
this.dialogType1 = "new";
this.dialogVisible1 = true;
this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});
},
handleEdit(scope) {
this.recordform = Object.assign({}, scope.row); // copy obj
this.dialogType = "edit";
this.dialogVisible = true;
this.$nextTick(() => {
this.$refs["Forms"].clearValidate();
});
},
handlefieldEdit(scope) {
this.field = Object.assign({}, scope.row); // copy obj
this.dialogType1 = "edit";
this.dialogVisible1 = true;
this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});
},
handleDelete(scope) {
this.$confirm("确认删除?", "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
await deleterecordform(scope.row.id);
this.recordformLists()
this.$message.success("成功");
})
.catch((err) => {
console.error(err);
});
},
handlefieldDelete(scope) {
this.$confirm("确认删除?", "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
await deleterffield(scope.row.id);
this.fieldLists()
this.$message.success("成功");
})
.catch((err) => {
console.error(err);
});
},
async recordformconfirm(form) {
this.$refs[form].validate((valid) => {
if (valid) {
const isEdit = this.dialogType === "edit";
if (isEdit) {
this.recordform.material=this.material;
this.recordform.type=2;
updaterecordform(this.recordform.id, this.recordform).then((res) => {
if (res.code >= 200) {
this.recordformLists()
this.dialogVisible = false;
this.$message.success("成功");
}
});
} else {
this.recordform.material=this.material;
this.recordform.type=2;
createrecordform(this.recordform).then((res) => {
if (res.code >= 200) {
this.recordformLists()
this.dialogVisible = false;
this.$message.success("成功");
}
});
}
} else {
return false;
}
});
},
async fieldconfirm(form) {
this.$refs[form].validate((valid) => {
if (valid) {
const isEdit = this.dialogType1 === "edit";
if (isEdit) {
this.field.form=this.formID
updaterffield(this.field.id, this.field).then((res) => {
if (res.code >= 200) {
this.fieldLists()
this.dialogVisible1 = false;
this.$message.success("成功");
}
});
} else {
this.field.form=this.formID
createrffield(this.field).then((res) => {
if (res.code >= 200) {
this.fieldLists()
this.dialogVisible1 = false;
this.$message.success("成功");
}
});
}
} else {
return false;
}
});
},
},
};
</script>
<style>
.my-label {
background: #E1F3D8;
}
.my-content {
background: #FDE2E2;
}
</style>

View File

@ -545,6 +545,7 @@
}
this.getList();
},
//新增工序
handleCreateStep() {
this.step = Object.assign({}, defaultstep);
this.dialogTypes = "new";
@ -620,7 +621,7 @@
recordformLists()
{
this.listQueryrecordform.step=this.stepid;
this.listQueryrecordform.type=1;
getrecordformList(this.listQueryrecordform).then((response) => {
if (response.data) {
this.recordformList = response.data;
@ -640,6 +641,7 @@
});
},
//新增记录表
handleCreate() {
this.recordform = Object.assign({}, defaultrecordform);
this.dialogType = "new";
@ -648,6 +650,7 @@
this.$refs["Forms"].clearValidate();
});
},
//新增字段
handlefieldCreate() {
this.field_choice = [''];

View File

@ -54,6 +54,9 @@
<el-table-column label="地址">
<template slot-scope="scope">{{ scope.row.address }}</template>
</el-table-column>
<el-table-column label="供应物料">
<template slot-scope="scope">{{ scope.row.material }}</template>
</el-table-column>
<el-table-column label="备注">
<template slot-scope="scope">{{ scope.row.description }}</template>
@ -114,7 +117,9 @@
<el-form-item label="地址" prop="address">
<el-input v-model="vendor.address" placeholder="地址" />
</el-form-item>
<el-form-item label="供应物料" prop="material">
<el-input v-model="vendor.material" placeholder="供应物料" />
</el-form-item>
<el-form-item label="备注" prop="description">
<el-input

View File

@ -30,6 +30,15 @@
:style="{width: '100%'}"
></el-input>
</el-form-item>
<el-form-item label="学历" prop="academic">
<el-input
v-model="formData.academic"
placeholder="请输入学历"
clearable
:style="{width: '100%'}"
></el-input>
</el-form-item>
</el-col>
@ -51,6 +60,32 @@
/>
</el-select>
</el-form-item>
<el-form-item label="在职状态" prop="jobstate">
<el-select
v-model="formData.jobstate"
placeholder="请选择在职状态"
clearable
:style="{width: '100%'}"
>
<el-option
v-for="item in jobstateOptions"
:key="item.key"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="出生年月" prop="birthdate">
<el-date-picker
v-model="formData.birthdate"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
style="width:100%"
>
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
@ -168,6 +203,10 @@ export default {
{ value: "", label: "" },
{ value: "", label: "" }
],
jobstateOptions:[
{ value: 1, label: "在职" },
{ value: 2, label: "离职" }
],
rules: {
ID_number: [

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<project ver="10" name="daq" libEmbed="true" icon="" ui="win" output="数据采集.exe" CompanyName="中存大数据" FileDescription="daq" LegalCopyright="Copyright (C) 作者 2021" ProductName="daq" InternalName="daq" FileVersion="0.0.0.08" ProductVersion="0.0.0.08" publishDir="/dist/" dstrip="false">
<project ver="10" name="daq" libEmbed="true" icon="" ui="win" output="数据采集.exe" CompanyName="中存大数据" FileDescription="daq" LegalCopyright="Copyright (C) 作者 2021" ProductName="daq" InternalName="daq" FileVersion="0.0.0.10" ProductVersion="0.0.0.10" publishDir="/dist/" dstrip="false">
<file name="main.aardio" path="main.aardio" comment="main.aardio"/>
<folder name="资源文件" path="res" embed="true" local="false" ignored="false"/>
<folder name="窗体文件" path="dlg" comment="目录" embed="true" local="false" ignored="false">

View File

@ -160,7 +160,7 @@ http.beginRequest( mainForm.serverUrl.text + "/api/em/daq/", "POST" );
var res = web.json.parse(html)
if(res['code']==200){
import fsys
var theDir = fsys.createDir(mainForm.backupPath.text+"\"+number, false)
var theDir = fsys.createDir(mainForm.backupPath.text+"\\"+number, false)
fsys.move(fullpath, theDir) //移动到备份文件库
mainForm.listbox.add("采集成功!")
}else{
@ -196,7 +196,7 @@ mainForm.listview.onnotify = function(id,code,ptr){
mainForm.add.oncommand = function(id,event){
var frmChild = mainForm.loadForm("\dlg\add.aardio");
var frmChild = mainForm.loadForm("\\dlg\\add.aardio");
frmChild.doModal();
}

View File

@ -1,3 +1,4 @@
from apps.mtm.models import Step
from rest_framework import serializers
from rest_framework.serializers import ModelSerializer
@ -8,6 +9,7 @@ from apps.system.serializers import OrganizationSimpleSerializer, UserSimpleSeri
class EquipmentSerializer(ModelSerializer):
belong_dept_ = OrganizationSimpleSerializer(source='belong_dept', read_only=True)
keeper_ = UserSimpleSerializer(source='keeper', read_only=True)
step_ = serializers.SerializerMethodField()
class Meta:
model = Equipment
fields = '__all__'
@ -18,6 +20,9 @@ class EquipmentSerializer(ModelSerializer):
queryset = queryset.select_related('belong_dept','keeper')
return queryset
def get_step_(self, obj):
return Step.objects.filter(equipments=obj).values('id', 'name', 'number')
class EquipmentSimpleSerializer(ModelSerializer):
class Meta:
model = Equipment

View File

@ -0,0 +1,35 @@
# Generated by Django 3.2.6 on 2021-09-24 03:27
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('system', '0003_auto_20210812_0909'),
('hrm', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='employee',
name='academic',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='学历'),
),
migrations.AddField(
model_name='employee',
name='birthdate',
field=models.DateField(blank=True, null=True, verbose_name='出生年月'),
),
migrations.AddField(
model_name='employee',
name='job',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='system.position', verbose_name='岗位'),
),
migrations.AddField(
model_name='employee',
name='jobstate',
field=models.IntegerField(choices=[(1, '在职'), (2, '离职')], default=1, verbose_name='在职状态'),
),
]

View File

@ -3,22 +3,31 @@ from django.contrib.auth.models import AbstractUser
from django.db.models.base import Model
import django.utils.timezone as timezone
from django.db.models.query import QuerySet
from apps.system.models import CommonAModel, CommonBModel, Organization, User, Dict, File
from apps.system.models import CommonAModel, CommonBModel, Organization, User, Dict, File,Position
from utils.model import SoftModel, BaseModel
from simple_history.models import HistoricalRecords
class Employee(CommonAModel):
"""
员工信息
"""
jobstate_choices = (
(1, '在职'),
(2, '离职'),
)
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='employee_user')
number = models.CharField('人员编号', max_length=50,null=True, blank=True, unique=True)
photo = models.CharField('证件照', max_length=1000, null=True, blank=True)
ID_number = models.CharField('身份证号', max_length=100, null=True, blank=True)
gender = models.CharField('性别', max_length=10, default='')
signature = models.CharField('签名图片', max_length=200, null=True, blank=True)
birthdate = models.DateField('出生年月', null=True, blank=True)
academic = models.CharField('学历', max_length=50, null=True, blank=True)
jobstate = models.IntegerField('在职状态', choices=jobstate_choices, default=1)
job = models.ForeignKey(Position, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='岗位')
class Meta:
verbose_name = '员工补充信息'
verbose_name_plural = verbose_name

View File

@ -0,0 +1,41 @@
# Generated by Django 3.2.6 on 2021-09-28 09:02
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),
('inm', '0002_fifo_inventory'),
]
operations = [
migrations.AlterField(
model_name='inventory',
name='count',
field=models.IntegerField(default=0, verbose_name='仓库物料存量'),
),
migrations.CreateModel(
name='Materials',
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='批次')),
('effective', models.DateField(blank=True, null=True, verbose_name='有效期')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='materials_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('inventory', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inm.inventory', verbose_name='仓库物料')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='materials_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'verbose_name': '库存表',
'verbose_name_plural': '库存表',
},
),
]

View File

@ -0,0 +1,45 @@
# Generated by Django 3.2.6 on 2021-09-29 00:42
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('mtm', '0018_material_count'),
('inm', '0003_auto_20210928_1702'),
]
operations = [
migrations.CreateModel(
name='MaterialBatch',
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='批次号')),
('expiration_date', models.DateField(blank=True, null=True, verbose_name='有效期')),
('material', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='物料信息')),
('warehouse', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inm.warehouse', verbose_name='所在仓库')),
],
options={
'verbose_name': '库存表',
'verbose_name_plural': '库存表',
},
),
migrations.RemoveField(
model_name='inventory',
name='create_by',
),
migrations.RemoveField(
model_name='inventory',
name='update_by',
),
migrations.DeleteModel(
name='Materials',
),
]

View File

@ -23,18 +23,29 @@ class WareHouse(CommonAModel):
def __str__(self):
return self.name
class Inventory(CommonAModel):
class Inventory(BaseModel):
"""
库存
库存物料
"""
material = models.ForeignKey(Material, on_delete=models.CASCADE, verbose_name='物料信息')
count = models.IntegerField('', default=0)
count = models.IntegerField('仓库物料存', default=0)
warehouse = models.ForeignKey(WareHouse, on_delete=models.CASCADE, verbose_name='所在仓库')
class Meta:
verbose_name = '库存表'
verbose_name_plural = verbose_name
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)
expiration_date = models.DateField('有效期', null=True, blank=True)
class Meta:
verbose_name = '库存表'
verbose_name_plural = verbose_name
class FIFO(CommonAModel):
"""

View File

@ -1,8 +1,9 @@
from rest_framework import serializers
from apps.inm.models import WareHouse
from apps.inm.models import WareHouse,Inventory
from apps.system.serializers import UserSimpleSerializer
from apps.mtm.serializers import MaterialSimpleSerializer
class WareHouseSerializer(serializers. ModelSerializer):
create_by_=UserSimpleSerializer('create_by', read_only=True)
@ -13,4 +14,21 @@ class WareHouseSerializer(serializers. ModelSerializer):
class WareHouseCreateUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = WareHouse
fields = ['name', 'number', 'place']
fields = ['name', 'number', 'place']
class WareHouseSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = WareHouse
fields = ['name', 'number', 'place']
class InventorySerializer(serializers. ModelSerializer):
material_= MaterialSimpleSerializer(source='material', read_only=True)
warehouse_ = WareHouseSimpleSerializer(source='material', read_only=True)
class Meta:
model = Inventory
fields = '__all__'
class InventoryCreateUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = Inventory
fields = ['material', 'count', 'warehouse']

View File

@ -1,11 +1,12 @@
from django.db.models import base
from rest_framework import urlpatterns
from apps.inm.views import WarehouseViewSet
from apps.inm.views import 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')
urlpatterns = [
path('', include(router.urls)),
]

View File

@ -1,8 +1,8 @@
from django.shortcuts import render
from rest_framework.viewsets import ModelViewSet
from apps.inm.models import WareHouse
from apps.inm.serializers import WareHouseSerializer, WareHouseCreateUpdateSerializer
from apps.inm.models import WareHouse,Inventory
from apps.inm.serializers import WareHouseSerializer, WareHouseCreateUpdateSerializer,InventorySerializer,InventoryCreateUpdateSerializer
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
@ -23,3 +23,18 @@ class WarehouseViewSet(CreateUpdateModelAMixin, ModelViewSet):
if self.action in ['create', 'update']:
return WareHouseCreateUpdateSerializer
return WareHouseSerializer
class InventoryViewSet(CreateUpdateModelAMixin, ModelViewSet):
"""
物料基本信息-增删改查
"""
perms_map = {'*': '*'}
queryset = Inventory.objects.select_related('create_by').all()
serializer_class = InventorySerializer
filterset_fields = []
ordering_fields = ['create_time']
ordering = ['-create_time']
def get_serializer_class(self):
if self.action in ['create', 'update']:
return InventoryCreateUpdateSerializer
return InventorySerializer

View File

@ -0,0 +1,34 @@
# Generated by Django 3.2.6 on 2021-09-28 05:55
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('mtm', '0016_auto_20210917_0900'),
]
operations = [
migrations.AddField(
model_name='recordform',
name='material',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='关联物料'),
),
migrations.AlterField(
model_name='material',
name='processes',
field=models.JSONField(blank=True, default=list, null=True, verbose_name='工艺流程'),
),
migrations.AlterField(
model_name='recordform',
name='step',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.step', verbose_name='关联子工序'),
),
migrations.AlterField(
model_name='recordform',
name='type',
field=models.IntegerField(choices=[(1, '生产记录'), (2, '检验记录')], default=1, verbose_name='表格类型'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-09-28 09:02
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mtm', '0017_auto_20210928_1355'),
]
operations = [
migrations.AddField(
model_name='material',
name='count',
field=models.IntegerField(default=0, verbose_name='物料总数'),
),
]

View File

@ -29,9 +29,9 @@ class Material(CommonAModel):
specification = models.CharField('型号', max_length=100, null=True, blank=True)
type = models.CharField('物料类型', choices= type_choices, max_length=20, default=1)
sort_str = models.CharField('排序字符', max_length=100, null=True, blank=True)
processes = models.JSONField('工艺流程', default=list, blank=True)
processes = models.JSONField('工艺流程', default=list, blank=True, null=True)
unit = models.CharField('基准计量单位', choices=unit_choices, default='', max_length=10)
count = models.IntegerField('物料总数', default=0)
class Meta:
verbose_name = '物料表'
verbose_name_plural = verbose_name
@ -80,11 +80,12 @@ class RecordForm(CommonAModel):
"""
type_choices=(
(1, '生产记录'),
(2, '检验记录')
)
name = models.CharField('表格名称', max_length=100, unique=True)
type = models.IntegerField('表格类型', choices=type_choices, default=1)
step = models.ForeignKey(Step, verbose_name='关联子工序', on_delete=models.CASCADE)
step = models.ForeignKey(Step, verbose_name='关联子工序', on_delete=models.CASCADE, null=True, blank=True)
material = models.ForeignKey(Material, verbose_name='关联物料', on_delete=models.CASCADE, null=True, blank=True)
class Meta:
verbose_name = '记录表格'
verbose_name_plural = verbose_name
@ -194,3 +195,4 @@ class TechDoc(CommonAModel):
class Meta:
verbose_name = '技术文件'
verbose_name_plural = verbose_name

View File

@ -1,6 +1,6 @@
from apps.em.serializers import EquipmentSerializer, EquipmentSimpleSerializer
from apps.em.serializers import EquipmentSimpleSerializer
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.exceptions import ParseError, ValidationError
from .models import InputMaterial, Material, OutputMaterial, Process, ProductProcess, RecordForm, RecordFormField, Step, TechDoc, UsedStep
from apps.system.serializers import FileSimpleSerializer, OrganizationSimpleSerializer
@ -140,6 +140,7 @@ class UsedStepListSerializer(serializers.ModelSerializer):
class RecordFormSerializer(serializers.ModelSerializer):
step_ = StepSimpleSerializer(source='step', read_only=True)
material_ = MaterialSimpleSerializer(source='material', read_only=True)
"""
记录表格序列化
"""
@ -150,13 +151,13 @@ class RecordFormSerializer(serializers.ModelSerializer):
@staticmethod
def setup_eager_loading(queryset):
""" Perform necessary eager loading of data. """
queryset = queryset.select_related('step')
queryset = queryset.select_related('step', 'material')
return queryset
class RecordFormCreateSerializer(serializers.ModelSerializer):
class Meta:
model = RecordForm
fields = ['name', 'type', 'step']
fields = ['name', 'type', 'step', 'material']
class RecordFormUpdateSerializer(serializers.ModelSerializer):
class Meta:

View File

@ -147,7 +147,7 @@ class RecordFormViewSet(OptimizationMixin, CreateUpdateModelAMixin, ModelViewSet
"""
perms_map = {'*':'*'}
queryset = RecordForm.objects.all()
filterset_fields = ['step', 'type']
filterset_fields = ['step', 'type', 'material']
search_fields = ['name']
def get_serializer_class(self):

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-09-24 06:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pum', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='vendor',
name='material',
field=models.CharField(blank=True, max_length=200, null=True, verbose_name='供应的物料'),
),
]

View File

@ -18,6 +18,7 @@ class Vendor(CommonAModel):
contact_phone = models.CharField('联系电话', max_length=11, unique=True)
address = models.CharField('地址', max_length=200, null=True, blank=True)
description = models.CharField('描述', max_length=200, blank=True, null=True)
material = models.CharField('供应的物料', max_length=200, blank=True, null=True)
class Meta:
verbose_name = '供应商信息'
verbose_name_plural = verbose_name

View File

@ -0,0 +1,19 @@
# Generated by Django 3.2.6 on 2021-09-29 00:42
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('wf', '0009_ticketflow_participant_type'),
]
operations = [
migrations.AlterField(
model_name='ticketflow',
name='transition',
field=models.ForeignKey(blank=True, help_text='与worklow.Transition关联 为0时表示认为干预的操作', null=True, on_delete=django.db.models.deletion.CASCADE, to='wf.transition', verbose_name='流转id'),
),
]

View File

@ -169,7 +169,7 @@ class TicketFlow(BaseModel):
工单流转日志
"""
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, verbose_name='关联工单')
transition = models.ForeignKey(Transition, verbose_name='流转id', help_text='与worklow.Transition关联 为0时表示认为干预的操作', on_delete=models.CASCADE)
transition = models.ForeignKey(Transition, verbose_name='流转id', help_text='与worklow.Transition关联 为0时表示认为干预的操作', on_delete=models.CASCADE, null=True, blank=True)
suggestion = models.CharField('处理意见', max_length=10000, default='', blank=True)
participant_type = models.IntegerField('处理人类型', default=0, help_text='0.无处理人,1.个人,2.多人', choices=State.type2_choices)
participant = models.ForeignKey(User, verbose_name='处理人', on_delete=models.SET_NULL, null=True, blank=True)

View File

@ -23,7 +23,7 @@ class WorkflowSimpleSerializer(serializers.ModelSerializer):
class StateSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = State
fields = ['id', 'name']
fields = ['id', 'name', 'type']
class TransitionSerializer(serializers.ModelSerializer):
source_state_ = StateSimpleSerializer(source='source_state', read_only=True)

View File

@ -168,8 +168,13 @@ class WfService(object):
if user.id not in participant:
return dict(permission=False, msg="非当前处理人")
current_participant_count = len(participant)
if current_participant_count >1 and state.distribute_type == State.STATE_DISTRIBUTE_TYPE_ACTIVE:
return dict(permission=False, msg="需要先接单再处理")
if current_participant_count == 1:
if [user.id] != participant:
return dict(permission=False, msg="非当前处理人")
elif current_participant_count >1 and state.distribute_type == State.STATE_DISTRIBUTE_TYPE_ACTIVE:
if user.id not in participant:
return dict(permission=False, msg="非当前处理人")
return dict(permission=False, msg="需要先接单再处理", need_accept=True)
if ticket.in_add_node:
return dict(permission=False, msg="工单当前处于加签中,请加签完成后操作")
return dict(permission=True, msg="")

View File

@ -247,6 +247,25 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
transitions = WfService.get_ticket_transitions(ticket)
return Response(TransitionSerializer(instance=transitions, many=True).data)
@action(methods=['post'], detail=True, perms_map={'post':'*'})
def accpet(self, request, pk=None):
"""
接单,当工单当前处理人实际为多个人时(角色部门多人都有可能 注意角色和部门有可能实际只有一人)
"""
ticket = self.get_object()
result = WfService.ticket_handle_permission_check(ticket, request.user)
if result.get('need_accept', False):
ticket.participant_type = State.PARTICIPANT_TYPE_PERSONAL
ticket.participant = request.user.id
ticket.save()
# 接单日志
# 更新工单流转记录
TicketFlow.objects.create(ticket=ticket, state=ticket.state, ticket_data=WfService.get_ticket_all_field_value(ticket),
suggestion='接单处理', participant_type=State.PARTICIPANT_TYPE_PERSONAL,
participant=request.user, transition=None)
return Response()
else:
raise APIException('无需接单')
class TicketFlowViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
"""