views.py 11.5 KB
import os
import re
from urllib import parse

from rest_framework.generics import CreateAPIView, RetrieveAPIView, UpdateAPIView, ListAPIView
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework import status
import pyexcel as p
from django.db import transaction
from django.forms import model_to_dict
from django.db.models import Q

from .models import SKU, Auditor, Flow, FlowSKU, Result, Leader
from wxProject.settings import MEDIA_ROOT
from utils.pagination import MyPageNumberPagination
from wxProject.qywx_settings import Conf, sku_conf
from utils.util import response
from utils.media import upload_media
from .serializers import FlowSerializer, SKUSerializer, FlowListSerializer, FlowSKUSerializer
from utils.helpers import WxPushHelper


class CreateSKUFlow(CreateAPIView):
	""" 创建SKU审核流程 """
	serializer_class = FlowSerializer
	permission_classes = (IsAuthenticated,)

	# @transaction.atomic
	def post(self, request, *args, **kwargs):
		super(CreateSKUFlow, self).__init__()
		skus = request.data.get('sku', None)
		if not skus or skus == []:
			return response(msg=u"sku不能为空", status_code=status.HTTP_400_BAD_REQUEST)
		# 提交过来sku的ID
		# flow 表  flow_sku表 result表
		wx_client = WxPushHelper(Conf[sku_conf['APP_ID']])
		with transaction.atomic():
			save_id = transaction.savepoint()

		try:
			skus = list(set(skus))
			sku_objs = SKU.objects.filter(pk__in=skus)
			flow = Flow.objects.create(starter=request.user)
			flow_sku_list = [FlowSKU(sku=sku, flow=flow) for sku in sku_objs]
			FlowSKU.objects.bulk_create(flow_sku_list)
			auditors = Auditor.objects.all().order_by('order')
			auditor_list = [Result(auditor=auditor, flow=flow) for auditor in auditors]
			Result.objects.bulk_create(auditor_list)

			transaction.savepoint_commit(save_id)

			url = re.sub("PK", str(flow.id), sku_conf['flow_detail'])
			url = re.sub("REDIRECT_URL", parse.quote(url, safe=''), sku_conf['wx_authorize'])
			first_auditor = auditors[0]

			wx_client.push_card(first_auditor.user.wx_token, url, f"{request.user.username}发起一个毛利不达标产品审批流程")
			wx_client.push_card(request.user.wx_token, url, u"流程创建成功")
			# 通知创建人和审批人
			return response()
		except Exception as e:
			print(e.args)
			print(e.with_traceback)
			transaction.savepoint_rollback(save_id)
			return response(msg=u"创建流程有误", status_code=status.HTTP_400_BAD_REQUEST)


class UpdateSKUInfo(UpdateAPIView):
	queryset = SKU.objects.all()
	serializer_class = SKUSerializer
	permission_classes = (IsAuthenticated,)

	def put(self, request, *args, **kwargs):
		""" 更新 单个sku的信息 """
		serializer = SKUSerializer(data=request.data)
		# 查找对应用户是否拥有该sku的修改权
		flow_id = request.data.get('flowID', None)
		if not flow_id:
			return response(msg=u"非法参数", status_code=status.HTTP_400_BAD_REQUEST)
		flow_sku = FlowSKU.objects.filter(flow_id=flow_id, sku_id=kwargs['pk']).first()
		if not flow_sku or flow_sku.flow.starter.id != request.user.id:
			return response(msg=u"非法参数", status_code=status.HTTP_401_UNAUTHORIZED)

		if not serializer.is_valid():
			raise ValueError(serializer.errors)
		else:
			serializer.update(instance=self.get_object(), validated_data=serializer.data)
		return response()


class FlowDetail(RetrieveAPIView):
	""" 查看当前流程 """
	# 查看当前流程的sku列表   流程进度  流程结果
	queryset = Flow.objects.all()
	serializer_class = FlowSerializer
	permission_classes = (IsAuthenticated,)

	def get(self, request, *args, **kwargs):
		cur_obj = self.get_object()
		cur_obj.result = []
		results = Result.objects.filter(flow=cur_obj)
		for i in results:
			cur_obj.result.append({
				"auditor": i.auditor.user.username,
				"is_accept": i.is_accept,
				"memo": i.memo,
			})
		# skus = (SKU.objects.filter(pk__in=FlowSKU.objects.filter(flow=cur_obj).values_list('sku_id', flat=True)))
		# cur_obj.skus = [model_to_dict(sku) for sku in skus]
		flow_skus = FlowSKU.objects.filter(flow=cur_obj)
		cur_obj.skus = [FlowSKUSerializer(sku).data for sku in flow_skus]

		return response(FlowSerializer(cur_obj).data)


class AuditFlow(UpdateAPIView):
	""" 更新当前流程状态,对流程进行审批"""
	queryset = Flow.objects.all()
	serializer_class = FlowSerializer
	permission_classes = (IsAuthenticated, IsAdminUser)

	@staticmethod
	def _check_audit(flow_obj):
		# 查看是否已经全部审核完毕 进行更新flow表,只有所有sku都审批通过才能流向下一节点
		# 所有的审核人员, 是否有result记录
		aud_result_len = Result.objects.filter(flow=flow_obj, is_accept__isnull=False).count()
		auditor_len = Auditor.objects.count()
		return aud_result_len == auditor_len

	@transaction.atomic
	def partial_update(self, request, *args, **kwargs):
		# 对项目进行更新

		accept_choices = (
			('accept', '通过'),
			('reject', '否决')
		)
		# accept_param = accept_choices[0][0] if request.data.get('is_accept') else accept_choices[1][0]
		skus = request.data.get('sku', [])
		if len(skus) == 0 or not isinstance(request.data.get('is_accept'), bool):
			return response(msg=u"非法审批参数", status_code=status.HTTP_400_BAD_REQUEST)

		result_obj = Result.objects.filter(auditor_id=self.request.user.id, is_accept__isnull=True,
		                                   flow__pk=kwargs['pk']).first()
		obj = self.get_object()
		if not obj or not result_obj:
			raise PermissionError
		else:
			# 只有当前flow的所有的sku审批通过,采流下一节点审批
			flow_skus = FlowSKU.objects.filter(flow=obj)
			accept_bool = bool(request.data.get('is_accept'))
			if len(flow_skus) == len(request.data.get('sku')):
				accept_param = accept_choices[0][0]
			else:
				obj.is_done = True
				accept_param = accept_choices[1][0]
			result_obj.is_accept = accept_param
			result_obj.memo = request.data.get('memo', '')
			result_obj.save()
			for flow_sku in flow_skus:
				if flow_sku.sku.id in skus:
					flow_sku.is_pass = accept_bool
				else:
					flow_sku.is_pass = not accept_bool
				flow_sku.save()

			wx_client = WxPushHelper(Conf[sku_conf['APP_ID']])
			full_audit_done = self._check_audit(obj)
			url = re.sub("PK", str(obj.id), sku_conf['flow_detail'])
			url = re.sub("REDIRECT_URL", parse.quote(url, safe=''), sku_conf['wx_authorize'])
			desc = "返单特采流程所有审批已完成" if full_audit_done else f"{request.user.username}已审批完成"
			if full_audit_done:
				obj.is_done = True
			else:
				if not bool(request.data.get('is_accept')):
					obj.is_done = True
				else:
					next_auditor_id = Result.objects.filter(flow=obj, is_accept__isnull=True) \
						.values_list('auditor_id', flat=True).order_by('pk').first()
					second_auditor = Auditor.objects.filter(pk=next_auditor_id).first()
					wx_client.push_card(second_auditor.user.wx_token, url, f"{request.user.username}已审核了一个返单特采申请")
			obj.save()
			wx_client.push_card(obj.starter.wx_token, url, desc)

		return response(FlowSerializer(obj).data)


class AuditFlowList(ListAPIView):
	""" 查看当前审核人员名下的审核清单 """
	queryset = Result.objects.all()
	serializer_class = FlowListSerializer
	pagination_class = MyPageNumberPagination
	permission_classes = (IsAuthenticated,)

	def get_queryset(self):
		data = Result.objects.filter(auditor_id=self.request.user.id).order_by('is_accept', '-create_time')
		for item in data:
			# result = Flow.objects.filter(=self.request.user).values_list('is_done', flat=True).first()
			item.creator_name = item.flow.starter.username
			item.result = True if item.is_accept else False
			item.flow_id = item.flow.id
		return data


class CheckAuth(APIView):
	""" 查看当前查看用户是否有审批权限"""
	allowed_methods = ('GET',)
	permission_classes = (IsAuthenticated,)

	@staticmethod
	def get(request, *args, **kwargs):
		flow_result = Result.objects.filter(flow__pk=kwargs['pk'])
		#  auditor=request.user) | Q(flow__starter=request.user)
		if not flow_result \
				or flow_result.result_auditor.user.id != request.user.id:
			return response(False)
		else:
			order = int(Auditor.objects.filter(user=request.user).values_list('order', flat=True).first())
			if order > 0:
				# 当审核人员排队时 需要判断是否已经流转到自己  前一个人员是否已经有处理
				# 还要判断自己是否已经审核过了
				index = order - 1
				if bool(flow_result[index].is_accept) and not bool(flow_result[order].is_accept):
					return response(True)
				else:
					return response(False)
			else:
				return response(False) if bool(flow_result[0].is_accept) else response(True)


class ParseSKUExcel(CreateAPIView):
	""" 解析上传excel的SKU数据"""
	serializer_class = SKUSerializer
	permission_classes = (IsAuthenticated,)
	allowed_extension = (".xls", ".xlsx")

	def post(self, request, *args, **kwargs):
		super(ParseSKUExcel, self).__init__()
		file_obj = self._upload(request)
		# 解析sku列表 插入数据库
		path = os.path.join(MEDIA_ROOT, str(file_obj.file))
		t = p.get_sheet(file_name=path, start_row=1, column_limit=16)
		with transaction.atomic():
			save_id = transaction.savepoint()
		try:
			s_list = self._insert_data(list(t.rows()))
			transaction.savepoint_commit(save_id)
			return response(s_list)
		except ValueError as e:
			transaction.savepoint_rollback(save_id)
			return response(msg=u"文件上传数据有误", status_code=status.HTTP_400_BAD_REQUEST)
		except IndexError as e:
			transaction.savepoint_rollback(save_id)
			return response(msg=u"文件上传数据有误", status_code=status.HTTP_400_BAD_REQUEST)
		except Exception as e:
			transaction.savepoint_rollback(save_id)
			return response(msg=u"文件上传数据有误", status_code=status.HTTP_400_BAD_REQUEST)

	@staticmethod
	def _insert_data(rows):
		obj_list = []
		for row in rows:
			if len(row) < 16:
				raise IndexError("上传数据字段不完整")
			qty = 0 if row[5] == "" or row[5] == "#N/A" else int(row[5])
			price_with_tax = 0 if row[6] == "" or row[6] == "#N/A" else float(row[6])
			amount_with_tax = 0 if row[7] == "" or row[7] == "#N/A" else float(row[7])
			sell_day = 0 if row[8] == "" or row[8] == "#N/A" else int(row[8])
			qty_within_30 = 0 if row[9] == "" or row[9] == "#N/A" else int(row[9])
			inventory = 0 if row[10] == "" or row[10] == "#N/A" else int(row[10])
			coming_inventory = 0 if row[11] == "" or row[11] == "#N/A" else int(row[11])
			gross_profit_rate = 0 if row[12] == "" or row[12] == "#N/A" else float(row[12])
			return_rate = 0 if row[13] == "" or row[13] == "#N/A" else float(row[13])
			memo = "" if row[14] == "#N/A" else row[14].strip()
			purchaser = row[15].strip()

			obj = SKU.objects.create(
				supplier=row[0],
				sku=row[1],
				model=row[2],
				title=row[3],
				is_new=row[4],
				qty=qty,
				price_with_tax=price_with_tax,
				amount_with_tax=amount_with_tax,
				sell_day=sell_day,
				qty_within_30=qty_within_30,
				inventory=inventory,
				coming_inventory=coming_inventory,
				gross_profit_rate=gross_profit_rate,
				return_rate=return_rate,
				memo=memo,
				purchaser=purchaser,

			)
			obj_list.append(SKUSerializer(obj).data)
		return obj_list

	def _upload(self, request):
		excel = request.FILES.get('file')

		if not excel:
			return response(msg=u"文件上传失败", status_code=status.HTTP_400_BAD_REQUEST)
		excel.name = excel.name.strip('"')
		ext_pos = excel.name.rfind('.')
		uploaded_file_ext = excel.name[ext_pos:]
		if uploaded_file_ext not in self.allowed_extension:
			return response(msg="非法文件类型", status_code=status.HTTP_400_BAD_REQUEST)
		return upload_media(excel, uploaded_file_ext, request.user)