from django.shortcuts import render
from rest_framework.generics import CreateAPIView, GenericAPIView
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin
from rest_framework.utils import serializer_helpers
from rest_framework.utils.field_mapping import get_relation_kwargs
from rest_framework.views import APIView
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from apps.inm.models import FIFO, FIFOItem, FIFOItemProduct, IProduct, WareHouse
from apps.inm.signals import update_inm
from apps.mtm.models import Material, RecordForm, RecordFormField, Step, SubprodctionMaterial, TechDoc
from apps.mtm.serializers import RecordFormDetailSerializer, SubprodctionMaterialListSerializer, TechDocListSerializer
from apps.pm.models import SubProductionPlan, SubProductionProgress
from apps.pm.serializers import SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer, SubProductionProgressSerializer
from apps.qm.models import TestRecord, TestRecordItem
from apps.qm.serializers import TestRecordDetailSerializer

from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
from rest_framework.decorators import action
from apps.wpm.filters import WMaterialFilterSet
from apps.wpm.models import OperationEquip, OperationWproduct, Pick, PickWproduct, WMaterial, WProduct, Operation, OperationMaterial, OperationRecord, OperationRecordItem

from apps.wpm.serializers import OperationEquipListSerializer, OperationEquipUpdateSerializer, OperationMaterialCreate1ListSerailizer, OperationMaterialCreate1Serailizer, OperationMaterialCreate2ListSerailizer, OperationMaterialCreate2Serailizer, OperationMaterialCreate3Serializer, OperationMaterialListSerializer, OperationRecordListSerializer, OperationRecordSubmitSerializer, OperationUpdateSerializer, OperationWproductListSerializer, OperationCreateSerializer, OperationDetailSerializer, OperationListSerializer, PickHalfSerializer, PickHalfsSerializer, PickSerializer, OperationInitSerializer, OperationSubmitSerializer, WMaterialListSerializer, WProductListSerializer, WplanPutInSerializer, WpmTestFormInitSerializer, WpmTestRecordCreateSerializer, WproductPutInSerializer, WproductPutInsSerializer
from rest_framework.response import Response
from django.db import transaction
from rest_framework import exceptions, serializers

from apps.wpm.services import WpmServies
from django.utils import timezone
from utils.tools import ranstr
from rest_framework import status
from django.db.models import Count
# Create your views here.
class WPlanViewSet(ListModelMixin, GenericViewSet):
    """
    车间生产计划
    """
    perms_map = {'*': '*'}
    queryset = SubProductionPlan.objects.select_related('process', 'workshop', 'subproduction', 'main_product').exclude(state=0)
    search_fields = []
    serializer_class = SubProductionPlanListSerializer
    filterset_fields = ['production_plan', 'process', 'state', 'main_product', 'workshop']
    ordering_fields = []
    ordering = ['-update_time']

    @action(methods=['post', 'get'], detail=True, perms_map={'post':'*', 'get':'*'}, serializer_class=PickHalfsSerializer)
    @transaction.atomic
    def pick_half(self, request, pk=None):
        """
        领半成品
        """
        sp = self.get_object()
        if request.method=='GET':
            """
            领半成品
            """
            spps = SubProductionProgress.objects.filter(type=SubprodctionMaterial.SUB_MA_TYPE_IN, 
                material__type=Material.MA_TYPE_HALFGOOD, subproduction_plan=sp).select_related('material')
            return Response(SubProductionProgressSerializer(instance=spps, many=True).data)
        elif request.method=='POST':
            serializer= PickHalfsSerializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            vdata = serializer.validated_data
            first_step = Step.objects.get(pk=sp.steps[0]['id'])
            # 创建领料记录
            pick = Pick()
            pick.subproduction_plan = sp
            pick.type = Pick.PICK_FROM_WPRODUCT
            pick.create_by = request.user
            pick.save()
            for i in vdata:
                if 'wproducts' in i and len(i['wproducts'])>0:
                    spp = i['id']
                    spp.count_pick = spp.count_pick + len(i['wproducts'])
                    # if spp.count_pick > spp.count:
                    #     raise exceptions.APIException('超过计划数')
                    spp.save()
                    wps = WProduct.objects.filter(pk__in=[x.id for x in i['wproducts']], act_state=WProduct.WPR_ACT_STATE_OK)
                    wps.update(step=first_step, 
                    act_state=WProduct.WPR_ACT_STATE_TORETEST, is_hidden=False, warehouse=None,
                    subproduction_plan=sp, update_by=request.user, update_time=timezone.now())
                    for i in wps:
                        pw = PickWproduct()
                        pw.pick =pick
                        pw.wproduct = i
                        pw.number = i.number
                        pw.material = i.material
                        pw.subproduction_plan = i.suproduction_plan
                        pw.save()
            sp.is_picked = True
            sp.save()
            
            return Response()
        

    @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=WplanPutInSerializer)
    @transaction.atomic
    def putin(self, request, pk=None):
        """
        半成品入库
        """
        serializer= WplanPutInSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        vdata = serializer.validated_data
        subplan = self.get_object()
        material = subplan.main_product
        batch = subplan.number
        warehouse = vdata['warehouse']
        wproducts = WProduct.objects.filter(subproduction_plan=subplan, 
            act_state=WProduct.WPR_ACT_STATE_OK, material=material, is_deleted=False)
        if wproducts.exists():
            # 创建入库记录
            remark = vdata.get('remark', '')
            fifo = FIFO.objects.create(type=FIFO.FIFO_TYPE_DO_IN,
                is_audited=True, auditor=request.user, inout_date=timezone.now(), create_by=request.user, remark=remark)
            # 创建入库明细
            fifoitem = FIFOItem()
            fifoitem.is_tested = True
            fifoitem.is_testok = True
            fifoitem.warehouse = warehouse 
            fifoitem.material = material
            fifoitem.count = wproducts.count()
            fifoitem.batch = batch
            fifoitem.fifo = fifo
            fifoitem.subproduction_plan = subplan
            fifoitem.save()
            # 创建入库明细半成品
            ips = []
            for i in wproducts:
                ip = {}
                ip['fifoitem'] = fifoitem
                ip['wproduct'] = i
                ip['number'] = i.number
                ip['material'] = material
                ips.append(FIFOItemProduct(**ip))
            FIFOItemProduct.objects.bulk_create(ips)
            # 创建IProduct
            ips2 = []
            for i in wproducts:
                ip = {}
                ip['warehouse'] = warehouse
                ip['batch'] = batch
                ip['wproduct'] = i
                ip['number'] = i.number
                ip['material'] = material
                ips2.append(IProduct(**ip))
            IProduct.objects.bulk_create(ips2)
            # 更新库存并修改半成品进行状态
            update_inm(fifo)
            wproducts.update(act_sate=WProduct.WPR_ACT_STATE_INM, warehouse=warehouse, update_by=request.user, update_time=timezone.now())
            
        return Response()


class WMaterialViewSet(CreateUpdateModelAMixin, ListModelMixin, GenericViewSet):
    """
    车间物料表
    """
    perms_map={'*':'*'}
    queryset = WMaterial.objects.select_related('material', 'subproduction_plan').filter(count__gt=0)
    serializer_class = WMaterialListSerializer
    filterset_class = WMaterialFilterSet
    ordering_fields = ['material__number']
    ordering = ['material__number']

    @action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=PickSerializer)
    def pick(self, request, pk=None):
        """
        领料
        """
        serializer= PickSerializer(data=request.data, context={'request': request})
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response()

class WProductViewSet(ListModelMixin, GenericViewSet):
    """
    半成品
    """
    perms_map={'*':'*'}
    queryset = WProduct.objects.select_related('step', 'material').filter(is_hidden=False)
    serializer_class = WProductListSerializer
    filterset_fields = ['step', 'subproduction_plan', 'material', 'step__process', 'act_state', 'material__type']
    search_fields = ['number']
    ordering_fields = ['id']
    ordering = ['id']

    @action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=WpmTestFormInitSerializer)
    def test_init(self, request, pk=None):
        """
        检验表单初始化
        """
        serializer = WpmTestFormInitSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        vdata = serializer.validated_data
        wproduct = vdata['wproduct']
        form = vdata['form']

        
        # 如果是复检记录, 需要带入原数据
        if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST:
            # 查找最近一条检验记录
            trs = TestRecord.objects.filter(wproduct=wproduct, type=TestRecord.TEST_PROCESS).order_by('-id').first()
            if trs:
                origin_test = TestRecordDetailSerializer(instance=trs).data
                data = RecordFormDetailSerializer(instance=form).data
                data['origin_test'] = origin_test
                o_dict = {}
                for i in origin_test['record_data_']:
                    o_dict[i['field_key']] = i['field_value']
                for i in data['form_fields']:
                    i['origin_value'] = o_dict[i['field_key']]
            else:
                raise exceptions.APIException('原工序检验记录不存在')
        else:
            data = RecordFormDetailSerializer(instance=form).data

        # 后续加入系统自带数据
        return Response(data)

    @action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=WpmTestRecordCreateSerializer)
    @transaction.atomic
    def test(self, request, pk=None):
        """
        检测
        """
        serializer = WpmTestRecordCreateSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        vdata = serializer.validated_data
        record_data = vdata.pop('record_data')
        wproduct = vdata['wproduct']
        if wproduct.act_state not in [WProduct.WPR_ACT_STATE_TOTEST, 
                                    WProduct.WPR_ACT_STATE_TORETEST, WProduct.WPR_ACT_STATE_TOFINALTEST, WProduct.WPR_ACT_STATE_TOCOMBTEST]:
            raise exceptions.APIException('该产品当前状态不可检验')
        if 'is_testok' not in vdata:
            raise exceptions.APIException('未填写检测结论')

        savedict = dict(create_by = self.request.user, 
        material=wproduct.material, number=wproduct.number, subproduction_plan=wproduct.subproduction_plan, step=wproduct.step)
        if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST:
            savedict['type'] = TestRecord.TEST_PROCESS_RE
        elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOFINALTEST:
            savedict['type'] = TestRecord.TEST_FINAL
        elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOCOMBTEST:
            savedict['type'] = TestRecord.TEST_COMB
        obj = serializer.save(**savedict)
        tris = []
        for m in record_data: # 保存记录详情
            form_field = m['form_field']
            m['field_name'] = form_field.field_name
            m['field_key'] = form_field.field_key
            m['field_type'] = form_field.field_type
            m['field_value'] = m['field_value']
            m['sort'] = form_field.sort
            m['need_judge'] = form_field.need_judge
            m['is_testok'] = m['is_testok'] if 'is_testok' in m else None
            m['test_record'] = obj
            tris.append(TestRecordItem(**m))
        TestRecordItem.objects.bulk_create(tris)

        # 如果检测合格, 变更动态产品进行状态

        if obj.is_testok:
            if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST: # 复检
                wproduct.act_state = WProduct.WPR_ACT_STATE_DOWAIT
            elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST and wproduct.material.type == Material.MA_TYPE_GOOD: # 成品检验
                wproduct.act_state = WProduct.WPR_ACT_STATE_TOFINALTEST 
            else:
                wproduct.act_state = WProduct.WPR_ACT_STATE_OK
            if wproduct.number is None: # 产生半成品编号
                wproduct.number = 'WP'+ranstr(7)
            # 更新子计划状态
            # 更新子计划主产品数
            instance = SubProductionProgress.objects.get(subproduction_plan=wproduct.subproduction_plan, 
            is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT)
            instance.count_ok = instance.count_ok + 1 # 这个地方可能会有问题
            instance.save()
        else:# 如果不合格
            wproduct.act_state = WProduct.WPR_ACT_STATE_NOTOK
            # 需要走不合格品审理单
        wproduct.update_by = request.user
        wproduct.save()
        return Response()

    @action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=WproductPutInsSerializer)
    @transaction.atomic
    def putins(self, request, pk=None):
        """
        半成品批量入库
        """
        serializer= WproductPutInsSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        vdata = serializer.validated_data
        wproducts = WProduct.objects.filter(pk__in=[x.id for x in vdata['wproducts']])
        warehouse = vdata['warehouse']
        for i in wproducts:
            if i.act_state is not WProduct.WPR_ACT_STATE_OK:
                raise exceptions.APIException('存在不可入库半成品')
        # 聚合一下
        wproducts_a = wproducts.values('subproduction_plan', 'material', 'subproduction_plan__number').annotate(total=Count('id'))
        # 创建入库记录
        remark = vdata.get('remark', '')
        fifo = FIFO.objects.create(type=FIFO.FIFO_TYPE_DO_IN,
            is_audited=True, auditor=request.user, inout_date=timezone.now(), create_by=request.user, remark=remark)
        # 创建入库明细
        for i in wproducts_a:
            spi = SubProductionPlan.objects.get(pk=i['subproduction_plan'])
            fifoitem = FIFOItem()
            fifoitem.is_tested = True
            fifoitem.is_testok = True
            fifoitem.warehouse = warehouse
            fifoitem.material = Material.objects.get(pk=i['material'])
            fifoitem.count = i['total']
            fifoitem.batch = spi.number
            fifoitem.fifo = fifo
            fifoitem.subproduction_plan = spi
            fifoitem.save()

            wproducts_items = wproducts.filter(subproduction_plan=i['subproduction_plan'], material=i['material'])
            ips = []
            for i in wproducts_items:       
                # 创建入库明细半成品
                ip = {}
                ip['fifoitem'] = fifoitem
                ip['wproduct'] = i
                ip['number'] = i.number
                ip['material'] = i.material
                ips.append(FIFOItemProduct(**ip))
            FIFOItemProduct.objects.bulk_create(ips)
        # 更新库存并修改半成品进行状态
        update_inm(fifo)
        wproducts.update(act_state=WProduct.WPR_ACT_STATE_INM, warehouse=warehouse, update_by=request.user, update_time=timezone.now())
        return Response()
    
    @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=WproductPutInSerializer)
    @transaction.atomic
    def putin(self, request, pk=None):
        """
        半成品入库
        """
        serializer= WproductPutInSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        vdata = serializer.validated_data
        wproduct = self.get_object()
        warehouse = vdata['warehouse']
        if wproduct.act_state != WProduct.WPR_ACT_STATE_OK:
            raise exceptions.APIException('半成品不可入库')
        material = wproduct.material
        batch = wproduct.subproduction_plan.number
        # 创建入库记录
        remark = vdata.get('remark', '')
        fifo = FIFO.objects.create(type=FIFO.FIFO_TYPE_DO_IN,
            is_audited=True, auditor=request.user, inout_date=timezone.now(), create_by=request.user, remark=remark)
        # 创建入库明细
        fifoitem = FIFOItem()
        fifoitem.is_tested = True
        fifoitem.is_testok = True
        fifoitem.warehouse = warehouse
        fifoitem.material = material
        fifoitem.count = 1 # 单个半成品入库
        fifoitem.batch = batch
        fifoitem.fifo = fifo
        fifoitem.subproduction_plan = wproduct.subproduction_plan
        fifoitem.save()
        # 创建入库明细半成品
        ips = []
        for i in [wproduct]:
            ip = {}
            ip['fifoitem'] = fifoitem
            ip['wproduct'] = i
            ip['number'] = i.number
            ip['material'] = material
            ips.append(FIFOItemProduct(**ip))
        FIFOItemProduct.objects.bulk_create(ips)
        # 更新库存并修改半成品进行状态
        update_inm(fifo)
        wproduct.act_state=WProduct.WPR_ACT_STATE_INM
        wproduct.warehouse=warehouse
        wproduct.save()
        return Response()

class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin, GenericViewSet):
    """
    生产操作记录
    """
    perms_map={'*':'*'}
    queryset = Operation.objects.select_related('step').prefetch_related('ow_operation', 'oe_operation', 'or_operation').all()
    serializer_class = OperationListSerializer
    filterset_fields = ['step', 'step__process', 'is_submited']
    ordering_fields = ['id']
    ordering = ['-id']

    def get_queryset(self):
        return self.queryset.filter(create_by=self.request.user)

    def get_serializer_class(self):
        if self.action == 'retrieve':
            return OperationDetailSerializer
        elif self.action == 'create':
            return OperationCreateSerializer
        elif self.action == 'update':
            return OperationUpdateSerializer
        return super().get_serializer_class()

    def update(self, request, *args, **kwargs):
        instance = self.get_object()
        if instance.is_submited:
            raise exceptions.APIException('该操作已提交')
        return super().update(request, *args, **kwargs)

    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        if instance.is_submited:
            raise exceptions.APIException('该操作已提交')
        # 恢复半成品可操作
        instance.wp_operation.all().update(act_state=WProduct.WPR_ACT_STATE_DOWAIT)
        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)

    @transaction.atomic
    def create(self, request, *args, **kwargs):
        data = request.data
        serializer = OperationCreateSerializer(data=data, context={'request':self.request})
        serializer.is_valid(raise_exception=True)
        vdata = serializer.validated_data #校验之后的数据
        step = vdata['step']
        op = Operation()
        op.step = step
        op.is_submited = False
        op.create_by = request.user
        op.save()
        splans = []
        # 创建操作所用半成品关联记录
        if 'wproducts' in vdata:
            owps = []
            WProduct.objects.filter(pk__in=[x.id for x in vdata['wproducts']]).update(operation=op, act_state=WProduct.WPR_ACT_STATE_DOING)
            splans = WpmServies.get_subplans_queryset_from_wproducts(vdata['wproducts'])
            for wpd in vdata['wproducts']:
                owp = {}
                owp['operation'] = op
                owp['wproduct'] = wpd
                owp['number'] = wpd.number
                owp['material'] = wpd.material
                owp['subproduction_plan'] = wpd.subproduction_plan
                owps.append(OperationWproduct(**owp))
            OperationWproduct.objects.bulk_create(owps)
        else:
            splans = WpmServies.get_subplans_queryset_from_wproducts(vdata['wproducts'])
        # 查询需要填写的自定义表格
        forms = RecordForm.objects.filter(step=step, type=RecordForm.RF_TYPE_DO)
        for i in forms:
            opr = OperationRecord()
            opr.operation = op
            opr.form = i
            opr.is_filled = False
            opr.save()
        # 查询需要使用的生产设备
        for i in step.equipments.all():
            ope = OperationEquip()
            ope.operation = op
            ope.equip = i
            ope.save()
        # 查询所需的工具工装
        for i in SubprodctionMaterial.objects.filter(type=SubprodctionMaterial.SUB_MA_TYPE_TOOL,
                        subproduction__subplan_subprod__in = splans, is_deleted=False).distinct():
            opm = OperationMaterial()
            opm.operation = op
            opm.material = i.material
            opm.type = SubprodctionMaterial.SUB_MA_TYPE_TOOL
            opm.save()
        return Response()
    
    @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=serializers.Serializer)
    @transaction.atomic
    def submit(self, request, pk=None):
        """
        提交车间操作重要
        """
        op = self.get_object()
        step = op.step
        # 检查自定义表单填写
        if OperationRecord.objects.filter(operation=op, is_filled=False).exists():
            raise exceptions.APIException('存在自定义表单未填写')
        # 更新物料消耗进度
        for i in OperationMaterial.objects.filter(operation=op, type=SubprodctionMaterial.SUB_MA_TYPE_IN):
            # 更新车间物料
            i_wmat = i.wmaterial
            i_wmat.count = i_wmat.count- i.count
            i_wmat.save()
            # 更新子计划物料消耗情况
            spp = SubProductionProgress.objects.get(subproduction_plan=i_wmat.subproduction_plan,
                    material=i_wmat.material)
            spp.count_real = spp.count_real + i.count
            spp.save()
        # 更新产出
        for i in OperationMaterial.objects.filter(operation=op, type=SubprodctionMaterial.SUB_MA_TYPE_OUT):
            if not i.subproduction_progress.is_main:
                # 更新车间物料产出情况
                ins, _ = WMaterial.objects.get_or_create(subproduction_plan=i.subproduction_plan, material=i.material)
                ins.count = ins.count + i.count
                ins.save()
                # 更新子计划物料产出情况
                spp = i.subproduction_progress
                spp.count_real = spp.count_real + i.count
                spp.save()
        # 更新动态产品表
        if step.type == Step.STEP_TYPE_NOM:
            for i in OperationWproduct.objects.filter(operation=op):
                wp = i.wproduct
                wsp = i.subproduction_plan
                # 获取下一步子工序
                newstep, hasNext = WpmServies.get_next_step(wsp, step)
                wp.step = newstep
                wp.pre_step = step
                if hasNext:
                    wp.act_state= WProduct.WPR_ACT_STATE_DOWAIT
                else:
                    wp.act_state = WProduct.WPR_ACT_STATE_TOTEST
                    wp.material = wsp.main_product
                    # 更新子计划进度
                    instance = SubProductionProgress.objects.get(subproduction_plan=wsp, 
                    is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT)
                    instance.count_real = instance.count_real + 1 # 这个地方可能会有问题,不够严谨
                    instance.save()
                wp.operation = None
                wp.update_by = request.user
                wp.save()
        elif step.type == Step.STEP_TYPE_DIV:
            # 更新物料产出情况
            for i in OperationMaterial.objects.filter(operation=op, type=SubprodctionMaterial.SUB_MA_TYPE_OUT):
                if i.subproduction_progress.is_main:
                    newstep, _ = WpmServies.get_next_step(i.subproduction_plan, step)
                    wpr = dict(material=i.material, step=newstep,
                        act_state=WProduct.WPR_ACT_STATE_DOWAIT, remark='', 
                        subproduction_plan=i.subproduction_plan)
                    for x in range(i.count):
                        WProduct.objects.create(**wpr)              
        elif step.type == Step.STEP_TYPE_COMB:
            oms_w = OperationMaterial.objects.filter(operation=op, type=SubprodctionMaterial.SUB_MA_TYPE_OUT, 
                subproduction_progress__ismain=True)
            if len(oms_w) == 1:
                oms_w = oms_w[0]
                # 校验单片数量是否正确, 暂时未写
                newstep, hasNext = WpmServies.get_next_step(oms_w.subproduction_plan, step)
                wproduct = WProduct()
                wproduct.material = oms_w.material
                wproduct.step = newstep
                wproduct.subproduction_plan = oms_w.subproduction_plan
                if hasNext:
                    wproduct.act_state = WProduct.WPR_ACT_STATE_DOWAIT
                else:
                    wproduct.act_state = WProduct.WPR_ACT_STATE_TOTEST
                    # 更新子计划进度
                    instance = oms_w.subproduction_progress
                    instance.count_real = instance.count_real + 1 # 这个地方可能会有问题,不够严谨
                    instance.save()
                wproduct.create_by = request.user
                wproduct.save()
                # 隐藏原半成品
                wps = WProduct.objects.filter(ow_wproduct__operation = op)
                wps.update(is_hidden=True, child=wproduct, update_by=request.user, update_time=timezone.now())
            else:
                raise exceptions.APIException('产出物料错误')
        op.is_submited = True
        op.save()
        return Response()
        


class OperationWproductViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
    """
    操作使用的半成品
    """
    perms_map={'*':'*'}
    queryset = OperationWproduct.objects.select_related('subproduction_plan', 'material').all()
    serializer_class = OperationWproductListSerializer
    filterset_fields = ['material', 'subproduction_plan', 'operation']
    ordering_fields = ['id']
    ordering = ['-id']

    @transaction.atomic()
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        if instance.operation.is_submited:
            raise exceptions.APIException('该操作已提交')
        instance.delete()
        wp = instance.wproduct
        wp.operation = None
        wp.save()
        return Response()

class OperationEquipViewSet(ListModelMixin, DestroyModelMixin, UpdateModelMixin, GenericViewSet):
    """
    操作使用的设备
    """
    perms_map={'*':'*'}
    queryset = OperationEquip.objects.select_related('operation', 'equip').all()
    serializer_class = OperationEquipListSerializer
    filterset_fields = ['operation', 'equip']
    ordering_fields = ['id']
    ordering = ['-id']

    def get_serializer_class(self):
        if self.action == 'update':
            return OperationEquipUpdateSerializer
        return super().get_serializer_class()
    
    def update(self, request, *args, **kwargs):
        instance = self.get_object()
        if instance.operation.is_submited:
            raise exceptions.APIException('该操作已提交')
        return super().update(request, *args, **kwargs)

    @transaction.atomic()
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        if instance.operation.is_submited:
            raise exceptions.APIException('该操作已提交')
        instance.delete()
        return Response()

class OperationRecordViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
    """
    操作使用的自定义表格
    """
    perms_map={'*':'*'}
    queryset = OperationRecord.objects.select_related('operation', 'form').all()
    serializer_class = OperationRecordListSerializer
    filterset_fields = ['operation', 'form']
    ordering_fields = ['id']
    ordering = ['-id']

    @transaction.atomic()
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        if instance.operation.is_submited:
            raise exceptions.APIException('该操作已提交')
        instance.delete()
        return Response()

    @action(methods=['get'], detail=True, perms_map={'get':'*'})
    def init(self, request, pk=None):
        '''
        表格初始化
        '''
        obj = self.get_object()
        data = RecordFormDetailSerializer(instance=obj.form).data
        # 后续加入系统带入数据
        return Response(data)

    @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=OperationRecordSubmitSerializer)
    def submit(self, request, pk=None):
        serializer = OperationRecordSubmitSerializer(data=request.data, context={'request':self.request})
        serializer.is_valid(raise_exception=True)
        vdata = serializer.validated_data
        opr = self.get_object()
        wrds = []
        for m in vdata['record_data']: # 保存记录详情
            form_field = m['form_field']
            m['form_field'] = form_field 
            m['field_name'] = form_field.field_name
            m['field_key'] = form_field.field_key
            m['field_type'] = form_field.field_type
            m['field_value'] = m['field_value']
            m['sort'] = form_field.sort
            m['operation_record'] = opr
            wrds.append(OperationRecordItem(**m))
        OperationRecordItem.objects.bulk_create(wrds)
        opr.is_filled = True
        opr.save()
        return Response()


class OperationMaterialInputViewSet(ListModelMixin, CreateModelMixin, DestroyModelMixin, GenericViewSet):
    """
    消耗物料
    """
    perms_map={'*':'*'}
    queryset = OperationMaterial.objects.select_related('operation', 'subproduction_plan').filter(type=SubprodctionMaterial.SUB_MA_TYPE_IN)
    serializer_class = OperationMaterialListSerializer
    filterset_fields = ['operation', 'subproduction_plan']
    ordering_fields = ['id']
    ordering = ['-id']

    def get_serializer_class(self):
        if self.action == 'create':
            return OperationMaterialCreate1Serailizer
        return super().get_serializer_class()
    
    @action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=OperationMaterialCreate1ListSerailizer)
    def creates(self, request, pk=None):
        """
        批量创建消耗物料
        """
        serializer = OperationMaterialCreate1ListSerailizer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response()


    @transaction.atomic()
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        if instance.operation.is_submited:
            raise exceptions.APIException('该操作已提交')
        instance.delete()
        return Response()

class OperationMaterialOutputViewSet(ListModelMixin, CreateModelMixin, DestroyModelMixin, GenericViewSet):
    """
    产出物料
    """
    perms_map={'*':'*'}
    queryset = OperationMaterial.objects.select_related('operation', 'subproduction_plan').filter(type=SubprodctionMaterial.SUB_MA_TYPE_OUT)
    serializer_class = OperationMaterialListSerializer
    filterset_fields = ['operation', 'subproduction_plan']
    ordering_fields = ['id']
    ordering = ['-id']

    def get_serializer_class(self):
        if self.action == 'create':
            return OperationMaterialCreate2Serailizer
        return super().get_serializer_class()
    
    @action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=OperationMaterialCreate2ListSerailizer)
    def creates(self, request, pk=None):
        """
        批量创建产出物料
        """
        serializer = OperationMaterialCreate2ListSerailizer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response()

    @transaction.atomic()
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        if instance.operation.is_submited:
            raise exceptions.APIException('该操作已提交')
        instance.delete()
        return Response()

class OperationMaterialToolViewSet(ListModelMixin, CreateModelMixin, DestroyModelMixin, GenericViewSet):
    """
    工具工装
    """
    perms_map={'*':'*'}
    queryset = OperationMaterial.objects.select_related('operation', 'subproduction_plan').filter(type=SubprodctionMaterial.SUB_MA_TYPE_TOOL)
    serializer_class = OperationMaterialListSerializer
    filterset_fields = ['operation', 'subproduction_plan']
    ordering_fields = ['id']
    ordering = ['-id']

    def get_serializer_class(self):
        if self.action == 'create':
            return OperationMaterialCreate3Serializer
        return super().get_serializer_class()
        
    @transaction.atomic()
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        if instance.operation.is_submited:
            raise exceptions.APIException('该操作已提交')
        instance.delete()
        return Response()

class DoFormInit(CreateAPIView, GenericAPIView):
    perms_map={'*':'*'}
    serializer_class=OperationInitSerializer
    def post(self, request, format=None):
        """
        调用操作表单
        """
        data = request.data
        serializer = OperationInitSerializer(data=data)
        serializer.is_valid(raise_exception=True)
        vdata = serializer.validated_data
        ret = {}
        ret_0 = {}
        ret_0['step'] = data['step']
        splans =[]
        ret_0['input'] = []
        # ret_0['subproduction_plan'] = data['subproduction_plan']
        if 'wproducts' in data and data['wproducts']:
            ret_0['wproducts'] = data['wproducts']
            splans = WProduct.objects.filter(id__in=data['wproducts']).values_list('subproduction_plan', flat=True)
            # 调出所属子计划现有物料
            ret_0['input'] = WMaterialListSerializer(instance=WMaterial.objects.filter(subproduction_plan__in=splans), many=True).data
        else:
            if 'subproduction_plan' in vdata:
                splans = [vdata['subproduction_plan']]
            else:
                splans = SubProductionPlan.objects.filter(is_deleted=False, 
                subproduction__usedstep_subproduction__step=vdata['step'], state=3)
            ret_0['wproducts'] = []
            ret_0['input'] = WMaterialListSerializer(instance=WMaterial.objects.filter(subproduction_plan__in=splans), many=True).data

        
        for i in ret_0['input']:
            i['count_input'] = 0
        # 需要输出的物料
        if ret_0['wproducts']:
            # 排除主要产物, 因为已经放到半成品里了, 由半成品进行处理, 夹层可能需要特殊处理
            o_objs = SubProductionProgress.objects.filter(
                subproduction_plan__in=splans, type=SubprodctionMaterial.SUB_MA_TYPE_OUT).exclude(is_main=True)
            
        else:
            # 此时显示所有子计划需要输出的物料
            o_objs = SubProductionProgress.objects.filter(
                subproduction_plan__in=splans, type=SubprodctionMaterial.SUB_MA_TYPE_OUT)
        ret_0['output'] = list(o_objs.values('subproduction_plan', 'material', 'material__name', 'material__number'))
        for i in ret_0['output']:
            i['count_output']=0
        ret['forms'] = []
        ret_0['id'] = 0
        ret_0['name'] = '基本信息'
        # 查询工具工装
        ret_0['tools'] = SubprodctionMaterialListSerializer(instance=
                        SubprodctionMaterial.objects.filter(type=SubprodctionMaterial.SUB_MA_TYPE_TOOL,
                        subproduction__subplan_subprod__in = splans), many=True).data
        # 查询技术文档
        ret_0['techdocs'] = TechDocListSerializer(instance = 
                TechDoc.objects.filter(subproduction__subplan_subprod__in = splans, enabled=True)\
            .distinct(), many=True).data

        ret['forms'].append(ret_0)
        forms = RecordForm.objects.filter(step=vdata['step'], type=RecordForm.RF_TYPE_DO)
        if forms.exists():
            ret['forms'].extend(RecordFormDetailSerializer(instance=forms, many=True).data) 
        return Response(ret)


class DoFormSubmit(CreateAPIView, GenericAPIView):
    perms_map={'*':'*'}
    serializer_class = OperationSubmitSerializer

    @transaction.atomic
    def post(self, request, format=None):
        """
        提交操作表单
        """
        data = request.data
        serializer = OperationSubmitSerializer(data=data, context={'request':self.request})
        serializer.is_valid(raise_exception=True)
        vdata = serializer.validated_data #校验之后的数据

        # 创建一个生产操作记录
        action_obj = Operation()
        action_obj.step = vdata['step']
        action_obj.remark = vdata.get('remark', '') # 操作备注
        action_obj.create_by = request.user
        action_obj.use_scrap = vdata.get('use_scrap', False)
        action_obj.save()
        
        # 保存关联半成品
        if 'wproducts' in data and data['wproducts']:
            owps = []
            for i in data['wproducts']:
                owp = {}
                owp['operation'] = action_obj
                wp = WProduct.objects.get(pk=i)
                owp['wproduct'] = wp
                owp['number'] = wp.number
                owp['material'] = wp.material
                owp['subproduction_plan'] = wp.subproduction_plan
                owps.append(OperationWproduct(**owp))
            OperationWproduct.objects.bulk_create(owps)

        # 保存物料消耗
        for i in vdata['input']:
            if i['count_input']: #如果有消耗
                i_wmat = i['id']
                OperationMaterial.objects.create(type=1, operation=action_obj,
                    wmaterial= i_wmat, count=i['count_input'])
                # 更新车间物料
                i_wmat.count = i_wmat.count- i['count_input']
                i_wmat.save()
                # 更新子计划物料消耗情况
                sp = SubProductionProgress.objects.get(subproduction_plan=i_wmat.subproduction_plan,
                    material=i_wmat.material)
                sp.count_real = sp.count_real + i['count_input']
                sp.save()
        
        # 物料产出
        if 'output' in data and data['output']:
            for i in vdata['output']: # 已经序列化好的数据
                ma = i['material']
                if i['subproduction_plan'].main_product == ma: # 如果是该计划主产物
                    # 如果是切割
                    # 获取下一步子工序
                    if vdata['step'].type == Step.STEP_TYPE_DIV:
                        newstep, _ = WpmServies.get_next_step(i['subproduction_plan'], vdata['step'])
                        wpr = dict(material=ma, step=newstep,
                            act_state=WProduct.WPR_ACT_STATE_DOWAIT, remark='', 
                            subproduction_plan=i['subproduction_plan'])
                        for x in range(i['count_output']):
                            WProduct.objects.create(**wpr)
                else:
                    # 更新操作产出物料表
                    OperationMaterial.objects.create(type=2, operation=action_obj,
                    material= ma, count=i['count_output'])
                    # 更新车间物料表
                    ins, _ = WMaterial.objects.get_or_create(subproduction_plan=i['subproduction_plan'],
                        material=ma)
                    ins.count = ins.count + i['count_output']
                    ins.save()
                    # 更新子计划进度表
                    sp = SubProductionProgress.objects.get(subproduction_plan=i['subproduction_plan'],
                    material=ma)
                    sp.count_real = sp.count_real + i['count_input']
                    sp.save()

        # 更新动态产品表
        if 'wproducts' in vdata and vdata['wproducts']:
            if vdata['step'].type == Step.STEP_TYPE_COMB:
                wproducts = vdata['wproducts']
                if 'suproduction_plan' in vdata:
                    wproducts.update(is_hidden=True) # 隐藏
                    newstep, hasNext = WpmServies.get_next_step(i['subproduction_plan'], vdata['step'])
                    wproduct = WProduct()
                    wproduct.material = vdata['subproduction_plan'].main_product
                    wproduct.step = newstep
                    wproduct.subproduction_plan=vdata['subproduction_plan']
                    wproduct.parent = data['wproducts']
                    if hasNext:
                        wproduct.act_state=WProduct.WPR_ACT_STATE_DOWAIT
                    else:
                        wproduct.act_state=WProduct.WPR_ACT_STATE_TOTEST
                    wproduct.save()
                else:
                    raise exceptions.APIException('请指定子计划')
            else:
                for wproduct in vdata['wproducts']:
                    # 获取下一步子工序
                    newstep, hasNext = WpmServies.get_next_step(wproduct.subproduction_plan, vdata['step'])
                    wproduct.step = newstep
                    wproduct.pre_step=vdata['step']
                    if hasNext:
                        wproduct.act_state=WProduct.WPR_ACT_STATE_DOWAIT
                    else:
                        wproduct.act_state=WProduct.WPR_ACT_STATE_TOTEST
                        wproduct.material=wproduct.subproduction_plan.main_product
                    wproduct.save()

        # 保存自定义表单结果
        for i in vdata['forms']:
            wr = OperationRecord()
            wr.form = i['form']
            wr.create_by = request.user
            wr.operation = action_obj
            wr.save()
            wrds = []
            for m in i['record_data']: # 保存记录详情
                form_field = m['form_field']
                m['field_name'] = form_field.field_name
                m['field_key'] = form_field.field_key
                m['field_type'] = form_field.field_type
                m['field_value'] = m['field_value']
                m['sort'] = form_field.sort
                m['operation_record'] = wr
                wrds.append(OperationRecordItem(**m))
            OperationRecordItem.objects.bulk_create(wrds)
        return Response()

        