factory/apps/wpm/views.py

514 lines
20 KiB
Python

from django.db import transaction
from django.db.models import Prefetch
from django.shortcuts import render
from rest_framework.decorators import action
from rest_framework.exceptions import ParseError
from rest_framework.mixins import DestroyModelMixin, ListModelMixin, UpdateModelMixin, CreateModelMixin
from rest_framework.response import Response
from rest_framework.serializers import Serializer
from django.db.models import Sum
from django.utils import timezone
from apps.system.models import User
from apps.mtm.models import Material, Process
from apps.pm.models import Mtask
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
from apps.utils.mixins import BulkCreateModelMixin
from .filters import StLogFilter, SfLogFilter, WMaterialFilter, MlogFilter, HandoverFilter, MlogbFilter
from .models import SfLog, SfLogExp, StLog, WMaterial, Mlog, Handover, Mlogb, AttLog, OtherLog, Fmlog
from .serializers import (SflogExpSerializer, SfLogSerializer, StLogSerializer, WMaterialSerializer, MlogRevertSerializer,
MlogSerializer, MlogRelatedSerializer, DeptBatchSerializer, HandoverSerializer, HandoverUpdateSerializer,
GenHandoverSerializer, GenHandoverWmSerializer, MlogAnaSerializer,
AttLogSerializer, OtherLogSerializer, MlogInitSerializer, MlogChangeSerializer,
MlogbDetailSerializer, MlogbInSerializer, MlogbInUpdateSerializer,
MlogbOutUpdateSerializer, FmlogSerializer, FmlogUpdateSerializer)
from .services import mlog_submit, update_mtask, handover_submit, mlog_revert, cal_material_count_from_mlog, cal_mtask_progress_from_mlog
from apps.utils.thread import MyThread
from apps.monitor.services import create_auditlog, delete_auditlog
from apps.wpm.services import mlog_submit_validate, generate_new_batch
# Create your views here.
class StLogViewSet(CustomModelViewSet):
"""
list:异常记录
异常记录
"""
queryset = StLog.objects.all()
serializer_class = StLogSerializer
select_related_fields = ['mgroup']
filterset_class = StLogFilter
ordering = ['-start_time']
def destroy(self, request, *args, **kwargs):
obj: StLog = self.get_object()
if obj.is_shutdown:
if obj.duration_sec <= 60:
pass
else:
raise ParseError('停机记录不可删除')
return super().destroy(request, *args, **kwargs)
class SfLogViewSet(UpdateModelMixin, ListModelMixin, DestroyModelMixin, CustomGenericViewSet):
"""
list:值班记录
值班记录
"""
perms_map = {'get': '*', 'put': 'sflog.update', 'delete': 'sflog.delete'}
queryset = SfLog.objects.all()
serializer_class = SfLogSerializer
select_related_fields = ['mgroup', 'shift', 'team', 'leader']
filterset_class = SfLogFilter
search_fields = ['note']
ordering = ['-start_time']
@action(methods=['get'], detail=True, perms_map={'get': '*'})
def init_test(self, request, pk=None):
"""
初始化检测录入
初始化检测录入
"""
from apps.qm.models import QuaStat, TestItem
from apps.qm.serializers import QuaStatSerializer
obj = self.get_object()
test_materials = Material.objects.filter(
id__in=obj.mgroup.test_materials).order_by('sort', '-create_time')
for material in test_materials:
testitems = TestItem.objects.filter(
id__in=material.testitems).order_by('sort', '-create_time')
for testitem in testitems:
params = {'material': material,
'testitem': testitem, 'sflog': obj}
QuaStat.objects.get_or_create(
**params, defaults={**params, **{'create_by': request.user, 'belong_dept': obj.mgroup.belong_dept}})
qs = QuaStat.objects.filter(sflog=obj).order_by(
'material__sort', 'material__create_time', 'testitem__sort', 'testitem__create_time')
sr = QuaStatSerializer(instance=qs, many=True)
return Response(sr.data)
class SfLogExpViewSet(ListModelMixin, UpdateModelMixin, CustomGenericViewSet):
"""
list:异常值班处理
异常值班处理
"""
perms_map = {'get': '*', 'put': 'sflogexp.update'}
queryset = SfLogExp.objects.all()
serializer_class = SflogExpSerializer
select_related_fields = ['sflog', 'sflog__mgroup', 'stlog', 'sflog__team', 'sflog__shift', 'sflog__leader', 'stlog']
filterset_fields = ['sflog', 'stlog']
class WMaterialViewSet(ListModelMixin, CustomGenericViewSet):
"""
list: 车间库存
车间库存
"""
perms_map = {'get': '*'}
queryset = WMaterial.objects.filter(count__gt=0)
serializer_class = WMaterialSerializer
select_related_fields = ['material', 'belong_dept', 'material__process', 'supplier']
search_fields = ['material__name',
'material__number', 'material__specification', 'batch', 'material__model']
filterset_class = WMaterialFilter
ordering_fields = ["update_time", "state", "count", "count_xtest"]
def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset)
if self.request.query_params.get('state_all'):
return queryset
return queryset.exclude(state=WMaterial.WM_SCRAP)
@action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=DeptBatchSerializer)
@transaction.atomic
def batchs(self, request):
"""获取车间的批次号(废弃)
获取车间的批次号
"""
sr = DeptBatchSerializer(data=request.data)
sr.is_valid(raise_exception=True)
vdata = sr.validated_data
batchs = WMaterial.objects.filter(
belong_dept__name=vdata['belong_dept_name'], count__gt=0).values_list('batch', flat=True).distinct()
return Response(list(batchs))
class MlogViewSet(CustomModelViewSet):
"""
list: 生产日志
生产日志
"""
queryset = Mlog.objects.all()
serializer_class = MlogSerializer
select_related_fields = ['create_by', 'update_by', 'mtask', 'mtaskb'
'handle_user', 'handle_user_2', 'equipment',
'equipment_2', 'material_in', 'material_out', 'route__routepack',
'supplier', 'ticket', 'mgroup__process', 'test_user', 'belong_dept']
prefetch_related_fields = ['handle_users',
'material_outs', 'b_mlog', 'equipments']
filterset_class = MlogFilter
search_fields = ['material_in__name',
'material_in__number', 'material_in__specification', 'batch', 'material_in__model',
'material_out__name', 'material_out__number', 'material_out__specification', 'material_out__model',]
@transaction.atomic
def perform_create(self, serializer):
ins = serializer.save()
data = MlogSerializer(ins).data
create_auditlog('create', ins, data)
@transaction.atomic
def perform_destroy(self, instance):
if instance.submit_time is not None:
raise ParseError('日志已提交不可变动')
delete_auditlog(instance, instance.id)
instance.delete()
@transaction.atomic
def perform_update(self, serializer):
ins = serializer.instance
if ins.submit_time is not None:
raise ParseError('该日志已提交!')
val_old = MlogSerializer(instance=ins).data
serializer.save()
val_new = MlogSerializer(instance=ins).data
create_auditlog('update', ins, val_new, val_old)
@action(methods=['post'], detail=False, perms_map={'post': 'mlog.init'}, serializer_class=MlogInitSerializer)
def init(self, request, *args, **kwargs):
"""初始化日志
初始化日志
"""
sr = self.get_serializer(data=request.data)
sr.is_valid(raise_exception=True)
ins = sr.save()
return Response(MlogSerializer(ins).data)
@action(methods=['post'], detail=True, perms_map={'post': 'mlog.update'}, serializer_class=MlogChangeSerializer)
def change(self, request, *args, **kwargs):
"""修改日志
修改日志
"""
ins = self.get_object()
sr = MlogChangeSerializer(instance=ins, data=request.data)
sr.is_valid(raise_exception=True)
sr.save()
return Response(MlogSerializer(ins).data)
@action(methods=['post'], detail=True, perms_map={'post': 'mlog.submit'}, serializer_class=Serializer)
def submit(self, request, *args, **kwargs):
"""日志提交(变动车间库存)
日志提交
"""
ins: Mlog = self.get_object()
vdata_old = MlogSerializer(ins).data
now = timezone.now()
if ins.ticket:
raise ParseError('该日志存在审批!')
else:
p: Process = ins.mgroup.process
if p.mlog_need_ticket:
raise ParseError('该日志需要审批!')
mlog_submit_validate(ins)
with transaction.atomic():
mlog_submit(ins, self.request.user, now)
vdata_new = MlogSerializer(ins).data
create_auditlog('submit', ins, vdata_new,
vdata_old, now, self.request.user)
MyThread(target=cal_mtask_progress_from_mlog,args=(ins,)).start()
MyThread(target=cal_material_count_from_mlog,args=(ins,)).start()
return Response(vdata_new)
@action(methods=['post'], detail=True, perms_map={'post': 'mlog.submit'}, serializer_class=MlogRevertSerializer)
def revert(self, request, *args, **kwargs):
"""撤回日志提交
撤回日志提交
"""
ins: Mlog = self.get_object()
user = request.user
if ins.submit_time is None:
raise ParseError('日志未提交不可撤销')
if user != ins.submit_user:
raise ParseError('非提交人不可撤销!')
now = timezone.now()
with transaction.atomic():
mlog_revert(ins, user, now)
create_auditlog('revert', ins, {}, {}, now, user,
request.data.get('change_reason', ''))
MyThread(target=cal_mtask_progress_from_mlog,args=(ins,)).start()
MyThread(target=cal_material_count_from_mlog,args=(ins,)).start()
return Response(MlogSerializer(instance=ins).data)
@action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=MlogRelatedSerializer)
@transaction.atomic
def related_first(self, request, *args, **kwargs):
"""获取相关任务的第一道工序日志
获取相关任务的第一道工序日志
"""
sr = MlogRelatedSerializer(data=request.data)
sr.is_valid(raise_exception=True)
vdata = sr.validated_data
mtask = vdata['mtask']
if mtask.utask:
mtasks = mtask.related
mlogs = Mlog.objects.filter(mtask__in=mtasks).order_by(
'mtask__mgroup__process__sort', 'batch', 'create_time')
data = MlogSerializer(instance=mlogs, many=True).data
res_data = []
for ind, val in enumerate(data):
if ind == 0:
res_data.append(val)
else:
before = data[ind-1]
if val['batch'] != before['batch']:
res_data.append(val)
return Response(res_data)
@action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=MlogAnaSerializer)
def ana(self, request):
"""核心统计数据
核心统计数据
"""
sr = MlogAnaSerializer(data=request.data)
sr.is_valid(raise_exception=True)
vdata = sr.validated_data
mlogs = Mlog.objects.exclude(submit_time=None)
if vdata.get('belong_dept_name', ''):
mlogs = mlogs.filter(
mgroup__belong_dept__name=vdata['belong_dept_name'])
if vdata.get('material_cate', ''):
mlogs = mlogs.filter(
material_out__cate=vdata['material_cate'])
if vdata.get('start_date', ''):
mlogs = mlogs.filter(handle_date__gte=vdata['start_date'])
if vdata.get('end_date', ''):
mlogs = mlogs.filter(handle_date__lte=vdata['end_date'])
res = mlogs.aggregate(
count_real=Sum('count_real'),
count_ok=Sum('count_ok'),
count_notok=Sum('count_notok'),
count_n_zw=Sum('count_n_zw'),
count_n_tw=Sum('count_n_tw'),
count_n_qp=Sum('count_n_qp'),
count_n_wq=Sum('count_n_wq'),
count_n_dl=Sum('count_n_dl'),
count_n_pb=Sum('count_n_pb'),
count_n_dxt=Sum('count_n_dxt'),
count_n_jsqx=Sum('count_n_jsqx'),
count_n_qt=Sum('count_n_qt'))
for i in res:
if res[i] is None:
res[i] = 0
return Response(res)
class HandoverViewSet(CustomModelViewSet):
"""
list: 交接记录
交接记录
"""
queryset = Handover.objects.all()
serializer_class = HandoverSerializer
update_serializer_class = HandoverUpdateSerializer
select_related_fields = ['send_user', 'send_mgroup', 'send_dept', 'recive_user', 'recive_mgroup', 'recive_dept', 'wm']
filterset_class = HandoverFilter
search_fields = ['id', 'material__name',
'material__number', 'material__specification', 'batch', 'material__model']
def perform_destroy(self, instance):
user = self.request.user
if instance.submit_time is not None:
raise ParseError('日志已提交不可变动')
if instance.send_user != user and instance.recive_user != user and instance.create_by != user:
raise ParseError('非交送人和接收人不可删除该记录')
return super().perform_destroy(instance)
@action(methods=['post'], detail=True, perms_map={'post': 'handover.submit'}, serializer_class=Serializer)
@transaction.atomic
def submit(self, request, *args, **kwargs):
"""交接记录提交(变动车间库存)
交接记录提交
"""
ins: Handover = self.get_object()
user: User = self.request.user
if ins.type != Handover.H_SCRAP:
if user == ins.recive_user or user.belong_dept == ins.recive_user.belong_dept:
pass
else:
raise ParseError('非接收人不可提交')
if ins.submit_time is None:
handover_submit(ins, user, None)
return Response()
@action(methods=['post'], detail=False, perms_map={'post': 'handover.create'}, serializer_class=GenHandoverWmSerializer)
@transaction.atomic
def gen_by_wm(self, request):
"""从车间库存生成交接记录(废弃)
从车间库存生成交接记录
"""
sr = GenHandoverWmSerializer(data=request.data)
sr.is_valid(raise_exception=True)
vdata = sr.validated_data
user = request.user
send_date, send_mgroup, send_user, recive_dept, recive_user, wm, count = vdata['send_date'], vdata['send_mgroup'], vdata[
'send_user'], vdata['recive_dept'], vdata['recive_user'], vdata['wm'], vdata['count']
if send_mgroup.belong_dept != wm.belong_dept:
raise ParseError('送料工段错误!')
handover = Handover.objects.create(
send_date=send_date,
send_user=send_user,
recive_dept=recive_dept,
recive_user=recive_user,
send_mgroup=send_mgroup,
send_dept=wm.belong_dept,
batch=wm.batch,
material=wm.material,
count=count,
wm=wm,
create_by=user
)
return Response({'handover': handover.id})
@action(methods=['post'], detail=False, perms_map={'post': 'handover.create'}, serializer_class=GenHandoverSerializer)
@transaction.atomic
def gen_by_mlog(self, request):
"""从生产日志生成交接记录(废弃)
从生产日志生成交接记录
"""
sr = GenHandoverSerializer(data=request.data)
sr.is_valid(raise_exception=True)
vdata = sr.validated_data
user = request.user
send_date, send_user, recive_dept, recive_user = vdata['send_date'], vdata[
'send_user'], vdata['recive_dept'], vdata['recive_user']
for mlog in vdata['mlogs']:
Handover.objects.create(
send_date=send_date,
send_user=send_user,
recive_dept=recive_dept,
recive_user=recive_user,
send_dept=mlog.mgroup.belong_dept,
batch=mlog.batch,
material=mlog.material_out,
count=mlog.count_real,
count_eweight=mlog.count_real_eweight,
mlog=mlog,
send_mgroup=mlog.mgroup,
create_by=user
)
return Response()
class AttlogViewSet(CustomModelViewSet):
"""
list: 车间到岗
车间到岗
"""
queryset = AttLog.objects.all()
serializer_class = AttLogSerializer
select_related_fields = ['user', 'post', 'sflog']
filterset_fields = ['sflog__mgroup',
'sflog__mgroup__belong_dept__name', 'sflog__work_date', 'sflog__mgroup__cate', 'sflog__mgroup__need_enm']
ordering = ['-sflog__work_date', 'create_time']
class OtherLogViewSet(CustomModelViewSet):
"""
list: 其他生产记录
其他生产记录
"""
queryset = OtherLog.objects.all()
serializer_class = OtherLogSerializer
filterset_fields = {
"product": ["exact"],
"handle_date": ["exact", "gte", "lte"]
}
search_fields = ['product']
ordering = ['-handle_date', '-create_time']
class MlogbViewSet(ListModelMixin, CustomGenericViewSet):
perms_map = {'get': '*'}
queryset = Mlogb.objects.all()
serializer_class = MlogbDetailSerializer
filterset_class = MlogbFilter
class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, CustomGenericViewSet):
perms_map = {'post': 'mlog.update', 'delete': 'mlog.update', 'put': 'mlog.update'}
queryset = Mlogb.objects.filter(material_in__isnull=False)
serializer_class = MlogbInSerializer
update_serializer_class = MlogbInUpdateSerializer
@transaction.atomic
def perform_destroy(self, instance):
ins: Mlogb = instance
if ins.mlog.submit_time is not None:
raise ParseError('生产日志已提交不可编辑')
if ins.mtask:
query_dict = {"material_out__isnull": False, "mlog": ins.mlog, "mtask": ins.mtask}
route = ins.mtask.route
if route.batch_bind:
query_dict["batch__contains"] = ins.batch
Mlogb.objects.filter(**query_dict).delete()
instance.delete()
@transaction.atomic
def perform_create(self, serializer):
ins: Mlogb = serializer.save()
mlog: Mlog = ins.mlog
# 创建输出
if ins.mtask and ins.material_in:
material_out = mlog.material_out
if material_out is None:
raise ParseError('产物不可为空')
m_dict = {
"mtask": ins.mtask,
"mlog": ins.mlog,
"material_out": ins.mlog.material_out
}
m_dict['batch'] = generate_new_batch(ins.batch, mlog)
wm_in: WMaterial = ins.wm_in
Mlogb.objects.get_or_create(**m_dict, defaults={"batch_ofrom": wm_in.batch_ofrom, "material_ofrom": wm_in.material_ofrom})
class MlogbOutViewSet(UpdateModelMixin, CustomGenericViewSet):
perms_map = {"put": "mlog.update"}
queryset = Mlogb.objects.filter(material_out__isnull=False)
serializer_class = MlogbOutUpdateSerializer
class FmlogViewSet(CustomModelViewSet):
perms_map = {'get': '*', 'post': 'mlog.create', 'put': 'mlog.update', 'delete': 'mlog.delete'}
queryset = Fmlog.objects.all()
serializer_class = FmlogSerializer
update_serializer_class = FmlogUpdateSerializer
filterset_fields = ['mtask', 'mgroup', 'route']
select_related_fields = ['mtask', 'mgroup', 'route', 'route__routepack']
def destroy(self, request, *args, **kwargs):
ins = self.get_object()
if Mlog.objects.filter(fmlog=ins).exists():
raise ParseError('因存在二级日志不可删除')
return super().destroy(request, *args, **kwargs)