1020 lines
43 KiB
Python
1020 lines
43 KiB
Python
from rest_framework.generics import CreateAPIView, GenericAPIView
|
|
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, \
|
|
ListModelMixin, RetrieveModelMixin, UpdateModelMixin
|
|
from rest_framework.viewsets import GenericViewSet
|
|
from apps.inm.models import FIFO, FIFOItem, FIFOItemProduct
|
|
from apps.inm.services import InmService
|
|
from apps.mtm.models import Material, RecordForm, RecordFormField, Step, SubprodctionMaterial, TechDoc
|
|
from apps.mtm.serializers import RecordFormDetailSerializer, SubprodctionMaterialListSerializer, TechDocListSerializer
|
|
from apps.pm.models import ProductionPlan, SubProductionPlan, SubProductionProgress
|
|
from apps.pm.serializers import SubProductionPlanListSerializer, SubProductionProgressSerializer
|
|
from apps.qm.models import TestRecord, TestRecordItem
|
|
from apps.qm.serializers import TestRecordDetailSerializer
|
|
|
|
from apps.system.mixins import CreateUpdateCustomMixin, CreateUpdateModelAMixin
|
|
from rest_framework.decorators import action
|
|
from apps.wf.models import Workflow
|
|
from apps.wpm.filters import CuttingFilterSet, OperationRecordFilterSet, WMaterialFilterSet, WProductFilterSet
|
|
from apps.wpm.models import OperationEquip, OperationWproduct, Pick, PickWproduct, WMaterial, WProduct, Operation, \
|
|
OperationMaterial, OperationRecord, OperationRecordItem, WproductFlow, WprouctTicket
|
|
|
|
from apps.wpm.serializers import CuttingListSerializer, OperationEquipListSerializer, OperationEquipUpdateSerializer, \
|
|
OperationMaterialCreate1ListSerailizer, OperationMaterialCreate1Serailizer, OperationMaterialCreate2ListSerailizer, \
|
|
OperationMaterialCreate2Serailizer, OperationMaterialCreate3Serializer, OperationMaterialListSerializer, \
|
|
OperationRecordDetailSerializer, OperationRecordListSerializer, OperationRecordSubmitSerializer, \
|
|
OperationUpdateSerializer, OperationWproductListSerializer, OperationCreateSerializer, OperationDetailSerializer, \
|
|
OperationListSerializer, OperationWproductUpdateSerializer, PickHalfsSerializer, \
|
|
PickSerializer, OperationInitSerializer, OperationSubmitSerializer, ScrapSerializer, WMaterialListSerializer, \
|
|
WProductCardSerializer, WProductDetailSerializer, WProductListSerializer, \
|
|
WpmTestFormInitSerializer, WproductMtestSerializer, WproductNeedToOrderSerializer, WproductPutInSerializer, \
|
|
WproductPutInsSerializer, WproductTicketListSerializer, WproductToOrderSerializer
|
|
|
|
from rest_framework.response import Response
|
|
from django.db import transaction
|
|
from rest_framework import exceptions, serializers
|
|
from apps.wpm.services import WpmService
|
|
from django.utils import timezone
|
|
from rest_framework import status
|
|
from django.db.models import Count, Q
|
|
|
|
from utils.tools import ranstr
|
|
|
|
|
|
# Create your views here.
|
|
|
|
|
|
class WPlanViewSet(ListModelMixin, GenericViewSet):
|
|
"""
|
|
车间生产计划
|
|
"""
|
|
perms_map = {'get': '*'}
|
|
queryset = SubProductionPlan.objects.select_related(
|
|
'process', 'workshop', 'subproduction', 'product').filter(
|
|
production_plan__state__in =[
|
|
ProductionPlan.PLAN_STATE_WORKING, ProductionPlan.PLAN_STATE_ASSGINED
|
|
]
|
|
)
|
|
search_fields = []
|
|
serializer_class = SubProductionPlanListSerializer
|
|
filterset_fields = ['production_plan',
|
|
'process', 'state', 'product', 'workshop']
|
|
ordering_fields = []
|
|
ordering = ['-update_time']
|
|
|
|
|
|
@action(methods=['post', 'get'], detail=True, perms_map={'post': 'pick_half', '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 m in i['wproducts']:
|
|
m.step = first_step
|
|
m.act_state = WProduct.WPR_ACT_STATE_TORETEST
|
|
m.is_hidden = False
|
|
m.warehouse = None
|
|
m.subproduction_plan = sp
|
|
m.update_by = request.user
|
|
m.update_time = timezone.now()
|
|
m.save()
|
|
WpmService.add_wproduct_flow_log(
|
|
instance=m, change_str='pick_half')
|
|
pw = PickWproduct()
|
|
pw.pick = pick
|
|
pw.wproduct = m
|
|
pw.number = m.number
|
|
pw.material = m.material
|
|
pw.subproduction_plan = m.subproduction_plan
|
|
pw.save()
|
|
sp.is_picked = True
|
|
sp.save()
|
|
if sp.production_plan.state in \
|
|
[ProductionPlan.PLAN_STATE_ASSGINED, ProductionPlan.PLAN_STATE_ACCEPTED]:
|
|
sp.production_plan.state = ProductionPlan.PLAN_STATE_WORKING
|
|
sp.production_plan.save()
|
|
|
|
return Response()
|
|
|
|
|
|
class WMaterialViewSet(CreateUpdateModelAMixin, ListModelMixin, GenericViewSet):
|
|
"""
|
|
车间物料表
|
|
"""
|
|
perms_map = {'get': '*'}
|
|
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': 'wmaterial_pick'}, 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, RetrieveModelMixin, GenericViewSet):
|
|
"""
|
|
半成品
|
|
"""
|
|
perms_map = {'get': '*'}
|
|
queryset = WProduct.objects.select_related('step', 'material',
|
|
'subproduction_plan', 'warehouse', 'subproduction_plan__production_plan__order',
|
|
'to_order').prefetch_related('wp_child')
|
|
serializer_class = WProductListSerializer
|
|
filterset_class = WProductFilterSet
|
|
search_fields = ['number', 'material__name', 'subproduction_plan__number']
|
|
ordering_fields = ['id']
|
|
ordering = ['id']
|
|
|
|
def get_serializer_class(self):
|
|
if self.action == 'retrieve':
|
|
return WProductDetailSerializer
|
|
return super().get_serializer_class()
|
|
|
|
def get_queryset(self):
|
|
queryset = self.queryset
|
|
if self.request.query_params.get('tag', None) != 'show_hidden':
|
|
queryset = queryset.filter(is_hidden=False)
|
|
return queryset
|
|
|
|
@action(methods=['post'], detail=False, perms_map={'post': 'wp_test_init'}, serializer_class=WpmTestFormInitSerializer)
|
|
@transaction.atomic
|
|
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.test:
|
|
raise exceptions.APIException('存在进行中检验')
|
|
|
|
# 根据情况创建一条检验记录
|
|
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('该产品当前状态不可检验')
|
|
savedict = dict(
|
|
create_by=request.user,
|
|
wproduct=wproduct,
|
|
material=wproduct.material,
|
|
number=wproduct.number,
|
|
subproduction_plan=wproduct.subproduction_plan,
|
|
step=wproduct.step,
|
|
form=form)
|
|
if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST:
|
|
# 查找最近一条检验记录
|
|
trs = wproduct.last_process_test
|
|
savedict['origin_test'] = trs
|
|
if not trs:
|
|
raise exceptions.APIException('原工序检验记录不存在')
|
|
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
|
|
elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST and\
|
|
wproduct.step != wproduct.pre_step: # 如果是工序内检验
|
|
savedict['is_midtesting'] = True
|
|
tr = TestRecord.objects.create(**savedict)
|
|
# 更新wproduct
|
|
wproduct.test = tr
|
|
wproduct.update_by = request.user
|
|
wproduct.update_time = timezone.now()
|
|
wproduct.save()
|
|
WpmService.add_wproduct_flow_log(wproduct, 'test_init')
|
|
# 创建检验条目
|
|
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()
|
|
return Response(TestRecordDetailSerializer(instance=tr).data)
|
|
|
|
|
|
@action(methods=['post'], detail=False, perms_map={'post': 'wp_putins'}, 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, number='RK'+ranstr(7))
|
|
# 创建入库明细
|
|
for i in wproducts_a:
|
|
spi = SubProductionPlan.objects.get(pk=i['subproduction_plan'])
|
|
fifoitem = FIFOItem()
|
|
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
|
|
if i.number is None:
|
|
raise exceptions.APIException('缺少编号')
|
|
ip['number'] = i.number
|
|
ip['material'] = i.material
|
|
ips.append(FIFOItemProduct(**ip))
|
|
FIFOItemProduct.objects.bulk_create(ips)
|
|
# 更新库存并修改半成品进行状态
|
|
InmService.update_inm(fifo)
|
|
for i in wproducts:
|
|
i.act_state = WProduct.WPR_ACT_STATE_INM
|
|
i.warehouse = warehouse
|
|
i.update_by = request.user
|
|
i.update_time = timezone.now()
|
|
i.save()
|
|
WpmService.add_wproduct_flow_log(i, 'putins')
|
|
return Response()
|
|
|
|
@action(methods=['post'], detail=True, perms_map={'post': 'wp_putins'}, 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, number='RK'+ranstr(7))
|
|
# 创建入库明细
|
|
fifoitem = FIFOItem()
|
|
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
|
|
if i.number is None:
|
|
raise exceptions.APIException('缺少编号')
|
|
ip['number'] = i.number
|
|
ip['material'] = material
|
|
ips.append(FIFOItemProduct(**ip))
|
|
FIFOItemProduct.objects.bulk_create(ips)
|
|
# 更新库存并修改半成品进行状态
|
|
InmService.update_inm(fifo)
|
|
wproduct.act_state = WProduct.WPR_ACT_STATE_INM
|
|
wproduct.warehouse = warehouse
|
|
wproduct.save()
|
|
WpmService.add_wproduct_flow_log(wproduct, 'putin')
|
|
return Response()
|
|
|
|
@action(methods=['post'], detail=True, perms_map={'post': 'wp_scrap'}, serializer_class=ScrapSerializer)
|
|
def scrap(self, request, pk=None):
|
|
"""
|
|
报废操作
|
|
"""
|
|
obj = self.get_object()
|
|
serializer = ScrapSerializer(data=request.data)
|
|
serializer.is_valid(raise_exception=True)
|
|
vdata = serializer.validated_data
|
|
if obj.ticket is not None:
|
|
raise exceptions.APIException('该产品存在进行工单')
|
|
if obj.act_state == WProduct.WPR_ACT_STATE_NOTOK:
|
|
pass
|
|
elif obj.step.process.id == 1 and \
|
|
obj.act_state in [WProduct.WPR_ACT_STATE_DOWAIT, WProduct.WPR_ACT_STATE_TOTEST]: # 如果是冷加工可直接报废
|
|
if vdata.get('scrap_reason', None):
|
|
obj.scrap_reason = vdata['scrap_reason']
|
|
else:
|
|
raise exceptions.APIException('请填写报废原因')
|
|
else:
|
|
raise exceptions.APIException('该产品不可报废')
|
|
obj.act_state = WProduct.WPR_ACT_STATE_SCRAP
|
|
obj.update_by = request.user
|
|
obj.update_time = timezone.now()
|
|
obj.save()
|
|
WpmService.add_wproduct_flow_log(obj, 'scrap')
|
|
if obj.step.process.id == 1: # 如果是冷加工
|
|
WpmService.update_cutting_list_with_operation(obj.coperation)
|
|
return Response()
|
|
|
|
# @action(methods=['get'], detail=False, perms_map={'get':'*'})
|
|
# def workflows(self, request, pk=None):
|
|
# """
|
|
# 可发起的工作流
|
|
# """
|
|
# wfs = Workflow.objects.filter(key__startswith= 'wp_')
|
|
# return WorkflowSimpleSerializer(instance=wfs, many=True).data
|
|
|
|
@action(methods=['get'], detail=True, perms_map={'get': '*'})
|
|
def wf_bhg(self, request, pk=None):
|
|
"""
|
|
发起不合格审理单
|
|
"""
|
|
obj = self.get_object()
|
|
if obj.act_state != WProduct.WPR_ACT_STATE_NOTOK or obj.ng_sign is not None \
|
|
or obj.ticket is not None:
|
|
raise exceptions.APIException('该产品不可发起不合格审理')
|
|
workflow = Workflow.objects.filter(
|
|
name='不合格品审理单', is_deleted=False).first()
|
|
if workflow:
|
|
test_record = TestRecord.objects.filter(
|
|
wproduct=obj, is_deleted=False, is_testok=False).order_by('-id').first()
|
|
exist_data = {
|
|
'wproduct': obj.id,
|
|
'wproduct_number': obj.number,
|
|
'wproduct_name': obj.material.name,
|
|
'wproduct_specification': obj.material.specification,
|
|
'finder': request.user.id,
|
|
'find_process': obj.step.process.id,
|
|
'tester': test_record.create_by.id
|
|
}
|
|
ret = {'workflow': workflow.id}
|
|
ret['exist_data'] = exist_data
|
|
return Response(ret)
|
|
else:
|
|
raise exceptions.APIException('未找到对应审批流程')
|
|
|
|
@action(methods=['post'], detail=True, perms_map={'post': 'wp_mtest'}, serializer_class=WproductMtestSerializer)
|
|
@transaction.atomic
|
|
def mtest(self, request, pk=None):
|
|
"""
|
|
军检
|
|
"""
|
|
obj = self.get_object()
|
|
if obj.is_mtestok is not None:
|
|
raise exceptions.APIException('已进行军检')
|
|
if obj.material.type != Material.MA_TYPE_GOOD:
|
|
raise exceptions.APIException('军检必须是成品')
|
|
obj.remark_mtest = request.data.get('remark_mtest', None)
|
|
is_mtestok = request.data.get('is_mtestok')
|
|
obj.is_mtestok = is_mtestok
|
|
if is_mtestok:
|
|
WpmService.update_plan_state_by_mtestok(
|
|
obj.subproduction_plan.production_plan)
|
|
obj.update_by = request.user
|
|
obj.save()
|
|
change_str = 'mtest_notok'
|
|
if is_mtestok:
|
|
change_str = 'mtest_ok'
|
|
WpmService.add_wproduct_flow_log(instance=obj, change_str=change_str)
|
|
return Response()
|
|
|
|
@action(methods=['get'], detail=True, perms_map={'get': 'wp_card'})
|
|
def card(self, request, pk=None):
|
|
"""
|
|
流程卡
|
|
"""
|
|
obj = self.get_object()
|
|
card_data = WProductCardSerializer(instance=obj).data
|
|
ret = []
|
|
if card_data['parents']:
|
|
line1 = ['序号', '工序']
|
|
for i in card_data['parents']:
|
|
line1.append(i['number'])
|
|
ret.append(line1)
|
|
steps_list_full_1 = card_data['parents'][0]['step_list_full']
|
|
for index, item in enumerate(steps_list_full_1):
|
|
linex = [str(index + 1), item['step_name']]
|
|
for i in card_data['parents']:
|
|
linex.append(i['step_list_full'][index]['actions'])
|
|
ret.append(linex)
|
|
ret.append(['序号', '工序', card_data['number']])
|
|
step_list_full = card_data['step_list_full']
|
|
for index, item in enumerate(step_list_full):
|
|
ret.append([str(index + 1), item['step_name'], item['actions']])
|
|
return Response(ret)
|
|
|
|
@action(methods=['post'], detail=False, perms_map={'post': 'wp_need_to_order'}, serializer_class=WproductNeedToOrderSerializer)
|
|
@transaction.atomic
|
|
def need_to_order(self, request, pk=None):
|
|
"""
|
|
设为需要指派订单
|
|
"""
|
|
serializer = WproductNeedToOrderSerializer(data=request.data)
|
|
serializer.is_valid(raise_exception=True)
|
|
vdata = serializer.validated_data
|
|
wps = WProduct.objects.filter(id__in = [i.id for i in vdata.get('wproducts')])
|
|
wps.update()
|
|
for i in wps:
|
|
i.need_to_order = True
|
|
i.update_by = request.user
|
|
i.save()
|
|
WpmService.add_wproduct_flow_log(i, change_str='need_to_order')
|
|
return Response()
|
|
|
|
@action(methods=['post'], detail=False, perms_map={'post': 'wp_to_order'}, serializer_class=WproductToOrderSerializer)
|
|
@transaction.atomic
|
|
def to_order(self, request, pk=None):
|
|
"""
|
|
指派发货订单
|
|
"""
|
|
serializer = WproductToOrderSerializer(data=request.data)
|
|
serializer.is_valid(raise_exception=True)
|
|
vdata = serializer.validated_data
|
|
wps = WProduct.objects.filter(id__in = [i.id for i in vdata.get('wproducts')])
|
|
wp = wps.first()
|
|
order = vdata['order']
|
|
if wp.material != order.product:
|
|
raise exceptions.ValidationError('所选订单与产品不符')
|
|
for i in wps:
|
|
if i.material != wp.material and i.material.type != Material.MA_TYPE_GOOD and i.act_state \
|
|
not in [WProduct.WPR_ACT_STATE_OK, WProduct.WPR_ACT_STATE_INM]:
|
|
raise exceptions.ValidationError('所选产品错误')
|
|
for i in wps:
|
|
i.to_order = order
|
|
i.update_by = request.user
|
|
i.save()
|
|
WpmService.add_wproduct_flow_log(i,change_str='to_order')
|
|
return Response()
|
|
|
|
class WproductTicketViewSet(ListModelMixin, GenericViewSet):
|
|
"""
|
|
玻璃审批工单
|
|
"""
|
|
perms_map = {'get': '*'}
|
|
queryset = WprouctTicket.objects.select_related('step', 'material', 'subproduction_plan',
|
|
'resp_process', 'subproduction_plan__production_plan__order',
|
|
'subproduction_plan__production_plan',
|
|
'ticket__workflow')
|
|
serializer_class = WproductTicketListSerializer
|
|
filterset_fields = ['step', 'material',
|
|
'subproduction_plan', 'resp_process']
|
|
ordering_fields = ['id']
|
|
ordering = ['-id']
|
|
|
|
|
|
class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin,
|
|
GenericViewSet):
|
|
"""
|
|
生产操作记录
|
|
"""
|
|
perms_map = {'get': '*', 'post':'operation_create',
|
|
'put':'operation_update', 'delete':'operation_delete'}
|
|
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_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('该操作已提交')
|
|
# 恢复半成品可操作
|
|
for i in instance.wp_operation.all():
|
|
i.act_state = WProduct.WPR_ACT_STATE_DOWAIT
|
|
i.update_by = request.user
|
|
i.save()
|
|
WpmService.add_wproduct_flow_log(i, 'operation_delete')
|
|
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 = []
|
|
splans = WpmService.get_subplans_queryset_from_wproducts(
|
|
vdata['wproducts'])
|
|
for wpd in vdata['wproducts']:
|
|
wpd.operation = op
|
|
wpd.act_state = WProduct.WPR_ACT_STATE_DOING
|
|
wpd.update_by = request.user
|
|
wpd.save()
|
|
WpmService.add_wproduct_flow_log(wpd, 'operation_create')
|
|
owp = {}
|
|
owp['operation'] = op
|
|
owp['wproduct'] = wpd
|
|
owp['number'] = wpd.number
|
|
owp['material'] = wpd.material
|
|
owp['subproduction_plan'] = wpd.subproduction_plan
|
|
owp['ng_sign'] = wpd.ng_sign
|
|
owps.append(OperationWproduct(**owp))
|
|
OperationWproduct.objects.bulk_create(owps)
|
|
else:
|
|
splans = WpmService.get_subplans_queryset_from_wproducts(
|
|
vdata['wproducts'])
|
|
# 查询需要填写的自定义表格
|
|
forms = RecordForm.objects.filter(
|
|
step=step, type=RecordForm.RF_TYPE_DO, enabled=True)
|
|
for i in forms:
|
|
opr = OperationRecord()
|
|
opr.operation = op
|
|
opr.form = i
|
|
opr.is_filled = False
|
|
opr.save()
|
|
opri_list = []
|
|
for m in RecordFormField.objects.filter(form=i, is_deleted=False):
|
|
opri_dict = {}
|
|
opri_dict['operation_record'] = opr
|
|
opri_dict['form_field'] = m
|
|
opri_list.append(OperationRecordItem(**opri_dict))
|
|
OperationRecordItem.objects.bulk_create(opri_list)
|
|
# 查询需要使用的生产设备
|
|
for i in step.equipments.all():
|
|
ope = OperationEquip()
|
|
ope.operation = op
|
|
ope.equip = i
|
|
ope.state = i.state
|
|
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': 'operation_submit'}, serializer_class=serializers.Serializer)
|
|
@transaction.atomic
|
|
def submit(self, request, pk=None):
|
|
"""
|
|
提交车间操作重要
|
|
"""
|
|
op = self.get_object()
|
|
step = op.step
|
|
processId = op.step.process.id
|
|
if op.is_submited:
|
|
raise exceptions.APIException('该操作已提交')
|
|
omis = OperationMaterial.objects.filter(operation=op,
|
|
type=SubprodctionMaterial.SUB_MA_TYPE_IN)
|
|
omos = OperationMaterial.objects.filter(operation=op,
|
|
type=SubprodctionMaterial.SUB_MA_TYPE_OUT)
|
|
# 校验消耗产出是否正确填写
|
|
if op.step.type == Step.STEP_TYPE_DIV:
|
|
sps_omi_l = omis.values_list('subproduction_plan', flat=True)
|
|
sps_omo_l = omos.filter(use_scrap=False).values_list(
|
|
'subproduction_plan', flat=True)
|
|
if set(list(sps_omi_l)) != set(list(sps_omo_l)):
|
|
raise exceptions.APIException('消耗与产出不一致')
|
|
else:
|
|
pass
|
|
# if omis.exists() and processId !=1: # 不是冷加工
|
|
# raise exceptions.APIException('请选择消耗物料')
|
|
|
|
# 检查自定义表单填写
|
|
if OperationRecord.objects.filter(operation=op, is_filled=False).exists():
|
|
raise exceptions.APIException('存在自定义表单未填写')
|
|
# 更新物料消耗进度
|
|
for i in omis:
|
|
# 更新车间物料
|
|
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 omos:
|
|
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:
|
|
ows = OperationWproduct.objects.filter(operation=op)
|
|
for i in ows:
|
|
wp = i.wproduct
|
|
wsp = i.subproduction_plan
|
|
|
|
# 获取下一步子工序
|
|
newstep, needTest = WpmService.get_next_step(wsp, step)
|
|
wp.step = newstep
|
|
wp.pre_step = step
|
|
wp.material = wsp.product
|
|
|
|
if step == newstep:
|
|
wp.act_state = WProduct.WPR_ACT_STATE_TOTEST
|
|
if wp.test:# 如果有正在进行的工序中检验
|
|
wp.test.is_midtesting = False
|
|
wp.test.is_submited = False
|
|
wp.test.save()
|
|
else:
|
|
wp.act_state = WProduct.WPR_ACT_STATE_DOWAIT
|
|
if needTest:
|
|
wp.act_state = WProduct.WPR_ACT_STATE_TOTEST
|
|
if wp.test:# 如果有正在进行的工序中检验
|
|
wp.test.is_submited = False
|
|
wp.test.save()
|
|
|
|
wp.operation = None
|
|
wp.update_by = request.user
|
|
wp.save()
|
|
WpmService.add_wproduct_flow_log(wp, 'operation_submit')
|
|
|
|
for i in ows.values('subproduction_plan').distinct():
|
|
# 更新进度
|
|
WpmService.update_subproduction_progress_main(sp=wsp)
|
|
|
|
elif step.type == Step.STEP_TYPE_DIV:
|
|
# 更新物料产出情况
|
|
if not omos.exists():
|
|
raise exceptions.APIException('请选择物料产出')
|
|
for i in omos:
|
|
if i.subproduction_progress.is_main:
|
|
newstep, _ = WpmService.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,
|
|
create_by=request.user, coperation=op)
|
|
for x in range(i.count):
|
|
ins = WProduct.objects.create(**wpr)
|
|
# 添加日志
|
|
WpmService.add_wproduct_flow_log(
|
|
ins, 'wproduct_create')
|
|
# 更新进度
|
|
WpmService.update_subproduction_progress_main(
|
|
sp=i.subproduction_plan)
|
|
elif step.type == Step.STEP_TYPE_COMB:
|
|
oms_w = omos.filter(subproduction_progress__is_main=True)
|
|
if len(oms_w) == 1:
|
|
oms_w = oms_w[0]
|
|
if oms_w.count!=1:
|
|
raise exceptions.APIException('产出数量应为1')
|
|
# 校验单片数量是否正确, 暂时未写
|
|
newstep, needTest = WpmService.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 step == newstep:
|
|
wproduct.act_state = WProduct.WPR_ACT_STATE_TOTEST
|
|
if wproduct.test:# 如果有正在进行的工序中检验
|
|
wproduct.test.is_midtesting = False
|
|
wproduct.test.is_submited = False
|
|
wproduct.test.save()
|
|
else:
|
|
wproduct.act_state = WProduct.WPR_ACT_STATE_DOWAIT
|
|
if needTest:
|
|
wproduct.act_state = WProduct.WPR_ACT_STATE_TOTEST
|
|
|
|
# 更新子计划进度
|
|
WpmService.update_subproduction_progress_main(
|
|
sp=oms_w.subproduction_plan)
|
|
wproduct.create_by = request.user
|
|
wproduct.coperation = op
|
|
wproduct.save()
|
|
WpmService.add_wproduct_flow_log(wproduct, 'wproduct_create')
|
|
# 隐藏原半成品
|
|
wps = WProduct.objects.filter(ow_wproduct__operation=op)
|
|
wps.update(act_state=WProduct.WPR_ACT_STATE_USED, child=wproduct, is_hidden=True,
|
|
update_by=request.user, update_time=timezone.now())
|
|
WpmService.add_wproducts_flow_log(wps, change_str='wproduct_create')
|
|
else:
|
|
raise exceptions.APIException('产出物料未填写或填写错误')
|
|
op.is_submited = True
|
|
op.update_by = request.user
|
|
op.save()
|
|
|
|
# 如果是冷加工
|
|
if processId == 1:
|
|
WpmService.update_cutting_list_with_operation(op=op)
|
|
return Response()
|
|
|
|
|
|
class OperationWproductViewSet(ListModelMixin, DestroyModelMixin, UpdateModelMixin, GenericViewSet):
|
|
"""
|
|
操作使用的半成品
|
|
"""
|
|
perms_map = {'get': '*', 'post':'operation_update',
|
|
'put':'operation_update', 'delete':'operation_update'}
|
|
queryset = OperationWproduct.objects.select_related(
|
|
'subproduction_plan', 'material').all()
|
|
serializer_class = OperationWproductListSerializer
|
|
filterset_fields = ['material', 'subproduction_plan', 'operation']
|
|
ordering_fields = ['id']
|
|
ordering = ['-id']
|
|
|
|
def get_serializer_class(self):
|
|
if self.action == 'update':
|
|
return OperationWproductUpdateSerializer
|
|
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()
|
|
wp = instance.wproduct
|
|
wp.act_state = WProduct.WPR_ACT_STATE_DOWAIT
|
|
wp.operation = None
|
|
wp.save()
|
|
return Response()
|
|
|
|
|
|
class OperationEquipViewSet(ListModelMixin, DestroyModelMixin, UpdateModelMixin, GenericViewSet):
|
|
"""
|
|
操作使用的设备
|
|
"""
|
|
perms_map = {'get': '*', 'post':'operation_update',
|
|
'put':'operation_update', 'delete':'operation_update'}
|
|
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, UpdateModelMixin, RetrieveModelMixin, GenericViewSet):
|
|
"""
|
|
操作使用的自定义表格
|
|
"""
|
|
perms_map = {'get': '*', 'post':'operation_update',
|
|
'put':'operation_update', 'delete':'operation_update'}
|
|
queryset = OperationRecord.objects.select_related(
|
|
'operation', 'form').all()
|
|
serializer_class = OperationRecordListSerializer
|
|
filterset_class = OperationRecordFilterSet
|
|
ordering_fields = ['id']
|
|
ordering = ['-id']
|
|
|
|
def get_serializer_class(self):
|
|
if self.action == 'update':
|
|
return OperationRecordSubmitSerializer
|
|
elif self.action == 'retrieve':
|
|
return OperationRecordDetailSerializer
|
|
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()
|
|
|
|
def update(self, request, *args, **kwargs):
|
|
serializer = OperationRecordSubmitSerializer(data=request.data)
|
|
serializer.is_valid(raise_exception=True)
|
|
vdata = serializer.validated_data
|
|
opr = self.get_object()
|
|
if opr.operation.is_submited:
|
|
raise exceptions.APIException('操作已提交不可修改')
|
|
for i in vdata['record_data']:
|
|
ori = i['id']
|
|
ori.field_value = i['field_value']
|
|
ori.save()
|
|
opr.is_filled = True
|
|
opr.update_by = request.user
|
|
opr.save()
|
|
return Response()
|
|
|
|
|
|
class OperationMaterialInputViewSet(ListModelMixin, CreateModelMixin, DestroyModelMixin, GenericViewSet):
|
|
"""
|
|
消耗物料
|
|
"""
|
|
perms_map = {'get': '*', 'post':'operation_update', 'delete':'operation_update'}
|
|
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': 'operation_update'},
|
|
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 CuttingListViewSet(ListModelMixin, GenericViewSet):
|
|
"""
|
|
下料清单
|
|
"""
|
|
perms_map = {'get': '*'}
|
|
queryset = OperationMaterial.objects.select_related('operation',
|
|
'subproduction_plan', 'material',
|
|
'operation__create_by').filter(operation__step__id=1,
|
|
type=SubprodctionMaterial.SUB_MA_TYPE_OUT)
|
|
serializer_class = CuttingListSerializer
|
|
filterset_class = CuttingFilterSet
|
|
ordering_fields = ['id']
|
|
ordering = ['-id']
|
|
|
|
|
|
class OperationMaterialOutputViewSet(ListModelMixin, CreateModelMixin, DestroyModelMixin, GenericViewSet):
|
|
"""
|
|
产出物料
|
|
"""
|
|
perms_map = {'get': '*', 'post':'operation_update', 'delete':'operation_update'}
|
|
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': 'operation_update'},
|
|
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 = {'get': '*', 'post':'operation_update', 'delete':'operation_update'}
|
|
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()
|
|
|
|
|