from datetime import timezone from typing import List from django.db import transaction from rest_framework import serializers from rest_framework.views import APIView from apps.em.models import Equipment from apps.em.serializers import EquipmentSimpleSerializer from apps.hrm.services import HRMService from apps.inm.models import MaterialBatch from apps.inm.serializers import MaterialBatchSerializer from apps.mtm.models import Material, RecordFormField, Step, SubProduction, SubprodctionMaterial from apps.pm.filters import PlanFilterSet, SubproductionProgressFilterSet from apps.qm.models import TestRecord, TestRecordItem from apps.qm.serializers import TestRecordDetailBaseSerializer from apps.system.mixins import CreateUpdateModelAMixin from apps.pm.serializers import FirstTestAuditSerializer, FirstTestInitSerializer, GenSubPlanSerializer, PickNeedSerializer, PlanDestorySerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, ResourceConvertListSerializer, ResourceConvertSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer, SubProductionProgressSerializer from rest_framework.mixins import CreateModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin from apps.pm.models import ProductionPlan, SubProductionProgress, SubProductionPlan from rest_framework.viewsets import GenericViewSet, ModelViewSet from django.shortcuts import render from apps.sam.models import Order from rest_framework.exceptions import APIException, ParseError, ValidationError from rest_framework.response import Response from rest_framework.decorators import action from django.db.models import F from apps.system.serializers import UserSimpleSerializer from utils.tools import ranstr from django.db import transaction from rest_framework import status from django.utils import timezone # Create your views here. def updateOrderPlanedCount(order): """ 更新订单已排数量 """ planed_count = 0 plans = ProductionPlan.objects.filter(order=order) for i in plans: planed_count = planed_count + i.count order.planed_count = planed_count order.save() class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModelMixin, RetrieveModelMixin, GenericViewSet): """ 生产计划 """ perms_map = {'get': '*', 'post':'plan_create'} queryset = ProductionPlan.objects.select_related('order', 'order__contract', 'product') serializer_class = ProductionPlanSerializer search_fields = ['number', 'order__number', 'order__contract__number', 'product__number'] filterset_class = PlanFilterSet ordering_fields = ['id'] ordering = ['-id'] def get_serializer_class(self): if self.action in ['create']: return ProductionPlanCreateFromOrderSerializer elif self.action == 'list': return ProductionPlanSerializer return super().get_serializer_class() @transaction.atomic() def create(self, request, *args, **kwargs): data = request.data serializer = self.get_serializer(data=data) serializer.is_valid(raise_exception=True) if data.get('order', None): order = Order.objects.get(pk=data['order']) if order.planed_count >= data['count'] or data['count'] > 0: pass else: raise APIException('排产数量错误') instance = serializer.save(create_by=request.user, product=order.product) updateOrderPlanedCount(instance.order) return Response() @action(methods=['post'], detail=False, perms_map={'post':'plan_delete'}, serializer_class=PlanDestorySerializer) def deletes(self, request, pk=None): """ 批量物理删除 """ ProductionPlan.objects.filter(id__in=request.data.get('ids', [])).delete(soft=False) return Response() @action(methods=['post'], detail=True, perms_map={'post':'gen_subplan'}, serializer_class=GenSubPlanSerializer) @transaction.atomic def gen_subplan(self, request, pk=None): """ 生成子计划 """ production_plan=self.get_object() if production_plan.is_planed: raise APIException('已生成子计划') if production_plan.state != ProductionPlan.PLAN_STATE_PLANING: raise APIException('不可操作') subps = SubProduction.objects.filter(product=production_plan.product).order_by('process__number') for index, i in enumerate(subps): steps = Step.objects.filter(usedstep__subproduction=i, usedstep__subproduction__is_deleted=False, usedstep__is_deleted=False, is_deleted=False ).order_by('number').values('id', 'number', 'name', 'usedstep__remark', need_test=F('usedstep__need_test')) instance = SubProductionPlan.objects.create(production_plan=production_plan, subproduction=i, start_date=production_plan.start_date, end_date=production_plan.end_date, workshop=i.process.workshop, process=i.process, create_by=request.user, steps = list(steps), number=production_plan.number + '-' + str(index+1)) # 生成子计划物料需求/进度 for m in SubprodctionMaterial.objects.filter(subproduction=i, is_deleted=False).order_by('sort'): spro = SubProductionProgress.objects.create(material=m.material, type=m.type, is_main=m.is_main, count=m.count*production_plan.count, subproduction_plan=instance) production_plan.is_planed=True production_plan.save() return Response() @action(methods=['put'], detail=True, perms_map={'post':'plan_toggle'}, serializer_class=serializers.Serializer) @transaction.atomic def toggle(self, request, pk=None): """ 计划暂停或启动 """ plan = self.get_object() if plan.state == ProductionPlan.PLAN_STATE_PAUSE: plan.state = plan.old_state plan.old_state = None plan.save() return Response() elif plan.state <= ProductionPlan.PLAN_STATE_WORKING: plan.old_state = plan.state plan.state = ProductionPlan.PLAN_STATE_PAUSE plan.save() return Response() raise APIException('不可操作') @action(methods=['put'], detail=True, perms_map={'post':'plan_stop'}, serializer_class=serializers.Serializer) @transaction.atomic def stop(self, request, pk=None): """ 计划终止 """ plan = self.get_object() if plan.state == ProductionPlan.PLAN_STATE_PAUSE: plan.state = ProductionPlan.PLAN_STATE_STOP plan.save() return Response() raise APIException('不可操作') class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateModelMixin, GenericViewSet): """ 子生产计划-列表/修改 """ perms_map = {'get': '*', 'put':'*'} queryset = SubProductionPlan.objects.select_related('process', 'workshop', 'subproduction', 'product', 'production_plan__product', 'leader_1', 'leader_2', 'leader_3', 'first_test') search_fields = [] serializer_class = SubProductionPlanListSerializer filterset_fields = ['production_plan', 'process', 'state', 'product', 'workshop'] ordering_fields = ['process__number'] ordering = ['process__number'] def get_serializer_class(self): if self.action == 'list': return SubProductionPlanListSerializer elif self.action == 'update': return SubProductionPlanUpdateSerializer return super().get_serializer_class() @action(methods=['get'], detail=True, perms_map={'get':'*'}, serializer_class=SubProductionProgressSerializer) def progress(self, request, pk=None): """ 生产进度详情 """ obj = self.get_object() serializer = SubProductionProgressSerializer(instance=obj.progress_subplan, many=True) return Response(serializer.data) @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=serializers.Serializer) @transaction.atomic def issue(self, request, pk=None): """ 下达任务 """ obj = self.get_object() if obj.state == SubProductionPlan.SUBPLAN_STATE_PLANING: obj.state = SubProductionPlan.SUBPLAN_STATE_ASSGINED obj.save() plan = obj.production_plan if plan.state == ProductionPlan.PLAN_STATE_PLANING: plan.state = ProductionPlan.PLAN_STATE_ASSGINED plan.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 [SubProductionPlan.SUBPLAN_STATE_ASSGINED, SubProductionPlan.SUBPLAN_STATE_ACCEPTED]: obj.state = SubProductionPlan.SUBPLAN_STATE_WORKING obj.start_date_real = timezone.now() obj.save() plan = obj.production_plan if plan.state in [ProductionPlan.PLAN_STATE_ASSGINED, ProductionPlan.PLAN_STATE_ACCEPTED]: plan.state = ProductionPlan.PLAN_STATE_WORKING plan.save() return Response() raise APIException('计划状态有误') @action(methods=['get'], detail=True, perms_map={'get':'*'}, serializer_class=serializers.Serializer) def pick_need_(self, request, pk=None): """ 领料需求清单 """ obj = self.get_object() instance = SubProductionProgress.objects.filter(subproduction_plan=obj, type=SubprodctionMaterial.SUB_MA_TYPE_IN) serializer = SubProductionProgressSerializer(instance=instance, many=True) return Response(serializer.data) @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=serializers.Serializer) def pick_need(self, request, pk=None): """ 领料需求清单/库存数 """ obj = self.get_object() instance = SubProductionProgress.objects.filter(subproduction_plan=obj, type=SubprodctionMaterial.SUB_MA_TYPE_IN) serializer = SubProductionProgressSerializer(instance=instance, many=True) need = serializer.data materials = [] for i in need: materials.append(i['material']) objs = MaterialBatch.objects.filter(material__id__in=materials, count__gt=0).order_by('material__number') have = MaterialBatchSerializer(instance=objs, many=True).data return Response({'need':need, 'have':have}) @action(methods=['post'], detail=True, perms_map={'post':'first_test'}, serializer_class=FirstTestInitSerializer) @transaction.atomic def first_test_init(self, request, pk=None): """ 首件检查表初始化 """ obj = self.get_object() first_test = obj.first_test if first_test: return Response(TestRecordDetailBaseSerializer(instance=first_test).data) else: rdata = request.data serializer = self.get_serializer(data=rdata) serializer.is_valid(raise_exception=True) form = serializer.validated_data.get('form') savedict = dict( create_by=request.user, subproduction_plan=obj, type = TestRecord.TEST_FIRST, form=form) tr = TestRecord.objects.create(**savedict) for i in RecordFormField.objects.filter(form=form, is_deleted=False): tri = TestRecordItem() tri.test_record = tr tri.form_field = i tri.is_hidden = i.is_hidden tri.create_by = request.user tri.save() obj.first_test = tr obj.save() return Response(TestRecordDetailBaseSerializer(instance=tr).data) @action(methods=['post'], detail=True, perms_map={'post':'first_audit'}, serializer_class=FirstTestAuditSerializer) @transaction.atomic def first_audit(self, request, pk=None): """ 首件审核 """ obj = self.get_object() if obj.leader_1 and obj.leader_2 and obj.leader_3: raise ValidationError('首件确认已完成') if obj.first_test is None: raise ValidationError('未进行首件检查') if not obj.first_test.is_submited: raise ValidationError('首件检查未提交') if not obj.first_test.is_testok: raise ValidationError('首件检查不合格') serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) vdata = serializer.validated_data le = vdata.get('leader') if le not in ['leader_1', 'leader_2', 'leader_3']: return Response('审核人有误', status=status.HTTP_400_BAD_REQUEST) if vdata.get('leader') == 'leader_1': obj.leader_1 = request.user elif vdata.get('leader') == 'leader_2': obj.leader_2 = request.user else: obj.leader_3 = request.user obj.first_sign_time = timezone.now() obj.save() return Response(UserSimpleSerializer(instance=request.user).data) class SubProductionProgressViewSet(ListModelMixin, GenericViewSet): """ 生产进度 """ perms_map = {'get': '*'} queryset = SubProductionProgress.objects.select_related('material', 'subproduction_plan') search_fields = [] serializer_class = SubProductionProgressSerializer filterset_class = SubproductionProgressFilterSet ordering_fields = ['id'] ordering = ['id'] class ResourceViewSet(GenericViewSet): perms_map = {'*': '*'} @action(methods=['post'], detail=False, perms_map={'post':'resource_cal'}, serializer_class=ResourceCalListSerializer) def cal(self, request, pk=None): """ 物料消耗计算 """ rdata = request.data serializer = self.get_serializer(data=rdata) serializer.is_valid(raise_exception=True) productIdList = [] productList = [] for i in rdata: if i['id'] not in productIdList: productIdList.append(i['id']) productList.append(i) else: index = productIdList.index(i['id']) productList[index]['count'] = productList[index]['count'] + i['count'] res_d_list = [] res = [] for i in productList: # 计算输入物料 materials = SubprodctionMaterial.objects.filter(subproduction__product__id=i['id'], subproduction__is_deleted=False, is_deleted=False, type= SubprodctionMaterial.SUB_MA_TYPE_IN).order_by( 'material__type', 'material__number')\ .values('material__id', 'material__name', 'material__number', 'material__type', 'count', 'material__count', 'material__count_safe') l_m = list(materials) for m in l_m: if m['material__id'] in res_d_list: if m['material__type'] in [Material.MA_TYPE_MAINSO, Material.MA_TYPE_HELPSO]: index = res_d_list.index(m['material__id']) res[index]['count'] = res[index]['count'] + m['count']*i['count'] else: res_d_list.append(m['material__id']) item = {'id':m['material__id'], 'name':m['material__name'], 'type':m['material__type'], 'number':m['material__number'], 'count': None,'inv_count':m['material__count'], 'count_safe':m['material__count_safe']} if item['type'] in [Material.MA_TYPE_MAINSO, Material.MA_TYPE_HELPSO]: item['count'] = m['count']*i['count'] res.append(item) return Response(res) @action(methods=['post'], detail=False, perms_map={'post':'resource_cal'}, serializer_class=ResourceConvertListSerializer) def convert(self, request, pk=None): rdata = request.data serializer = self.get_serializer(data=rdata) serializer.is_valid(raise_exception=True) res_d_list = [] res = [] half_list = rdata while half_list: fitem = half_list[0] sm = SubprodctionMaterial.objects.filter( type= SubprodctionMaterial.SUB_MA_TYPE_OUT, is_deleted = False, material__id = fitem['id'] ).first() if sm: spn = sm.subproduction sm_left_l = list(SubprodctionMaterial.objects.filter( subproduction = spn, type= SubprodctionMaterial.SUB_MA_TYPE_IN, is_deleted = False, ).order_by('material__number')\ .values('material__id', 'material__name', 'material__number', 'material__type', 'count', 'material__count', 'material__count_safe')) for i in sm_left_l: if i['material__type'] == Material.MA_TYPE_HALFGOOD: item = { 'id':i['material__id'], 'count':(i['count']*fitem['count'])/(sm.count) } half_list.append(item) else: if i['material__id'] in res_d_list: index = res_d_list.index(i['material__id']) res[index]['count'] = res[index]['count'] + \ (i['count']*fitem['count'])/(sm.count) else: res_d_list.append(i['material__id']) item = { 'id': i['material__id'], 'count':(i['count']*fitem['count'])/(sm.count) } res.append(item) del(half_list[0]) return Response(res) @action(methods=['post'], detail=False, perms_map={'post':'resource_cal'}, serializer_class=ResourceCalListSerializer) def cal_equip(self, request, pk=None): """ 设备状态查看 """ rdata = request.data serializer = self.get_serializer(data=rdata) serializer.is_valid(raise_exception=True) rdata_l = [] for i in rdata: rdata_l.append(i['id']) subproductions = SubProduction.objects.filter(product__id__in=rdata_l, is_deleted=False) steps = Step.objects.filter(usedstep__is_deleted=False, usedstep__subproduction__in=subproductions) equips = Equipment.objects.filter(step_equips__in=steps, is_deleted=False).distinct() serializer = EquipmentSimpleSerializer(instance=equips, many=True) return Response(serializer.data)