429 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			429 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Python
		
	
	
	
| 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, number='JH'+ranstr(7))
 | |
|             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':'subplan_update'}
 | |
|     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':'subplan_issue'}, 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':'subplan_start'}, 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) |