416 lines
16 KiB
Python
416 lines
16 KiB
Python
from django.shortcuts import render
|
|
from django.conf import settings
|
|
from rest_framework.decorators import action
|
|
from rest_framework.mixins import ListModelMixin
|
|
from rest_framework.response import Response
|
|
from rest_framework.exceptions import ParseError
|
|
|
|
from apps.mtm.filters import GoalFilter, MaterialFilter, RouteFilter
|
|
from apps.mtm.models import Goal, Material, Mgroup, Shift, Team, Process, Route, TeamMember, RoutePack, Srule, RouteMat
|
|
from apps.mtm.serializers import (GoalSerializer, MaterialSerializer,
|
|
MgroupGoalYearSerializer, MgroupSerializer, MgroupDaysSerializer,
|
|
ShiftSerializer, TeamSerializer, ProcessSerializer,
|
|
RouteSerializer, TeamMemberSerializer, RoutePackSerializer,
|
|
SruleSerializer, RouteMatSerializer, RoutePackCopySerializer, MaterialExportSerializer)
|
|
from apps.mtm.services import get_mgroup_goals, daoru_material, get_mgroup_days
|
|
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
|
|
from apps.utils.mixins import BulkCreateModelMixin, BulkDestroyModelMixin, CustomListModelMixin
|
|
from rest_framework.serializers import Serializer
|
|
from django.db import transaction
|
|
from django.db.models import Q
|
|
from apps.wf.models import Ticket
|
|
from django.utils import timezone
|
|
from rest_framework.permissions import IsAdminUser
|
|
from apps.utils.export import export_excel
|
|
from operator import itemgetter
|
|
|
|
# Create your views here.
|
|
class MaterialViewSet(CustomModelViewSet):
|
|
"""
|
|
list:产品
|
|
|
|
产品
|
|
"""
|
|
queryset = Material.objects.all()
|
|
serializer_class = MaterialSerializer
|
|
filterset_class = MaterialFilter
|
|
search_fields = ['name', 'code', 'number', 'specification', 'model', 'bin_number_main']
|
|
select_related_fields = ['process']
|
|
ordering = ['name', 'model', 'specification',
|
|
'type', 'process', 'process__sort', 'sort', 'id', 'number']
|
|
ordering_fields = ['name', 'model', 'specification',
|
|
'type', 'process', 'process__sort', 'sort', 'id', 'number']
|
|
|
|
@transaction.atomic
|
|
def perform_destroy(self, instance):
|
|
from apps.inm.models import MaterialBatch
|
|
if MaterialBatch.objects.filter(material=instance).exists():
|
|
raise ParseError('该物料有库存!')
|
|
if Route.objects.filter(Q(material=instance) | Q(material_in=instance) | Q(material_out=instance)).exists():
|
|
raise ParseError('该物料有工艺路线!')
|
|
instance.delete()
|
|
Material.objects.filter(parent=instance).update(is_deleted=True)
|
|
|
|
@action(methods=['post'], detail=False, serializer_class=Serializer, perms_map={'post': 'material.create'})
|
|
@transaction.atomic
|
|
def daoru(self, request, *args, **kwargs):
|
|
"""导入物料
|
|
|
|
导入物料
|
|
"""
|
|
daoru_material(settings.BASE_DIR + request.data.get('path', ''))
|
|
return Response()
|
|
|
|
@action(methods=['post'], detail=True, serializer_class=Serializer, perms_map={'post': 'material.create'})
|
|
@transaction.atomic
|
|
def cal_count(self, request, *args, **kwargs):
|
|
"""统计数量
|
|
|
|
统计数量
|
|
"""
|
|
ins = self.get_object()
|
|
from apps.mtm.services_2 import cal_material_count
|
|
cal_material_count([ins.id])
|
|
return Response()
|
|
|
|
@action(methods=['put'], detail=True, serializer_class=Serializer, perms_map={'put': '*'})
|
|
@transaction.atomic
|
|
def set_week_esitimate_consume(self, request, *args, **kwargs):
|
|
"""设置周预估消耗量
|
|
|
|
设置周预估消耗量
|
|
"""
|
|
obj: Material = self.get_object()
|
|
obj.week_esitimate_consume = request.data.get(
|
|
'week_esitimate_consume', None)
|
|
obj.save()
|
|
return Response()
|
|
|
|
@action(methods=['get'], detail=False, serializer_class=Serializer, perms_map={'get': '*'})
|
|
def cates(self, request, *args, **kwargs):
|
|
res = Material.objects.exclude(cate='').exclude(cate=None).values_list('cate', flat=True).distinct()
|
|
return Response(set(res))
|
|
|
|
@action(methods=['get'], detail=False, perms_map={'get': '*'})
|
|
def export_excel(self, request, pk=None):
|
|
"""导出excel
|
|
导出excel
|
|
"""
|
|
field_data = ['大类', '物料编号', '名称', '规格', '型号', '计量单位', '仓库位号', "安全库存", "单价"]
|
|
queryset = self.filter_queryset(self.get_queryset())
|
|
if queryset.count() > 1000:
|
|
raise ParseError('数据量超过1000,请筛选后导出')
|
|
odata = MaterialExportSerializer(queryset, many=True).data
|
|
# 处理数据
|
|
field_keys = ['cate', 'number', 'name', 'specification', 'model', 'unit',
|
|
'bin_number_main', 'count_safe', 'unit_price']
|
|
getter = itemgetter(*field_keys)
|
|
data = [list(getter(item)) for item in odata]
|
|
return Response({'path': export_excel(field_data, data, '物料清单')})
|
|
|
|
class ShiftViewSet(ListModelMixin, CustomGenericViewSet):
|
|
"""
|
|
list:班次
|
|
|
|
班次
|
|
"""
|
|
perms_map = {'get': '*'}
|
|
queryset = Shift.objects.all()
|
|
serializer_class = ShiftSerializer
|
|
search_fields = ['name']
|
|
ordering = ['rule', 'sort', 'id']
|
|
|
|
|
|
class MgroupViewSet(CustomModelViewSet):
|
|
"""
|
|
list:测点集
|
|
|
|
测点集
|
|
"""
|
|
queryset = Mgroup.objects.all()
|
|
serializer_class = MgroupSerializer
|
|
select_related_fields = ['create_by', 'belong_dept', 'process']
|
|
filterset_fields = {
|
|
"belong_dept": ["exact"],
|
|
"process": ["exact"],
|
|
"cate": ["exact"],
|
|
"belong_dept__name": ["exact", "contains"],
|
|
"name": ["exact", "contains"],
|
|
"code": ["exact", "in", "isnull"]
|
|
}
|
|
search_fields = ['name']
|
|
ordering = ['sort', 'create_time']
|
|
|
|
def perform_destroy(self, instance):
|
|
Route.objects.filter(
|
|
mgroup=instance, material__need_route=False).delete()
|
|
Route.objects.filter(mgroup=instance).update(mgroup=None)
|
|
instance.delete()
|
|
|
|
@action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=MgroupDaysSerializer)
|
|
def get_days(self, request):
|
|
"""
|
|
获取所属班年月日
|
|
|
|
给定一个时间获取所属班年月日
|
|
"""
|
|
data = request.data
|
|
sr = MgroupDaysSerializer(data=data)
|
|
sr.is_valid(raise_exception=True)
|
|
vdata = sr.validated_data
|
|
mgroup = Mgroup.objects.get(id=vdata["mgroup"])
|
|
return Response(get_mgroup_days(mgroup, vdata.get("now", None)))
|
|
|
|
|
|
|
|
|
|
class TeamViewSet(CustomModelViewSet):
|
|
"""
|
|
list:班组
|
|
|
|
班组
|
|
"""
|
|
queryset = Team.objects.all()
|
|
serializer_class = TeamSerializer
|
|
select_related_fields = ['belong_dept', 'leader']
|
|
filterset_fields = ['belong_dept']
|
|
search_fields = ['rule']
|
|
|
|
|
|
class TeamMemberViewSet(ListModelMixin, BulkCreateModelMixin, BulkDestroyModelMixin, CustomGenericViewSet):
|
|
"""
|
|
list: 班组成员
|
|
|
|
班组成员
|
|
"""
|
|
perms_map = {'get': '*', 'post': 'team.update', 'delete': 'team.delete'}
|
|
queryset = TeamMember.objects.all()
|
|
serializer_class = TeamMemberSerializer
|
|
select_related_fields = ['mgroup', 'post', 'team', 'user']
|
|
filterset_fields = ['mgroup', 'post', 'team']
|
|
|
|
|
|
class GoalViewSet(CustomModelViewSet):
|
|
"""
|
|
list: 目标
|
|
|
|
目标
|
|
"""
|
|
queryset = Goal.objects.all()
|
|
serializer_class = GoalSerializer
|
|
select_related_fields = ['mgroup', 'goal_cate']
|
|
filterset_class = GoalFilter
|
|
search_fields = ['name']
|
|
ordering = ['mgroup__sort', 'goal_cate__sort', 'goal_cate__create_time']
|
|
|
|
@action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=MgroupGoalYearSerializer)
|
|
def mgroup_goals_year(self, request, pk=None):
|
|
"""
|
|
获取工段全年目标
|
|
|
|
获取工段全年目标
|
|
"""
|
|
sr = MgroupGoalYearSerializer(data=request.data)
|
|
sr.is_valid(raise_exception=True)
|
|
vdata = sr.validated_data
|
|
res = get_mgroup_goals(vdata['mgroup'], vdata['year'], True)
|
|
return Response(res)
|
|
|
|
|
|
class ProcessViewSet(CustomModelViewSet):
|
|
queryset = Process.objects.all()
|
|
serializer_class = ProcessSerializer
|
|
select_related_fields = ['belong_dept']
|
|
search_fields = ['name', 'cate']
|
|
filterset_fields = {
|
|
"cate": ['exact'],
|
|
"parent": ['isnull', "exact"]
|
|
}
|
|
ordering = ['sort', 'create_time']
|
|
|
|
def perform_destroy(self, instance):
|
|
if Route.objects.filter(process=instance).exists():
|
|
raise ParseError('存在使用的工艺路线!')
|
|
return super().perform_destroy(instance)
|
|
|
|
|
|
|
|
class RoutePackViewSet(CustomModelViewSet):
|
|
"""
|
|
list: 工艺路线包
|
|
|
|
工艺路线包
|
|
"""
|
|
queryset = RoutePack.objects.all()
|
|
serializer_class = RoutePackSerializer
|
|
search_fields = ['name']
|
|
select_related_fields = ['material', 'create_by', "document"]
|
|
filterset_fields = ['material', 'state']
|
|
|
|
def update(self, request, *args, **kwargs):
|
|
obj: RoutePack = self.get_object()
|
|
if obj.state != RoutePack.RP_S_CREATE:
|
|
raise ParseError('该状态下不可编辑')
|
|
return super().update(request, *args, **kwargs)
|
|
|
|
@transaction.atomic
|
|
def destroy(self, request, *args, **kwargs):
|
|
obj: RoutePack = self.get_object()
|
|
if obj.state != RoutePack.RP_S_CREATE:
|
|
raise ParseError('该状态下不可删除')
|
|
obj.delete()
|
|
Ticket.objects.filter(ticket_data__t_id=obj.id, ticket_data__t_model='routepack').delete()
|
|
return Response(status=204)
|
|
|
|
@transaction.atomic
|
|
@action(methods=['post'], detail=False, perms_map={'post': 'routepack.create'}, serializer_class=RoutePackCopySerializer)
|
|
def copy(self, request, *args, **kwargs):
|
|
"""复制工艺路线
|
|
|
|
复制工艺路线
|
|
"""
|
|
data = request.data
|
|
sr = RoutePackCopySerializer(data=data)
|
|
sr.is_valid(raise_exception=True)
|
|
vdata = sr.validated_data
|
|
user = request.user
|
|
now = timezone.now()
|
|
new_name = vdata["new_name"]
|
|
rp = RoutePack.objects.get(id=vdata["routepack"])
|
|
matin = Material.objects.get(id=vdata["material_in"])
|
|
matout = Material.objects.get(id=vdata["material_out"])
|
|
obj_c = RoutePack()
|
|
obj_c.name = new_name
|
|
obj_c.material = matout
|
|
obj_c.create_by = user
|
|
obj_c.create_time = now
|
|
obj_c.save()
|
|
genM = {}
|
|
for ind, route in enumerate(Route.get_routes(routepack=rp)):
|
|
route_new = Route()
|
|
process = route.process
|
|
for f in Route._meta.fields:
|
|
if f.name in ['process', 'sort', 'is_autotask', 'is_count_utask', 'out_rate', 'div_number', 'hour_work', 'batch_bind']:
|
|
setattr(route_new, f.name, getattr(route, f.name, None))
|
|
route_new.material = matout
|
|
# material_out = RouteSerializer.gen_material_out(instance=route_new, material_out_tracking=route.material_out.tracking)
|
|
# route_new.material_out = material_out
|
|
# if ind == 0:
|
|
# route_new.material_in = matin
|
|
# elif route.material_in.process and route.material_in.process.id in genM:
|
|
# route_new.material_in = genM[route.material_in.process.id]
|
|
# genM[process.id] = material_out
|
|
route_new.routepack = obj_c
|
|
route_new.create_by = user
|
|
route_new.create_time = now
|
|
route_new.save()
|
|
for rm in RouteMat.objects.filter(route=route):
|
|
rm_new = RouteMat()
|
|
rm_new.route = route_new
|
|
rm_new.material = rm.material
|
|
rm_new.save()
|
|
return Response({"id": route_new.id})
|
|
|
|
@transaction.atomic
|
|
@action(methods=['post'], detail=True, perms_map={'post': 'routepack.update'}, serializer_class=Serializer)
|
|
def toggle_state(self, request, *args, **kwargs):
|
|
"""变更工艺路线状态
|
|
|
|
变更工艺路线状态
|
|
"""
|
|
ins:RoutePack = self.get_object()
|
|
if ins.state == RoutePack.RP_S_CONFIRM:
|
|
ins.state = RoutePack.RP_S_CREATE
|
|
elif ins.state == RoutePack.RP_S_CREATE:
|
|
if ins.ticket is not None:
|
|
ins.get_gjson(need_update=True)
|
|
ins.state = RoutePack.RP_S_CONFIRM
|
|
else:
|
|
raise ParseError("该路线未提交审核")
|
|
else:
|
|
raise ParseError("该状态下不可变更")
|
|
ins.save()
|
|
return Response()
|
|
|
|
@action(methods=['get'], detail=True, perms_map={'get': '*'})
|
|
def dag(self, request, *args, **kwargs):
|
|
"""获取总图
|
|
|
|
获取总图
|
|
"""
|
|
return Response(Route.get_dag(rqs=Route.objects.filter(routepack=self.get_object())))
|
|
|
|
@action(methods=['get'], detail=True, perms_map={'get': '*'})
|
|
def dags(self, request, *args, **kwargs):
|
|
"""获取所有子图
|
|
|
|
获取所有子图
|
|
"""
|
|
ins = self.get_object()
|
|
return Response(ins.get_dags())
|
|
|
|
@action(methods=['get'], detail=True, perms_map={'get': '*'})
|
|
def final_materials(self, request, *args, **kwargs):
|
|
"""获取最终产品
|
|
|
|
获取最终产品"""
|
|
ins:RoutePack = self.get_object()
|
|
matIds = ins.get_final_material_ids()
|
|
qs = Material.objects.filter(id__in=matIds)
|
|
res = [{"id": x.id, "name": str(x)} for x in qs]
|
|
return Response(res)
|
|
class RouteViewSet(CustomModelViewSet):
|
|
queryset = Route.objects.all()
|
|
serializer_class = RouteSerializer
|
|
filterset_class = RouteFilter
|
|
ordering = ['sort', 'process__sort', 'create_time']
|
|
ordering_fields = ['sort', 'process__sort', 'create_time', 'update_time']
|
|
select_related_fields = ['material',
|
|
'process', 'material_in', 'material_out', 'mgroup', 'routepack']
|
|
|
|
@transaction.atomic
|
|
def perform_update(self, serializer):
|
|
ins:Route = self.get_object()
|
|
if ins.from_route is not None:
|
|
raise ParseError('该工艺步骤引用其他步骤, 无法编辑')
|
|
old_m_in, old_m_out, process = ins.material_in, ins.material_out, ins.process
|
|
routepack = ins.routepack
|
|
if routepack and routepack.state != RoutePack.RP_S_CREATE:
|
|
raise ParseError('该工艺路线非创建中不可编辑')
|
|
ins_n:Route = serializer.save()
|
|
if ins_n.material_in != old_m_in or ins_n.material_out != old_m_out or ins_n.process != process:
|
|
raise ParseError("该工艺步骤被其他步骤引用, 无法修改关键信息")
|
|
|
|
def perform_destroy(self, instance:Route):
|
|
if Route.objects.filter(from_route=instance).exists():
|
|
raise ParseError('该工艺步骤被其他步骤引用,无法删除')
|
|
return super().perform_destroy(instance)
|
|
|
|
|
|
class SruleViewSet(CustomModelViewSet):
|
|
"""
|
|
list: 排班规则
|
|
"""
|
|
queryset = Srule.objects.all()
|
|
serializer_class = SruleSerializer
|
|
select_related_fields = ['belong_dept']
|
|
filterset_fields = ['belong_dept']
|
|
search_fields = ['belong_dept__name']
|
|
|
|
def add_info_for_list(self, data):
|
|
rule_list = [x['rule'] for x in data]
|
|
teamIds = []
|
|
for rule in rule_list:
|
|
teamIds.extend(rule)
|
|
teams = Team.objects.filter(id__in=teamIds).values_list('id', 'name')
|
|
teams_dict = dict(teams)
|
|
for item in data:
|
|
rule = item["rule"]
|
|
item["rule_display"] = "->".join([teams_dict.get(x, '未知') for x in rule])
|
|
return data
|
|
|
|
class RouteMatViewSet(CustomListModelMixin, BulkCreateModelMixin, BulkDestroyModelMixin, CustomGenericViewSet):
|
|
perms_map = {"get": "*", "post": "route.update", "delete": "route.update"}
|
|
queryset = RouteMat.objects.all()
|
|
serializer_class = RouteMatSerializer
|
|
filterset_fields = ['route', 'material'] |