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

This commit is contained in:
““shijing 2021-09-06 11:12:24 +08:00
commit 71474afd1f
9 changed files with 368 additions and 19 deletions

30
hb_client/src/api/inm.js Normal file
View File

@ -0,0 +1,30 @@
import request from '@/utils/request'
export function getWarehouseList(query) {
return request({
url: '/inm/warehouse/',
method: 'get',
params: query
})
}
export function createWarehouse(data) {
return request({
url: '/inm/warehouse/',
method: 'post',
data
})
}
export function updateWarehouse(id, data) {
return request({
url: `/inm/warehouse/${id}/`,
method: 'put',
data
})
}
export function deleteWarehouse(id, data) {
return request({
url: `/inm/warehouse/${id}/`,
method: 'delete',
data
})
}

View File

@ -87,6 +87,12 @@ export const asyncRoutes = [
name: 'equipment',
meta: { title: '设备管理', icon: 'example', perms: ['equipment_set'] },
children: [
{
path: 'index',
name: 'index',
component: () => import('@/views/equipment/index'),
meta: { title: '设备台账', icon: 'example', perms: ['index_manage'] }
},
{
path: 'index',
name: 'index',
@ -95,14 +101,34 @@ export const asyncRoutes = [
}
]
},
{
path: '/inm',
component: Layout,
redirect: '/inm/warehouse',
name: 'inm',
meta: { title: '库存管理', icon: 'example', perms: ['equipment_set'] },
children: [
{
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'] }
}
]
},
{
path: '/mtm',
component: Layout,
redirect: '/mtm/material/',
name: 'mtm',
meta: { title: '制造技术管理', icon: 'example', perms: ['procurement_set'] },
meta: { title: '制造管理', icon: 'example', perms: ['procurement_set'] },
children: [
{
path: 'material',
@ -147,6 +173,12 @@ export const asyncRoutes = [
name: 'procurement',
meta: { title: '采购管理', icon: 'example', perms: ['procurement_set'] },
children: [
{
path: 'vendor',
name: 'vendor',
component: () => import('@/views/procurement/vendor'),
meta: { title: '供应商', icon: 'example', perms: ['vendor_manage'] }
},
{
path: 'vendor',
name: 'vendor',

View File

@ -0,0 +1,248 @@
<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>
<div style="margin-top: 10px">
<el-button type="primary" icon="el-icon-plus" @click="handleCreate"
>新增设备</el-button
>
</div>
</el-card>
<el-card style="margin-top: 10px">
<el-table
v-loading="listLoading"
:data="warehouseList.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>
</el-table-column>
<el-table-column label="具体地点">
<template slot-scope="scope">{{ scope.row.place }}</template>
</el-table-column>
<el-table-column
align="center"
label="操作"
width="220px"
>
<template slot-scope="scope">
<el-link
v-if="checkPermission(['warehouse_update'])"
@click="handleEdit(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="warehouseList.count > 0"
:total="warehouseList.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="warehouse"
label-width="80px"
label-position="right"
:rules="rule1"
>
<el-form-item label="仓库名称" prop="name">
<el-input v-model="warehouse.name" placeholder="仓库名称" />
</el-form-item>
<el-form-item label="仓库编号" prop="number">
<el-input v-model="warehouse.number" placeholder="仓库编号" />
</el-form-item>
<el-form-item label="具体地点" prop="place">
<el-input v-model="warehouse.place" 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="confirm('Form')">确认</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getWarehouseList, createWarehouse,updateWarehouse,deleteWarehouse } from "@/api/inm";
import checkPermission from "@/utils/permission";
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: {
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) => {
if (response.data) {
this.warehouseList = response.data;
}
this.listLoading = false;
});
},
handleFilter() {
this.listQuery.page = 1;
this.getList();
},
resetFilter() {
this.listQuery = {
page: 1,
page_size: 20,
}
this.getList();
},
handleCreate() {
this.warehouse = Object.assign({}, defaultewarehouse);
this.dialogType = "new";
this.dialogVisible = true;
this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});
},
handleEdit(scope) {
this.warehouse = Object.assign({}, scope.row); // copy obj
this.dialogType = "edit";
this.dialogVisible = true;
this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});
},
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);
});
},
async confirm(form) {
this.$refs[form].validate((valid) => {
if (valid) {
const isEdit = this.dialogType === "edit";
if (isEdit) {
updateWarehouse(this.warehouse.id, this.warehouse).then((res) => {
if (res.code >= 200) {
this.getList();
this.dialogVisible = false;
this.$message.success("成功");
}
});
} else {
createWarehouse(this.warehouse).then((res) => {
if (res.code >= 200) {
this.getList();
this.dialogVisible = false;
this.$message.success("成功");
}
});
}
} else {
return false;
}
});
},
},
};
</script>

View File

@ -18,7 +18,7 @@ class MaterialDetailSerializer(serializers.ModelSerializer):
fields = '__all__'
def get_processes_(self, obj):
objs = Process.objects.filter(id__in=obj.processes)
objs = Process.objects.filter(id__in=obj.processes).order_by('number')
return ProcessSimpleSerializer(instance=objs, many=True).data

View File

@ -53,16 +53,16 @@ class ProcessViewSet(PageOrNot, CreateUpdateModelAMixin, ModelViewSet):
ordering_fields = ['number']
ordering = ['number']
@action(methods=['get'], detail=True, perms_map={'get':'process_update'}, pagination_class=None, serializer_class=StepSerializer)
@action(methods=['get'], detail=True, perms_map={'get':'process_update'}, pagination_class=None, serializer_class=StepDetailSerializer)
def steps(self, request, pk=None):
"""
工序下的子工序
"""
process = self.get_object()
serializer = self.serializer_class(instance=Step.objects.filter(process=process, is_deleted=False), many=True)
serializer = self.serializer_class(instance=Step.objects.prefetch_related('equipments').filter(process=process, is_deleted=False), many=True)
return Response(serializer.data)
class StepViewSet(CreateUpdateModelAMixin, CreateModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin, GenericViewSet):
class StepViewSet(OptimizationMixin, CreateUpdateModelAMixin, CreateModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin, GenericViewSet):
"""
子工序-增删改查
"""
@ -156,6 +156,15 @@ class RecordFormViewSet(OptimizationMixin, CreateUpdateModelAMixin, ModelViewSet
elif self.action == 'update':
return RecordFormUpdateSerializer
return RecordFormSerializer
@action(methods=['get'], detail=True, perms_map={'get':'*'}, pagination_class=None, serializer_class=RecordFormFieldSerializer)
def fields(self, request, pk=None):
"""
表格下的字段
"""
instance = self.get_object()
serializer = self.serializer_class(instance=RecordFormField.objects.filter(form=instance, is_deleted=False), many=True)
return Response(serializer.data)
class RecordFormFieldViewSet(OptimizationMixin, CreateUpdateModelAMixin, ModelViewSet):

View File

@ -79,6 +79,9 @@ class Transition(CommonAModel):
"""
工作流流转定时器条件(允许跳过) 条件流转与定时器不可同时存在
"""
TRANSITION_ATTRIBUTE_TYPE_ACCEPT = 1 # 同意
TRANSITION_ATTRIBUTE_TYPE_REFUSE = 2 # 拒绝
TRANSITION_ATTRIBUTE_TYPE_OTHER = 3 # 其他
attribute_type_choices = (
(1, '同意'),
(2, '拒绝'),

View File

@ -1,7 +1,8 @@
from apps.system.serializers import UserSimpleSerializer
import rest_framework
from rest_framework import serializers
from .models import State, Ticket, Workflow, Transition, CustomField
from .models import State, Ticket, TicketFlow, Workflow, Transition, CustomField
class WorkflowSerializer(serializers.ModelSerializer):
@ -42,6 +43,10 @@ class CustomFieldSerializer(serializers.ModelSerializer):
model = CustomField
fields = '__all__'
class TicketSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = Ticket
fields = '__all__'
class TicketCreateSerializer(serializers.ModelSerializer):
transition = serializers.IntegerField(label='流转ID')
@ -65,3 +70,12 @@ class TicketSerializer(serializers.ModelSerializer):
def setup_eager_loading(queryset):
queryset = queryset.select_related('workflow','state')
return queryset
class TicketFlowSerializer(serializers.ModelSerializer):
participant_ = UserSimpleSerializer(source='participant', read_only=True)
state_ = StateSimpleSerializer(source='state', read_only=True)
class Meta:
model = TicketFlow
fields = '__all__'

View File

@ -1,3 +1,4 @@
from apps.wf.serializers import TicketSerializer, TicketSimpleSerializer
from typing import Tuple
from apps.system.models import User
from apps.wf.models import CustomField, State, Ticket, Transition, Workflow
@ -198,7 +199,7 @@ class WfService(object):
:return:
"""
# 获取工单基础表中的字段中的字段信息
field_info_dict = ticket.get_dict()
field_info_dict = TicketSimpleSerializer(instance=ticket).data
# 获取自定义字段的值
custom_fields_queryset = cls.get_workflow_custom_fields(ticket.workflow)
for i in custom_fields_queryset:

View File

@ -2,7 +2,7 @@ from django.core.exceptions import AppRegistryNotReady
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin
from apps.wf.serializers import CustomFieldSerializer, StateSerializer, TicketCreateSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer
from apps.wf.serializers import CustomFieldSerializer, StateSerializer, TicketCreateSerializer, TicketFlowSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer
from django.shortcuts import get_object_or_404, render
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from rest_framework.decorators import action, api_view
@ -116,7 +116,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
if value == State.STATE_FIELD_REQUIRED:
if key not in ticket_data or not ticket_data[key]:
raise APIException('字段{}必填'.format(key))
ticket = serializer.save(state=start_state) # 先创建出来
ticket = serializer.save(state=start_state, create_by=request.user) # 先创建出来
next_state = WfService.get_next_state_by_transition_and_ticket_info(ticket=ticket, transition=transition)
participant_info = WfService.get_ticket_state_participant_info(state=next_state, ticket=ticket, ticket_data=ticket.ticket_data)
destination_participant_type = participant_info.get('destination_participant_type', 0)
@ -134,7 +134,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
if title_template:
all_ticket_data = {**rdata, **rdata['ticket_data']}
title = title_template.format(**all_ticket_data)
# 更新工单
# 更新一下信息
ticket.sn=sn
ticket.title=title
ticket.state=next_state
@ -146,7 +146,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
# 新增流转记录
TicketFlow.objects.create(ticket=ticket, state=start_state, ticket_data=WfService.get_ticket_all_field_value(ticket),
suggestion=rdata.get('suggestion',''), participant_type=State.PARTICIPANT_TYPE_PERSONAL,
participant=ticket.create_by, transition=transition, create_by=request.user)
participant=ticket.create_by, transition=transition)
return Response(TicketSerializer(instance=ticket).data)
@ -204,14 +204,14 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
ticket.participant = destination_participant
ticket.multi_all_person = multi_all_person
if destination_state.type == State.STATE_TYPE_END:
ticket.act_state = State.TICKET_ACT_STATE_FINISH
ticket.act_state = Ticket.TICKET_ACT_STATE_FINISH
elif destination_state.type == State.STATE_TYPE_START:
ticket.act_state = State.TICKET_ACT_STATE_DRAFT
ticket.act_state = Ticket.TICKET_ACT_STATE_DRAFT
else:
ticket.act_state = State.TICKET_ACT_STATE_ONGOING
ticket.act_state = Ticket.TICKET_ACT_STATE_ONGOING
if transition.attribute_type == State.TRANSITION_ATTRIBUTE_TYPE_REFUSE:
transition.act_state = State.TICKET_ACT_STATE_BACK
if transition.attribute_type == Transition.TRANSITION_ATTRIBUTE_TYPE_REFUSE:
transition.act_state = Ticket.TICKET_ACT_STATE_BACK
# 只更新必填和可选的字段
for key, value in ticket.state.state_fields.items():
@ -223,7 +223,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
# 更新工单流转记录
TicketFlow.objects.create(ticket=ticket, state=source_state, ticket_data=WfService.get_ticket_all_field_value(ticket),
suggestion=data.get('suggestion',''), participant_type=State.PARTICIPANT_TYPE_PERSONAL,
participant=request.user, transition=transition, create_by=request.user)
participant=request.user, transition=transition)
return Response(TicketSerializer(instance=ticket).data)
@ -243,4 +243,16 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
"""
ticket = self.get_object()
transitions = WfService.get_ticket_transitions(ticket)
return Response(TransitionSerializer(instance=transitions, many=True).data)
return Response(TransitionSerializer(instance=transitions, many=True).data)
class TicketFlowViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
"""
工单日志
"""
perms_map = {'*':'*'}
queryset = TicketFlow.objects.all()
serializer_class = TicketFlowSerializer
search_fields = ['suggestion']
filterset_fields = ['paticipant', 'state', 'ticket']
ordering = ['-create_time']