作者 vanwhebin

增加产品SKU审批

品牌四部企业微信对接项目
\ No newline at end of file
### 品牌四部企业微信对接项目
![](https://img.shields.io/badge/Python-v3.8-brightgreen)
[gitlab地址](http://gitlab.aukeyit.com/wanweibin/product-project.git)
\ No newline at end of file
... ...
... ... @@ -13,7 +13,6 @@ from .models import Auditor, Project, Result
from utils.helpers import WxPushHelper
from utils.pagination import MyPageNumberPagination
from utils.util import response
from wxProject.settings import FRONT_URL
from wxProject.qywx_settings import Conf, project_conf
... ... @@ -37,8 +36,8 @@ class CreateProject(CreateAPIView):
auditor_id=i.pk,
project_id=serializer.data['id']
)
url = re.sub("PK", str(obj_dict['id']), FRONT_URL['flow_detail'])
url = re.sub("REDIRECT_URL", parse.quote(url, safe=''), FRONT_URL['wx_authorize'])
url = re.sub("PK", str(obj_dict['id']), project_conf['flow_detail'])
url = re.sub("REDIRECT_URL", parse.quote(url, safe=''), project_conf['wx_authorize'])
first_auditor = auditors[0]
wx_client.push_card(first_auditor.user.wx_token, url, f"{request.user.username}提交了一个产品立项申请")
... ... @@ -101,8 +100,8 @@ class AuditProject(UpdateAPIView):
wx_client = WxPushHelper(Conf[project_conf['APP_ID']])
full_audit_done = self._check_audit(obj)
url = re.sub("PK", str(obj.id), FRONT_URL['flow_detail'])
url = re.sub("REDIRECT_URL", parse.quote(url, safe=''), FRONT_URL['wx_authorize'])
url = re.sub("PK", str(obj.id), project_conf['flow_detail'])
url = re.sub("REDIRECT_URL", parse.quote(url, safe=''), project_conf['wx_authorize'])
desc = "产品立项流程所有审批已完成" if full_audit_done else f"{request.user.username}已审批完成"
if full_audit_done:
obj.is_done = True
... ...
不能预览此文件类型
不能预览此文件类型
# Generated by Django 3.1.1 on 2020-11-03 08:15
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Auditor',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('order', models.PositiveSmallIntegerField(blank=True, default=0, null=True, verbose_name='排序')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sku_aud', to=settings.AUTH_USER_MODEL, verbose_name='审批人员')),
],
options={
'ordering': ('order',),
},
),
migrations.CreateModel(
name='Flow',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('is_done', models.BooleanField(default=False, verbose_name='是否已结束')),
('create_time', models.DateTimeField(auto_now_add=True)),
('auditor', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='sku.auditor', verbose_name='审核人员')),
('starter', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL, verbose_name='发起人')),
],
),
migrations.CreateModel(
name='SKU',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('supplier', models.CharField(default='', max_length=100, verbose_name='供应商')),
('sku', models.CharField(default='', max_length=100, verbose_name='SKU')),
('model', models.CharField(default='', max_length=100, verbose_name='型号')),
('title', models.CharField(default='', max_length=100, verbose_name='SKU名称')),
('is_new', models.CharField(default='', max_length=100, verbose_name='是否新品')),
('qty', models.CharField(default='', max_length=100, verbose_name='需求单数量')),
('price_with_tax', models.DecimalField(decimal_places=4, default=0, max_digits=9, verbose_name='含税单价')),
('amount_with_tax', models.DecimalField(decimal_places=4, default=0, max_digits=9, verbose_name='含税总金额')),
('sell_day', models.PositiveIntegerField(default=0, null=True, verbose_name='可售天数')),
('qty_within_30', models.PositiveIntegerField(default=0, null=True, verbose_name='30天销量')),
('inventory', models.PositiveIntegerField(default=0, null=True, verbose_name='库存')),
('coming_inventory', models.PositiveIntegerField(default=0, null=True, verbose_name='采购在途')),
('gross_profit_rate', models.FloatField(default=0, null=True, verbose_name='毛利率')),
('return_rate', models.FloatField(default=0, null=True, verbose_name='客退率')),
('memo', models.CharField(default='', max_length=255, null=True, verbose_name='备注')),
('create_time', models.DateTimeField(auto_now_add=True)),
('purchaser', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='purchase_user', to=settings.AUTH_USER_MODEL, verbose_name='采购')),
],
),
migrations.CreateModel(
name='Result',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('is_accept', models.CharField(choices=[('accept', '通过'), ('reject', '否决')], max_length=10, null=True, verbose_name='审核项目')),
('memo', models.CharField(blank=True, default='', max_length=300, verbose_name='审核结果陈述')),
('create_time', models.DateTimeField(auto_now_add=True)),
('auditor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='result_auditor', to='sku.auditor', verbose_name='审核人员')),
('flow', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='result_flow', to='sku.flow', verbose_name='审核项目')),
],
options={
'ordering': ('pk',),
},
),
migrations.CreateModel(
name='Leader',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sku_leader', to=settings.AUTH_USER_MODEL, verbose_name='采购负责人')),
],
),
migrations.CreateModel(
name='FlowSKU',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('is_pass', models.BooleanField(default=False, verbose_name='是否通过,默认为否')),
('flow', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sku.flow', verbose_name='对应的流程')),
('sku', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sku.sku', verbose_name='流程中的的SKU')),
],
),
]
... ...
# Generated by Django 3.1.1 on 2020-11-03 09:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sku', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='sku',
name='is_new',
field=models.CharField(choices=[('是', '新品'), ('否', '非新品')], max_length=10, verbose_name='是否新品'),
),
migrations.AlterField(
model_name='sku',
name='purchaser',
field=models.CharField(max_length=255, verbose_name='采购名字'),
),
migrations.AlterField(
model_name='sku',
name='supplier',
field=models.CharField(default='', max_length=255, verbose_name='供应商'),
),
]
... ...
# Generated by Django 3.1.1 on 2020-11-03 10:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sku', '0002_auto_20201103_1751'),
]
operations = [
migrations.AlterField(
model_name='sku',
name='amount_with_tax',
field=models.DecimalField(decimal_places=4, default=0, max_digits=12, verbose_name='含税总金额'),
),
migrations.AlterField(
model_name='sku',
name='price_with_tax',
field=models.DecimalField(decimal_places=4, default=0, max_digits=12, verbose_name='含税单价'),
),
]
... ...
# Generated by Django 3.1.1 on 2020-11-03 10:57
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('sku', '0003_auto_20201103_1818'),
]
operations = [
migrations.RemoveField(
model_name='flow',
name='auditor',
),
]
... ...
# Generated by Django 3.1.1 on 2020-11-05 09:10
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('sku', '0004_remove_flow_auditor'),
]
operations = [
migrations.AlterField(
model_name='flowsku',
name='flow',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sku_flow', to='sku.flow', verbose_name='对应的流程'),
),
migrations.AlterField(
model_name='flowsku',
name='sku',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='flow_sku', to='sku.sku', verbose_name='流程中的的SKU'),
),
migrations.AlterField(
model_name='sku',
name='memo',
field=models.CharField(blank=True, default='', max_length=255, null=True, verbose_name='备注'),
),
]
... ...
# Generated by Django 3.1.1 on 2020-11-05 09:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sku', '0005_auto_20201105_1710'),
]
operations = [
migrations.AlterField(
model_name='flowsku',
name='is_pass',
field=models.BooleanField(null=True, verbose_name='是否通过,默认为否'),
),
]
... ...
from django.db import models
# Create your models here.
from usercenter.models import User
class Auditor(models.Model):
user = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name="sku_aud",
verbose_name="审批人员")
order = models.PositiveSmallIntegerField(default=0, null=True, blank=True, verbose_name="排序")
def __str__(self):
return self.user.username
class Meta:
ordering = ('order', )
class SKU(models.Model):
NEW_CHOICES = (
("是", "新品"),
("否", "非新品")
)
supplier = models.CharField(max_length=255, default="", verbose_name="供应商")
sku = models.CharField(max_length=100, default="", verbose_name="SKU")
model = models.CharField(max_length=100, default="", verbose_name="型号")
title = models.CharField(max_length=100, default="", verbose_name="SKU名称")
is_new = models.CharField(max_length=10, choices=NEW_CHOICES, verbose_name="是否新品")
qty = models.CharField(max_length=100, default="", verbose_name="需求单数量")
price_with_tax = models.DecimalField(max_digits=12, decimal_places=4, default=0, verbose_name="含税单价")
amount_with_tax = models.DecimalField(max_digits=12, decimal_places=4, default=0, verbose_name="含税总金额")
sell_day = models.PositiveIntegerField(default=0, null=True, verbose_name="可售天数")
qty_within_30 = models.PositiveIntegerField(default=0, null=True, verbose_name="30天销量")
inventory = models.PositiveIntegerField(default=0, null=True, verbose_name="库存")
coming_inventory = models.PositiveIntegerField(default=0, null=True, verbose_name="采购在途")
gross_profit_rate = models.FloatField(default=0, null=True, verbose_name="毛利率")
return_rate = models.FloatField(default=0, null=True, verbose_name="客退率")
memo = models.CharField(default="", max_length=255, null=True, verbose_name="备注", blank=True)
create_time = models.DateTimeField(auto_now_add=True)
purchaser = models.CharField(max_length=255, verbose_name="采购名字")
class Flow(models.Model):
""" 提交发起的流程"""
starter = models.ForeignKey(User, on_delete=models.DO_NOTHING, verbose_name="发起人")
is_done = models.BooleanField(default=False, verbose_name="是否已结束")
create_time = models.DateTimeField(auto_now_add=True)
class FlowSKU(models.Model):
""" 流程中的SKU """
sku = models.ForeignKey(SKU, on_delete=models.CASCADE, verbose_name="流程中的的SKU", related_name="flow_sku")
flow = models.ForeignKey(Flow, on_delete=models.CASCADE, verbose_name="对应的流程", related_name="sku_flow")
is_pass = models.BooleanField(null=True, verbose_name="是否通过,默认为否")
class Result(models.Model):
ACCEPT_CHOICES = (
('accept', '通过'),
('reject', '否决')
)
auditor = models.ForeignKey(Auditor, on_delete=models.CASCADE, related_name="result_auditor", verbose_name="审核人员")
flow = models.ForeignKey(Flow, on_delete=models.CASCADE, related_name="result_flow", verbose_name="审核项目")
is_accept = models.CharField(max_length=10, null=True, choices=ACCEPT_CHOICES, verbose_name="审核项目")
memo = models.CharField(max_length=300, blank=True, default="", verbose_name="审核结果陈述")
create_time = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('pk', )
class Leader(models.Model):
""" 采购负责人"""
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="sku_leader", verbose_name="采购负责人")
... ...
# _*_ coding: utf-8 _*_
# @Time : 2020/11/3 16:34
# @Author vanwhebin
from rest_framework import serializers
from .models import Auditor, Flow, Result, SKU, FlowSKU
class AuditorSerializer(serializers.ModelSerializer):
class Meta:
model = Auditor
fields = '__all__'
depth = 1
class SKUSerializer(serializers.ModelSerializer):
# pk = serializers.ReadOnlyField()
# is_new = serializers.ReadOnlyField()
# purchaser = serializers.ReadOnlyField()
class Meta:
model = SKU
# fields = ("supplier", "sku", "model", "title", "is_new", "qty", "price_with_tax", "amount_with_tax", "sell_day",
# "qty_within_30", "inventory", "coming_inventory", "gross_profit_rate", "return_rate", "memo",
# "purchaser")
fields = "__all__"
class FlowSerializer(serializers.ModelSerializer):
result = serializers.ReadOnlyField()
creator_name = serializers.ReadOnlyField()
skus = serializers.ReadOnlyField()
class Meta:
model = Flow
fields = ("result", "creator_name", "skus", "is_done", "create_time")
class FlowListSerializer(serializers.ModelSerializer):
result = serializers.BooleanField()
creator_name = serializers.CharField()
flow_id = serializers.IntegerField()
class Meta:
model = Result
fields = ("creator_name", "result", "flow_id", "create_time")
class ResultSerializer(serializers.ModelSerializer):
class Meta:
model = Result
fields = "__all__"
class FlowSKUSerializer(serializers.ModelSerializer):
class Meta:
model = FlowSKU
fields = "__all__"
depth = 1
... ...
... ... @@ -2,10 +2,17 @@
# @Time : 2020/11/2 18:41
# @Author vanwhebin
from django.urls import path, include
from django.urls import path
from sku import views
app_name = "sku"
urlpatterns = [
path('create', views.CreateSKUFlow.as_view(), name="create_sku_flow"),
path('audit/<int:pk>', views.AuditFlow.as_view(), name="audit_flow"),
path('right/<int:pk>', views.CheckAuth.as_view(), name="check_audit_auth"),
path('list', views.AuditFlowList.as_view(), name="list_flow"),
path('<int:pk>', views.FlowDetail.as_view(), name="retrieve_flow"),
path('info/<int:pk>', views.UpdateSKUInfo.as_view(), name="update_sku"),
path('template', views.ParseSKUExcel.as_view(), name="upload_sku_template"),
]
... ...
from django.shortcuts import render
import os
import re
from urllib import parse
# Create your views here.
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)
... ...
... ... @@ -59,7 +59,7 @@ class WxPushHelper:
"msgtype": "textcard",
"agentid": self.conf['APP_ID'], # 企业应用ID
"textcard": {
"title": "产品立项流程通知",
"title": f"{self.conf['title']}流程通知",
"description": description,
"url": url,
"btntxt": "点击查看"
... ...
... ... @@ -15,21 +15,8 @@ from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework import status
class UploadMedia(CreateAPIView):
serializer_class = MediaSerializer
permission_classes = (IsAuthenticated, )
def post(self, request, *args, **kwargs):
super(UploadMedia, self).__init__()
uploaded_file = request.FILES.get('file')
uploaded_file.name = uploaded_file.name.strip('"')
if not uploaded_file:
return response(msg=u"文件上传失败", status_code=status.HTTP_400_BAD_REQUEST)
ext_pos = uploaded_file.name.rfind('.')
uploaded_file_ext = uploaded_file.name[ext_pos:]
if not self.allowed_extension(uploaded_file_ext):
return response(msg="非法文件类型", status_code=status.HTTP_400_BAD_REQUEST)
def upload_media(uploaded_file, uploaded_file_ext, user):
""" 处理上传新建媒体记录 """
hash_file_name = uuid() + uploaded_file_ext
time_tag = time.strftime('%Y-%m-%d')
file_path = time_tag + os.sep + hash_file_name
... ... @@ -48,17 +35,35 @@ class UploadMedia(CreateAPIView):
file=uploaded_file,
file_name=uploaded_file.name,
hash=file_hash,
user=request.user,
user=user,
size=file_size,
extension=uploaded_file_ext
)
os.remove(os.path.join(MEDIA_ROOT, file_path))
# return response.Response(MediaSerializer(file, context={'request': request}).data)
return file
class UploadMedia(CreateAPIView):
serializer_class = MediaSerializer
permission_classes = (IsAuthenticated, )
def post(self, request, *args, **kwargs):
super(UploadMedia, self).__init__()
uploaded_file = request.FILES.get('file')
uploaded_file.name = uploaded_file.name.strip('"')
if not uploaded_file:
return response(msg=u"文件上传失败", status_code=status.HTTP_400_BAD_REQUEST)
ext_pos = uploaded_file.name.rfind('.')
uploaded_file_ext = uploaded_file.name[ext_pos:]
if not self.allowed_extension(uploaded_file_ext):
return response(msg="非法文件类型", status_code=status.HTTP_400_BAD_REQUEST)
file = upload_media(uploaded_file, uploaded_file_ext, request.user)
return response(MediaSerializer(file, context={'request': request}).data)
@staticmethod
def allowed_extension(ext):
choices = UPLOAD_MEDIA_CHOICES
def allowed_extension(ext, allowed_ext=None):
choices = allowed_ext if allowed_ext else UPLOAD_MEDIA_CHOICES
for allowed_ext in choices:
if ext == allowed_ext[0]:
return True
... ...
... ... @@ -11,13 +11,17 @@ project_conf = {
"CORP_ID": "ww0f3efc2873ad11c3",
"APP_ID": '1000078',
"APP_SECRET": "7MHpdQICiegx9rIc4iZrEPunb1aYUqdJYKSW9v7a1A8",
"wx_authorize": "https://open.weixin.qq.com/connect/oauth2/authorize?appid=ww0f3efc2873ad11c3&redirect_uri=REDIRECT_URL&response_type=code&scope=snsapi_base&state=1000078#wechat_redirect",
"flow_detail": "http://project.tacklifetools.com/product/audit/PK"
}
sku_conf = {
"title": "sku条目审批应用",
"title": "返单特采",
"CORP_ID": "ww0f3efc2873ad11c3",
"APP_ID": '1000081',
"APP_SECRET": "_O_MrxbQO1vojzNBPAiaF_MdzikHRbnVfFc3v8iOtKo",
"wx_authorize": "https://open.weixin.qq.com/connect/oauth2/authorize?appid=ww0f3efc2873ad11c3&redirect_uri=REDIRECT_URL&response_type=code&scope=snsapi_base&state=1000081#wechat_redirect",
"flow_detail": "http://project.tacklifetools.com/sku/audit/PK"
}
... ... @@ -25,3 +29,4 @@ Conf = {
"1000078": project_conf,
"1000081": sku_conf
}
... ...
... ... @@ -177,7 +177,3 @@ SIMPLE_JWT = {
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
}
FRONT_URL = {
"wx_authorize": "https://open.weixin.qq.com/connect/oauth2/authorize?appid=ww0f3efc2873ad11c3&redirect_uri=REDIRECT_URL&response_type=code&scope=snsapi_base#wechat_redirect",
"flow_detail": "http://project.tacklifetools.com/product/audit/PK"
}
... ...
... ... @@ -18,6 +18,7 @@ urlpatterns = [
path(api_version + 'auth/', include('usercenter.urls', namespace='usercenter')),
path(api_version + 'upload/', UploadMedia.as_view()),
path(api_version + 'project/', include('project.urls', namespace="project")),
path(api_version + 'sku/', include('sku.urls', namespace="sku")),
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
re_path(r'api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
re_path(r'api/token/', MyTokenObtainPairView.as_view(), name='token_obtain_pair'),
... ...