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)
|
|
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) |