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 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 from .serializers import (SflogExpSerializer, SfLogSerializer, StLogSerializer, WMaterialSerializer, MlogRevertSerializer, MlogSerializer, MlogRelatedSerializer, DeptBatchSerializer, HandoverSerializer, GenHandoverSerializer, GenHandoverWmSerializer, MlogAnaSerializer, AttLogSerializer, OtherLogSerializer, MlogInitSerializer, MlogChangeSerializer, MlogbDetailSerializer, MlogbInSerializer, MlogbInUpdateSerializer, MlogbOutUpdateSerializer) 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 # 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'] 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'] search_fields = ['material__name', 'material__number', 'material__specification', 'batch', 'material__model'] filterset_class = WMaterialFilter @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', 'handle_user', 'handle_user_2', 'equipment', 'equipment_2', 'material_in', 'material_out'] 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.change'}, 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.submit_time: raise ParseError('该日志已提交!') if ins.mtask and ins.mtask.state == Mtask.MTASK_STOP: raise ParseError('该任务已停止!') 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(cal_mtask_progress_from_mlog,args=(ins,)).start() MyThread(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(cal_mtask_progress_from_mlog,args=(ins,)).start() MyThread(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 select_related_fields = ['send_user', 'send_dept', 'recive_user', 'recive_dept'] 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 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"] = ins.batch Mlogb.objects.filter(**query_dict).delete() instance.delete() @transaction.atomic def perform_create(self, serializer): ins: Mlogb = serializer.save() # 创建输出 if ins.mtask and ins.material_in: material_out = ins.mlog.material_out route = ins.mlog.route if material_out is None: raise ParseError('产物不可为空') m_dict = { "mtask": ins.mtask, "mlog": ins.mlog, "material_out": ins.mlog.material_out } if route.batch_bind: # 批次绑定 m_dict['batch'] = ins.batch Mlogb.objects.get_or_create(**m_dict, defaults=m_dict) class MlogbOutViewSet(UpdateModelMixin, CustomGenericViewSet): queryset = Mlogb.objects.filter(material_out__isnull=False) serializer_class = MlogbOutUpdateSerializer