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

This commit is contained in:
shilixia 2021-10-29 11:10:42 +08:00
commit e71d5e0e32
36 changed files with 669 additions and 99 deletions

View File

@ -19,7 +19,7 @@ export default {
}
},
mounted(){
// this.$store.dispatch("user/getCount", {})
this.$store.dispatch("user/getCount", {});
this.timer = window.setInterval(() => {
setTimeout(() => {
this.$store.dispatch("user/getCount", {})

View File

@ -6,7 +6,7 @@
<div class="right-menu">
<template>
<el-badge v-if="count.total_count>0" :value="count.total_count" class="item right-menu-item navbarBadge" @click.native="gotoTicketPage">
<el-badge :value="count.total_count" class="item right-menu-item navbarBadge" @click.native="gotoTicketPage">
<el-icon class="el-icon-s-management" style="font-size: 25px;color: #409EFF;padding-top: 12px;cursor: pointer;"></el-icon>
</el-badge>
<search id="header-search" class="right-menu-item" />

View File

@ -17,14 +17,13 @@
</router-link>
</scroll-pane>
<ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
<li @click="refreshSelectedTag(selectedTag)">Refresh</li>
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">Close</li>
<li @click="closeOthersTags">Close Others</li>
<li @click="closeAllTags(selectedTag)">Close All</li>
<li @click="refreshSelectedTag(selectedTag)">刷新</li>
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">关闭</li>
<li @click="closeOthersTags">关闭其他</li>
<li @click="closeAllTags(selectedTag)">关闭多有</li>
</ul>
</div>
</template>
<script>
import ScrollPane from './ScrollPane'
import path from 'path'
@ -62,7 +61,7 @@ export default {
}
},
mounted() {
this.initTags()
this.initTags();
this.addTags()
},
methods: {
@ -73,10 +72,10 @@ export default {
return tag.meta && tag.meta.affix
},
filterAffixTags(routes, basePath = '/') {
let tags = []
let tags = [];
routes.forEach(route => {
if (route.meta && route.meta.affix) {
const tagPath = path.resolve(basePath, route.path)
const tagPath = path.resolve(basePath, route.path);
tags.push({
fullPath: tagPath,
path: tagPath,
@ -85,16 +84,16 @@ export default {
})
}
if (route.children) {
const tempTags = this.filterAffixTags(route.children, route.path)
const tempTags = this.filterAffixTags(route.children, route.path);
if (tempTags.length >= 1) {
tags = [...tags, ...tempTags]
}
}
})
});
return tags
},
initTags() {
const affixTags = this.affixTags = this.filterAffixTags(this.routes)
const affixTags = this.affixTags = this.filterAffixTags(this.routes);
for (const tag of affixTags) {
// Must have tag name
if (tag.name) {
@ -103,18 +102,18 @@ export default {
}
},
addTags() {
const { name } = this.$route
const { name } = this.$route;
if (name) {
this.$store.dispatch('tagsView/addView', this.$route)
}
return false
},
moveToCurrentTag() {
const tags = this.$refs.tag
const tags = this.$refs.tag;
this.$nextTick(() => {
for (const tag of tags) {
if (tag.to.path === this.$route.path) {
this.$refs.scrollPane.moveToTarget(tag)
this.$refs.scrollPane.moveToTarget(tag);
// when query is different then update
if (tag.to.fullPath !== this.$route.fullPath) {
this.$store.dispatch('tagsView/updateVisitedView', this.$route)
@ -126,7 +125,7 @@ export default {
},
refreshSelectedTag(view) {
this.$store.dispatch('tagsView/delCachedView', view).then(() => {
const { fullPath } = view
const { fullPath } = view;
this.$nextTick(() => {
this.$router.replace({
path: '/redirect' + fullPath
@ -142,7 +141,7 @@ export default {
})
},
closeOthersTags() {
this.$router.push(this.selectedTag)
this.$router.push(this.selectedTag);
this.$store.dispatch('tagsView/delOthersViews', this.selectedTag).then(() => {
this.moveToCurrentTag()
})

View File

@ -80,7 +80,17 @@ export const constantRoutes = [
* the routes that need to be dynamically loaded based on user perms
*/
export const asyncRoutes = [
{
path: '/redirect',
component: Layout,
hidden: true,
children: [
{
path: '/redirect/:path(.*)',
component: () => import('@/views/redirect/index')
}
]
},
{
path: '/mtm',
component: Layout,

View File

@ -1,6 +1,6 @@
<template>
<div class="dashboard-container">
<el-badge v-if="count.total_count>0" :value="count.total_count" class="item" @click.native="gotoTicketPage">
<el-badge :value="count.total_count" class="item" @click.native="gotoTicketPage">
<el-icon class="el-icon-s-management" style="font-size: 70px;color: #d29898"></el-icon>
</el-badge>
<div></div>

View File

@ -0,0 +1,12 @@
<script>
export default {
created() {
const { params, query } = this.$route;
const { path } = params;
this.$router.replace({ path: '/' + path, query })
},
render: function(h) {
return h() // avoid warning message
}
}
</script>

View File

@ -12,9 +12,8 @@
</template>
<script>
import { getToken } from '@/utils/auth'
import {faceLogin} from "@/api/testModel";
import { setToken, removeToken } from '@/utils/auth'
import { login, getInfo } from '@/api/user'
// import "tracking/build/data/face-min.js";
// import "tracking/build/data/mouth-min.js";
// import "tracking/build/data/tracking-min.js";
@ -75,8 +74,6 @@
let imgData = {base64:img};
faceLogin(imgData).then((res) => {
if (res.code >= 200) {
debugger;
console.log(res);
let data = res.data;
this.$confirm("是否切换登陆人?", "提示", {
confirmButtonText: "确认",
@ -85,7 +82,11 @@
}).then(async () => {
await this.$store.dispatch("user/resetToken",data);
await this.$store.dispatch("user/getInfo",data.access);
window.location.reload();
// window.location.reload();
this.$emit('func',false);
debugger;
console.log(getToken());
debugger;
})
.catch((err) => {
console.error(err);
@ -104,8 +105,6 @@
});
this.$refs['video'].srcObject = null
},
//切换本地摄像头
changePhoto(){
/**得到所有的设备*/
@ -146,8 +145,6 @@
navigator.getUserMedia(constraints, success, error);
}
},
/**
* 检查取景框是否有人脸
* */
@ -181,9 +178,6 @@
setFace(data){
this.isHasFace = data;
}
},
beforeDestroy () {

View File

@ -8,19 +8,27 @@
<hr>
<img id="canvasImg" src="./../../assets/404_images/404.png" style="display: none">
<div style="position: relative;">
<canvas id="canvas" width="700" height="500" >
<canvas id="canvas" width="700" height="500">
您的浏览器不支持绘图请升级或更换浏览器
</canvas>
<input type="text" value="" class="hide" id="txt" placeholder="请输入文字">
<textarea class="hide" id="word" cols="30" rows="10" placeholder="请输入文字" autofocus></textarea>
<input id="inputV" type="hidden" value="1">
<textarea class="hide" id="word" cols="30" rows="10" placeholder="请输入文字" autofocus></textarea>
<input id="inputV" type="hidden" value="1">
<span>保存的图片</span>
<div id="res"></div>
</div>
<button @click="takePhoto()">拍照</button>
<el-form
style="width: 700px;"
label-width="100px"
>
<el-form-item label="名称" prop="name">
<el-input v-model="test" placeholder="名称"/>
</el-form-item>
</el-form>
<el-dialog :visible.sync="limitedPhoto" title="人脸登录">
<div class="testTracking">
<faceLogin name="faceLogin"></faceLogin>
<faceLogin name="faceLogin" @func="getMsgFormSon"></faceLogin>
</div>
</el-dialog>
</div>
@ -46,6 +54,7 @@
colorF:"#e42343",
imgData:'',
limitedPhoto:false,
test:''
}
},
mounted(){
@ -64,6 +73,9 @@
},1000)
},
methods: {
getMsgFormSon(data){
this.limitedPhoto = data;
},
takePhoto(){
this.limitedPhoto = true;
},

View File

@ -139,7 +139,7 @@
</el-select>
</el-form-item>
<el-form-item label="角色" prop="participant" v-if="wfstate.participant_type==4">
<el-select style="width: 100%" v-model="participants" placeholder="请选择角色">
<el-select style="width: 100%" v-model="participants" multiple placeholder="请选择角色">
<el-option v-for="item in roles" :key="item.id" :label="item.name" :value="item.id">
</el-option>
</el-select>
@ -158,6 +158,12 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item label="抄送给" prop="participant">
<el-select style="width: 100%" v-model="wfstate.participant_cc" multiple placeholder="请选择抄送给谁">
<el-option v-for="item in staffs" :key="item.id" :label="item.name" :value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="字段状态">
<el-button @click="addWordStatusChange">添加修改</el-button>
<el-row v-for="(item,$index) in statusChange" :key="item+$index" style="margin-top: 10px">
@ -210,6 +216,7 @@
is_hidden: '',
sort: '',
type: '',
participant_cc: [],
enable_retreat: '',
participant_type: '',
filter_policy: '',
@ -291,7 +298,6 @@
},
created() {
this.getList();
this.getUser();
this.getDepartment();

View File

@ -176,7 +176,7 @@
@pagination="getList"
/>
</el-tab-pane>
<el-tab-pane label="抄送我" name="relation">
<el-tab-pane label="抄送我" name="cc">
<el-table :data="tickets"
border fit stripe
style="width: 100%"

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,12 +41,14 @@ 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 = '库存表'
verbose_name_plural = verbose_name
class FIFO(CommonAModel):
"""
出入库记录
@ -58,12 +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 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,84 @@ 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('没有入库内容')
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,25 @@
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 + 1
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,69 @@ 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):
# """
# 复杂查询
# """
# serializer = MaterialBatchQuerySerializer(data=request.data)
# serializer.is_valid(raise_exception=True)
# queryset = self.queryset.filter()
# return Response()
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,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
@ -46,13 +47,16 @@ class SubProductionPlan(CommonAModel):
process = models.ForeignKey(Process, verbose_name='关联大工序', on_delete=models.CASCADE)
steps = models.JSONField('工艺步骤', default=list)
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, '输入物料'),
@ -61,5 +65,5 @@ class SubProductionProgress(BaseModel):
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

@ -43,4 +43,7 @@ class SubProductionProgressSerializer(serializers.ModelSerializer):
material_ = MaterialSimpleSerializer(source='material', read_only=True)
class Meta:
model = SubProductionProgress
fields = '__all__'
fields = '__all__'
class PickNeedSerializer(serializers.Serializer):
warehouse = serializers.IntegerField(label="仓库ID")

View File

@ -1,10 +1,13 @@
from datetime import timezone
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.inm.models import MaterialBatch
from apps.inm.serializers import MaterialBatchSerializer
from apps.mtm.models import InputMaterial, OutputMaterial, 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, 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
@ -77,9 +80,9 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel
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)
SubProductionProgress.objects.create(material=m.material, type=1, count=m.count*production_plan.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)
SubProductionProgress.objects.create(material=m.material, type=2, count=m.count*production_plan.count, subproduction_plan=instance)
production_plan.is_planed=True
production_plan.save()
return Response()
@ -91,6 +94,7 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo
perms_map = {'*': '*'}
queryset = SubProductionPlan.objects.select_related('process', 'workshop')
search_fields = []
serializer_class = SubProductionPlanListSerializer
filterset_fields = ['production_plan']
ordering_fields = ['process__number']
ordering = ['process__number']
@ -100,7 +104,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):
@ -122,6 +126,36 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo
obj.save()
return Response()
raise APIException('计划状态有误')
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=serializers.Serializer)
def start(self, request, pk=None):
"""
开始生产
"""
obj = self.get_object()
if obj.state in [1,2]:
obj.state = 3
obj.start_date_real = timezone.now()
obj.save()
return Response()
raise APIException('计划状态有误')
@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):

View File

@ -8,7 +8,8 @@ class GetParticipants:
# # return list(filter(lambda x: x.startswith('get_') and callable(getattr(self, x)), dir(self)))
# return [(func, getattr(self, func).__doc__) for func in dir(self) if callable(getattr(self, func)) and func.startswith('get_')]
def get_create_by(self, state:dict={}, ticket:dict={}, ticket_data:dict={}, request={}):
@classmethod
def get_create_by(cls, state:dict={}, ticket:dict={}, ticket_data:dict={}, request={}):
"""工单创建人"""
participant = ticket.create_by.id
return participant

View File

@ -159,13 +159,13 @@ class WfService(object):
elif destination_participant_type == State.PARTICIPANT_TYPE_ROLE:#角色
user_queryset = User.objects.filter(roles__in=destination_participant)
# 如果选择了角色, 需要走过滤策略
if ticket.filter_policy == 1:
if state.filter_policy == 1:
depts = get_parent_queryset(ticket.belong_dept)
user_queryset = user_queryset.filter(dept__in=depts)
elif ticket.filter_policy == 2:
elif state.filter_policy == 2:
depts = get_parent_queryset(ticket.create_by.dept)
user_queryset = user_queryset.filter(dept__in=depts)
elif ticket.filter_policy == 3:
elif state.filter_policy == 3:
depts = get_parent_queryset(request.user.dept)
user_queryset = user_queryset.filter(dept__in=depts)
destination_participant = list(user_queryset.values_list('id', flat=True))

View File

@ -183,7 +183,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
participant=None, participant_cc=start_state.participant_cc)
# 目标状态需要抄送
if next_state.participant_cc:
TicketFlow.objects.create(ticket=ticket, state=ticket.next_state,
TicketFlow.objects.create(ticket=ticket, state=next_state,
participant_type=0, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_CC,
participant=None, participant_cc=next_state.participant_cc)
return Response(TicketSerializer(instance=ticket).data)
@ -276,7 +276,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
participant=request.user, transition=transition)
# 目标状态需要抄送
if destination_state.participant_cc:
TicketFlow.objects.create(ticket=ticket, state=ticket.destination_state,
TicketFlow.objects.create(ticket=ticket, state=destination_state,
participant_type=0, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_CC,
participant=None, participant_cc=destination_state.participant_cc)
return Response(TicketSerializer(instance=ticket).data)

View File

@ -0,0 +1,86 @@
# 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='关联物料')),
('subproduction_plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pm.subproductionplan', verbose_name='关联子计划')),
],
options={
'abstract': False,
},
),
]

View File

@ -2,27 +2,39 @@ 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 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
class Product(CommonAModel):
class WMaterial(BaseModel):
"""
产品(所有生产过程中出现过的)
车间生产物料
"""
subproduction_plan = models.ForeignKey(SubProductionPlan, 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, '待执行'),
(1, '进行中'),
(2, '已完成')
)
number = models.CharField('物品编号', primary_key=True, null=True, blank=True, max_length=50)
number = models.CharField('物品编号', unique=True, null=True, blank=True, max_length=50)
m_state = models.ForeignKey(Material, verbose_name='所属物料状态', on_delete=models.CASCADE)
p_state = models.ForeignKey(Step, verbose_name='所在步骤', on_delete=models.CASCADE, null=True, blank=True)
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)
class ProductForm(CommonAModel):
class WProductForm(CommonAModel):
"""
记录表格
"""
@ -30,8 +42,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,9 +1,10 @@
from rest_framework import serializers
from rest_framework.serializers import ModelSerializer
from .models import Vendor
class PickDetailSerializer(serializers.Serializer):
material = serializers.IntegerField(label='物料ID')
batch = serializers.CharField(label='物料批次')
pick_count = serializers.IntegerField(label="领料数量")
class VendorSerializer(ModelSerializer):
class Meta:
model = Vendor
fields = '__all__'
class PickSerializer(serializers.Serializer):
warehouse = serializers.IntegerField(label="仓库ID")

View File

@ -1,11 +1,10 @@
from django.db.models import base
from rest_framework import urlpatterns
from apps.pum.views import VendorViewSet
from django.urls import path, include
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register('vendor', VendorViewSet, basename='vendor')
urlpatterns = [
path('', include(router.urls)),
]

View File

@ -1,20 +1,12 @@
from django.shortcuts import render
from rest_framework.viewsets import ModelViewSet
from rest_framework.generics import CreateAPIView, GenericAPIView
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from apps.pum.models import Vendor
from apps.pum.serializers import VendorSerializer
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
# Create your views here.
class VendorViewSet(CreateUpdateModelAMixin, ModelViewSet):
class WpmPickView(CreateUpdateModelAMixin, CreateAPIView):
"""
供应商-增删改查
领料
"""
perms_map = {'get': '*', 'post': 'vendor_create',
'put': 'vendor_update', 'delete': 'vendor_delete'}
queryset = Vendor.objects.all()
serializer_class = VendorSerializer
search_fields = ['name', 'contact']
filterset_fields = []
ordering_fields = ['create_time']
ordering = ['-create_time']
pass

View File

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