+ statistics by clients

+ cdek_tracking
+ checklist create API
+ some permissions
* Cleanup
This commit is contained in:
Phil Zhitnikov 2023-07-05 02:19:58 +04:00
parent 369dbf31ae
commit 4511a65b7f
7 changed files with 149 additions and 102 deletions

View File

@ -1,6 +1,7 @@
from django.contrib import admin from django.contrib import admin
from django.contrib.admin import display
from .models import Category, Checklist, GlobalSettings, PaymentMethod, PromoCode, User, Image from .models import Category, Checklist, GlobalSettings, PaymentMethod, Promocode, User, Image
@admin.register(User) @admin.register(User)
@ -21,8 +22,12 @@ class ImageAdmin(admin.ModelAdmin):
@admin.register(Checklist) @admin.register(Checklist)
class ChecklistAdmin(admin.ModelAdmin): class ChecklistAdmin(admin.ModelAdmin):
list_display = ('id', 'date', 'price_rub', 'commission_rub') list_display = ('id', 'brand', 'model', 'price_rub', 'commission_rub', 'date', 'status_display')
filter_horizontal = ('images',) ordering = ('-status_updated_at', '-created_at')
@display(description='Статус')
def status_display(self, obj: Checklist):
return obj.get_status_display()
def date(self, obj: Checklist): def date(self, obj: Checklist):
return obj.status_updated_at or obj.created_at return obj.status_updated_at or obj.created_at
@ -41,7 +46,7 @@ class PaymentMethodAdmin(admin.ModelAdmin):
list_display = ('name', 'slug') list_display = ('name', 'slug')
@admin.register(PromoCode) @admin.register(Promocode)
class PromoCodeAdmin(admin.ModelAdmin): class PromoCodeAdmin(admin.ModelAdmin):
list_display = ('name', 'discount', 'free_delivery', 'no_comission') list_display = ('name', 'discount', 'free_delivery', 'no_comission')

View File

@ -3,6 +3,8 @@ from rest_framework.exceptions import APIException
class CRMException(APIException): class CRMException(APIException):
status_code = status.HTTP_400_BAD_REQUEST
def __init__(self, detail=None): def __init__(self, detail=None):
if detail is None: if detail is None:
detail = self.default_detail detail = self.default_detail

View File

@ -130,12 +130,15 @@ class User(AbstractUser):
return concat_not_null_values(self.last_name, self.first_name, self.middle_name) return concat_not_null_values(self.last_name, self.first_name, self.middle_name)
class PromoCode(models.Model): class Promocode(models.Model):
name = models.CharField('Название', max_length=100, unique=True) name = models.CharField('Название', max_length=100, unique=True)
discount = models.DecimalField('Скидка', max_digits=10, decimal_places=2,) discount = models.DecimalField('Скидка', max_digits=10, decimal_places=2,)
free_delivery = models.BooleanField('Бесплатная доставка', default=False) # freedelivery free_delivery = models.BooleanField('Бесплатная доставка', default=False) # freedelivery
no_comission = models.BooleanField('Без комиссии', default=False) # nocomission no_comission = models.BooleanField('Без комиссии', default=False) # nocomission
def __str__(self):
return self.name
class Meta: class Meta:
verbose_name = 'Промокод' verbose_name = 'Промокод'
verbose_name_plural = 'Промокоды' verbose_name_plural = 'Промокоды'
@ -155,9 +158,11 @@ class PaymentMethod(models.Model):
return self.name return self.name
@cleanup.select
class Image(models.Model): class Image(models.Model):
image = models.ImageField(upload_to='checklist_images') image = models.ImageField(upload_to='checklist_images')
is_preview = models.BooleanField(default=False) is_preview = models.BooleanField(default=False)
checklist = models.ForeignKey('Checklist', on_delete=models.CASCADE, related_name='images')
class Meta: class Meta:
verbose_name = 'Изображение' verbose_name = 'Изображение'
@ -168,6 +173,8 @@ class Image(models.Model):
def generate_checklist_id(): def generate_checklist_id():
""" Generate unique id for Checklist """
all_ids = Checklist.objects.all().values_list('id', flat=True) all_ids = Checklist.objects.all().values_list('id', flat=True)
allowed_chars = string.ascii_letters + string.digits allowed_chars = string.ascii_letters + string.digits
@ -226,18 +233,6 @@ class Checklist(models.Model):
(COMPLETED, 'Завершен'), (COMPLETED, 'Завершен'),
) )
# Payment types
class PaymentType:
ALFA = "alfa"
TINKOFF = "tink"
RAIFFEISEN = "raif"
CHOICES = (
(ALFA, 'Альфа-Банк'),
(TINKOFF, 'Тинькофф Банк'),
(RAIFFEISEN, 'Райффайзен Банк'),
)
# Delivery # Delivery
class DeliveryType: class DeliveryType:
PICKUP = "pickup" PICKUP = "pickup"
@ -249,7 +244,7 @@ class Checklist(models.Model):
) )
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
status_updated_at = models.DateTimeField('Дата обновления статуса заказа') status_updated_at = models.DateTimeField('Дата обновления статуса заказа', auto_now_add=True)
id = models.CharField(primary_key=True, max_length=settings.CHECKLIST_ID_LENGTH, id = models.CharField(primary_key=True, max_length=settings.CHECKLIST_ID_LENGTH,
default=generate_checklist_id, editable=False) default=generate_checklist_id, editable=False)
status = models.CharField('Статус заказа', max_length=15, choices=Status.CHOICES, default=Status.NEW) status = models.CharField('Статус заказа', max_length=15, choices=Status.CHOICES, default=Status.NEW)
@ -263,14 +258,12 @@ class Checklist(models.Model):
model = models.CharField('Модель', max_length=100, null=True, blank=True) model = models.CharField('Модель', max_length=100, null=True, blank=True)
size = models.CharField('Размер', max_length=30, null=True, blank=True) size = models.CharField('Размер', max_length=30, null=True, blank=True)
images = models.ManyToManyField('Image', verbose_name='Картинки', blank=True)
# curencycurency2 # curencycurency2
price_yuan = models.DecimalField('Цена в юанях', max_digits=10, decimal_places=2, default=0) price_yuan = models.DecimalField('Цена в юанях', max_digits=10, decimal_places=2, default=0)
# TODO: replace by parser # TODO: replace real_price by parser
real_price = models.DecimalField('Реальная цена', max_digits=10, decimal_places=2, null=True, blank=True) real_price = models.DecimalField('Реальная цена', max_digits=10, decimal_places=2, null=True, blank=True)
# TODO: choose from PromoCode table # TODO: choose from Promocode table
# promo # promo
promocode = models.CharField('Промокод', max_length=100, null=True, blank=True) promocode = models.CharField('Промокод', max_length=100, null=True, blank=True)
comment = models.CharField('Комментарий', max_length=200, null=True, blank=True) comment = models.CharField('Комментарий', max_length=200, null=True, blank=True)
@ -296,7 +289,8 @@ class Checklist(models.Model):
delivery = models.CharField('Тип доставки', max_length=10, choices=DeliveryType.CHOICES, null=True, blank=True) delivery = models.CharField('Тип доставки', max_length=10, choices=DeliveryType.CHOICES, null=True, blank=True)
# trackid # trackid
track_number = models.CharField('Трек-номер', max_length=100, null=True, blank=True) poizon_tracking = models.CharField('Трек-номер Poizon', max_length=100, null=True, blank=True)
cdek_tracking = models.CharField('Трек-номер СДЭК', max_length=100, null=True, blank=True)
objects = ChecklistQuerySet.as_manager() objects = ChecklistQuerySet.as_manager()
@ -359,9 +353,9 @@ class Checklist(models.Model):
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if self.id: if self.id:
old_obj = Checklist.objects.get(id=self.id) old_obj = Checklist.objects.filter(id=self.id).first()
# If status was updated, update status_updated_at field # If status was updated, update status_updated_at field
if self.status != old_obj.status: if old_obj and self.status != old_obj.status:
self.status_updated_at = timezone.now() self.status_updated_at = timezone.now()
# Create preview image # Create preview image
@ -375,11 +369,10 @@ class Checklist(models.Model):
preview.save(image_io, format='JPEG') preview.save(image_io, format='JPEG')
# Create Image model and save it # Create Image model and save it
image_obj = Image(is_preview=True) image_obj = Image(is_preview=True, checklist_id=self.id)
image_obj.image.save(name=f'{self.id}_preview.jpg', image_obj.image.save(name=f'{self.id}_preview.jpg',
content=ContentFile(image_io.getvalue()), content=ContentFile(image_io.getvalue()),
save=True) save=True)
self.images.add(image_obj)
super().save(*args, **kwargs) super().save(*args, **kwargs)

View File

@ -1,8 +1,12 @@
import base64
from django.contrib.auth import authenticate from django.contrib.auth import authenticate
from django.core.files.base import ContentFile
from drf_extra_fields.fields import Base64ImageField
from rest_framework import serializers from rest_framework import serializers
from store.exceptions import CRMException, InvalidCredentialsException from store.exceptions import CRMException, InvalidCredentialsException
from store.models import User, Checklist, GlobalSettings, Category, PaymentMethod, PromoCode, Image from store.models import User, Checklist, GlobalSettings, Category, PaymentMethod, Promocode, Image
class LoginSerializer(serializers.Serializer): class LoginSerializer(serializers.Serializer):
@ -44,12 +48,14 @@ class UserSerializer(serializers.ModelSerializer):
class ImageSerializer(serializers.ModelSerializer): class ImageSerializer(serializers.ModelSerializer):
image = Base64ImageField()
class Meta: class Meta:
model = Image model = Image
fields = ('image', ) fields = ('image',)
class OrderImageListSerializer(serializers.ListSerializer): class ChecklistImageListSerializer(serializers.ListSerializer):
child = ImageSerializer() child = ImageSerializer()
def to_representation(self, data): def to_representation(self, data):
@ -59,44 +65,54 @@ class OrderImageListSerializer(serializers.ListSerializer):
class ChecklistSerializer(serializers.ModelSerializer): class ChecklistSerializer(serializers.ModelSerializer):
id = serializers.CharField(read_only=True) id = serializers.CharField(read_only=True)
managerid = serializers.CharField(source='manager.manager_id', required=False, allow_null=True) managerid = serializers.CharField(source='manager.manager_id', read_only=True)
link = serializers.URLField(source='product_link', required=False) link = serializers.URLField(source='product_link', required=False)
category = serializers.SlugRelatedField(slug_field='slug', queryset=Category.objects.all()) category = serializers.SlugRelatedField(slug_field='slug', queryset=Category.objects.all())
image = OrderImageListSerializer(source='main_images')
previewimage = serializers.ImageField(source='preview_image_url')
# TODO: choose from PromoCode table # image = Base64ImageField(source='images', many=True, queryset=Image.objects.all())
previewimage = serializers.ImageField(source='preview_image_url', read_only=True)
# TODO: choose from Promocode table
promo = serializers.CharField(source='promocode', required=False) promo = serializers.CharField(source='promocode', required=False)
currency = serializers.SerializerMethodField('get_yuan_rate') currency = serializers.SerializerMethodField('get_yuan_rate')
curencycurency2 = serializers.DecimalField(source='price_yuan', max_digits=10, decimal_places=2) curencycurency2 = serializers.DecimalField(source='price_yuan', max_digits=10, decimal_places=2)
currency3 = serializers.DecimalField(source='price_rub', max_digits=10, decimal_places=2, read_only=True) currency3 = serializers.DecimalField(source='price_rub', read_only=True, max_digits=10, decimal_places=2)
chinadelivery = serializers.SerializerMethodField('get_delivery_price_CN') chinadelivery = serializers.SerializerMethodField('get_delivery_price_CN', read_only=True)
chinadelivery2 = serializers.DecimalField(source='delivery_price_CN_RU', max_digits=10, decimal_places=2, read_only=True) chinadelivery2 = serializers.DecimalField(source='delivery_price_CN_RU', read_only=True, max_digits=10,
fullprice = serializers.DecimalField(source='full_price', max_digits=10, decimal_places=2) decimal_places=2)
realprice = serializers.DecimalField(source='real_price', max_digits=10, decimal_places=2) fullprice = serializers.DecimalField(source='full_price', read_only=True, max_digits=10, decimal_places=2)
comission = serializers.SerializerMethodField('get_comission') realprice = serializers.DecimalField(source='real_price', required=False, allow_null=True, max_digits=10,
decimal_places=2)
comission = serializers.SerializerMethodField('get_comission', read_only=True)
buyername = serializers.CharField(source='buyer_name') buyername = serializers.CharField(source='buyer_name', required=False, allow_null=True)
buyerphone = serializers.CharField(source='buyer_phone') buyerphone = serializers.CharField(source='buyer_phone', required=False, allow_null=True)
tg = serializers.CharField(source='buyer_telegram') tg = serializers.CharField(source='buyer_telegram', required=False, allow_null=True)
receivername = serializers.CharField(source='receiver_name') receivername = serializers.CharField(source='receiver_name', required=False, allow_null=True)
reveiverphone = serializers.CharField(source='receiver_phone') reveiverphone = serializers.CharField(source='receiver_phone', required=False, allow_null=True)
paymenttype = serializers.SlugRelatedField(source='payment_method', slug_field='slug', queryset=PaymentMethod.objects.all()) paymenttype = serializers.SlugRelatedField(source='payment_method', slug_field='slug',
paymentproovement = serializers.ImageField(source='payment_proof') queryset=PaymentMethod.objects.all(),
checkphoto = serializers.ImageField(source='cheque_photo') required=False, allow_null=True)
trackid = serializers.CharField(source='track_number') paymentproovement = serializers.ImageField(source='payment_proof', required=False, allow_null=True)
delivery = serializers.CharField(source='get_delivery_display') checkphoto = serializers.ImageField(source='cheque_photo', required=False, allow_null=True)
trackid = serializers.CharField(source='poizon_tracking', required=False, allow_null=True)
cdek_tracking = serializers.CharField(required=False, allow_null=True)
delivery = serializers.CharField(source='get_delivery_display', required=False, allow_null=True)
startDate = serializers.DateTimeField(source='created_at') startDate = serializers.DateTimeField(source='created_at', read_only=True)
currentDate = serializers.DateTimeField(source='status_updated_at') currentDate = serializers.DateTimeField(source='status_updated_at', read_only=True)
@staticmethod @staticmethod
def get_yuan_rate(obj: Checklist): def get_yuan_rate(obj: Checklist):
return GlobalSettings.load().yuan_rate return GlobalSettings.load().yuan_rate
@staticmethod
def get_image(obj: Checklist):
return obj.images.all()
@staticmethod @staticmethod
def get_delivery_price_CN(obj: Checklist): def get_delivery_price_CN(obj: Checklist):
return GlobalSettings.load().delivery_price_CN return GlobalSettings.load().delivery_price_CN
@ -110,7 +126,7 @@ class ChecklistSerializer(serializers.ModelSerializer):
fields = ('id', 'status', 'managerid', 'link', fields = ('id', 'status', 'managerid', 'link',
'category', 'subcategory', 'category', 'subcategory',
'brand', 'model', 'size', 'brand', 'model', 'size',
'image', # 'image',
'previewimage', 'previewimage',
'currency', 'curencycurency2', 'currency3', 'chinadelivery', 'chinadelivery2', 'comission', 'currency', 'curencycurency2', 'currency3', 'chinadelivery', 'chinadelivery2', 'comission',
'promo', 'promo',
@ -119,7 +135,7 @@ class ChecklistSerializer(serializers.ModelSerializer):
'buyername', 'buyerphone', 'tg', 'buyername', 'buyerphone', 'tg',
'receivername', 'reveiverphone', 'receivername', 'reveiverphone',
'paymenttype', 'paymentproovement', 'checkphoto', 'paymenttype', 'paymentproovement', 'checkphoto',
'trackid', 'delivery', 'trackid', 'cdek_tracking', 'delivery',
'startDate', 'currentDate', 'startDate', 'currentDate',
) )
@ -171,5 +187,5 @@ class PromocodeSerializer(serializers.ModelSerializer):
nocomission = serializers.BooleanField(source='no_comission') nocomission = serializers.BooleanField(source='no_comission')
class Meta: class Meta:
model = PromoCode model = Promocode
fields = ('name', 'discount', 'freedelivery', 'nocomission') fields = ('name', 'discount', 'freedelivery', 'nocomission')

View File

@ -7,6 +7,7 @@ router = DefaultRouter()
# FIXME: renamed # FIXME: renamed
router.register(r'statistics', views.StatisticsAPI, basename='statistics') router.register(r'statistics', views.StatisticsAPI, basename='statistics')
router.register(r'cdek', views.CDEKAPI, basename='cdek')
urlpatterns = [ urlpatterns = [
path("login/", views.LoginAPI.as_view()), path("login/", views.LoginAPI.as_view()),

View File

@ -1,20 +1,23 @@
import calendar import calendar
import json import json
from collections import OrderedDict, defaultdict
from django.conf import settings
from django.contrib.auth import login from django.contrib.auth import login
from django.db.models import F, Count, Q, Sum from django.db.models import F, Count, Q, Sum, Value, Subquery
from django.utils import timezone from django.utils import timezone
from rest_framework import generics, permissions, mixins, status, viewsets from rest_framework import generics, permissions, mixins, status, viewsets
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.generics import get_object_or_404 from rest_framework.generics import get_object_or_404
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response from rest_framework.response import Response
from cdek.api import CDEKClient
from store.exceptions import CRMException from store.exceptions import CRMException
from store.models import User, Checklist, GlobalSettings, Category, PaymentMethod, PromoCode from store.models import User, Checklist, GlobalSettings, Category, PaymentMethod, Promocode
from store.serializers import (UserSerializer, LoginSerializer, ChecklistSerializer, GlobalSettingsYuanRateSerializer, from store.serializers import (UserSerializer, LoginSerializer, ChecklistSerializer, GlobalSettingsYuanRateSerializer,
CategorySerializer, GlobalSettingsPriceSerializer, PaymentMethodSerializer, CategorySerializer, GlobalSettingsPriceSerializer, PaymentMethodSerializer,
PromocodeSerializer, GlobalSettingsPickupSerializer) PromocodeSerializer, GlobalSettingsPickupSerializer)
from utils.permissions import ReadOnly
class UserAPI(mixins.ListModelMixin, mixins.RetrieveModelMixin, generics.GenericAPIView): class UserAPI(mixins.ListModelMixin, mixins.RetrieveModelMixin, generics.GenericAPIView):
@ -53,8 +56,9 @@ class LoginAPI(generics.GenericAPIView):
return Response(serializer.data) return Response(serializer.data)
class ChecklistAPI(mixins.ListModelMixin, mixins.RetrieveModelMixin, generics.GenericAPIView): class ChecklistAPI(mixins.ListModelMixin, mixins.CreateModelMixin, mixins.RetrieveModelMixin, generics.GenericAPIView):
serializer_class = ChecklistSerializer serializer_class = ChecklistSerializer
permission_classes = [IsAuthenticated | ReadOnly]
lookup_field = 'id' lookup_field = 'id'
filterset_fields = ['status', ] filterset_fields = ['status', ]
search_fields = ['id', 'track_id', 'buyer_phone', 'full_price'] search_fields = ['id', 'track_id', 'buyer_phone', 'full_price']
@ -76,6 +80,9 @@ class ChecklistAPI(mixins.ListModelMixin, mixins.RetrieveModelMixin, generics.Ge
return obj return obj
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
if 'id' in kwargs: if 'id' in kwargs:
return self.retrieve(request, *args, **kwargs) return self.retrieve(request, *args, **kwargs)
@ -129,8 +136,7 @@ class CategoryAPI(generics.GenericAPIView):
'prices': GlobalSettingsPriceSerializer(global_settings).data, 'prices': GlobalSettingsPriceSerializer(global_settings).data,
}) })
# FIXME: use PATCH method for updates def patch(self, request, *args, **kwargs):
def post(self, request, *args, **kwargs):
data = json.loads(request.body) data = json.loads(request.body)
if not all(k in data for k in ("category", "chinarush")): if not all(k in data for k in ("category", "chinarush")):
raise CRMException('category and chinarush is required') raise CRMException('category and chinarush is required')
@ -146,8 +152,7 @@ class CategoryAPI(generics.GenericAPIView):
class PricesAPI(generics.GenericAPIView): class PricesAPI(generics.GenericAPIView):
serializer_class = GlobalSettingsPriceSerializer serializer_class = GlobalSettingsPriceSerializer
# FIXME: use PATCH method for updates def patch(self, request, *args, **kwargs):
def post(self, request, *args, **kwargs):
data = json.loads(request.body) data = json.loads(request.body)
instance = GlobalSettings.load() instance = GlobalSettings.load()
@ -160,6 +165,7 @@ class PricesAPI(generics.GenericAPIView):
class PickupAPI(generics.GenericAPIView): class PickupAPI(generics.GenericAPIView):
serializer_class = GlobalSettingsPickupSerializer serializer_class = GlobalSettingsPickupSerializer
permission_classes = [IsAuthenticated | ReadOnly]
def get_object(self): def get_object(self):
return GlobalSettings.load() return GlobalSettings.load()
@ -193,8 +199,7 @@ class PaymentMethodsAPI(generics.GenericAPIView):
return Response(data) return Response(data)
# FIXME: use PATCH method for updates def patch(self, request, *args, **kwargs):
def post(self, request, *args, **kwargs):
data = json.loads(request.body) data = json.loads(request.body)
if 'type' not in data: if 'type' not in data:
raise CRMException('type is required') raise CRMException('type is required')
@ -212,7 +217,7 @@ class PromoCodeAPI(mixins.CreateModelMixin, generics.GenericAPIView):
lookup_field = 'name' lookup_field = 'name'
def get_queryset(self): def get_queryset(self):
return PromoCode.objects.all() return Promocode.objects.all()
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
qs = self.get_queryset() qs = self.get_queryset()
@ -245,8 +250,6 @@ class StatisticsAPI(viewsets.GenericViewSet):
global_settings = GlobalSettings.load() global_settings = GlobalSettings.load()
yuan_rate = global_settings.yuan_rate yuan_rate = global_settings.yuan_rate
completed_filter = Q(status=Checklist.Status.COMPLETED)
# Prepare query to collect the stats # Prepare query to collect the stats
qs = self.get_queryset() \ qs = self.get_queryset() \
.annotate_price_rub() \ .annotate_price_rub() \
@ -307,51 +310,72 @@ class StatisticsAPI(viewsets.GenericViewSet):
return Response(result) return Response(result)
# TODO: implement stats_by_clients
@action(url_path='clients', detail=False, methods=['get']) @action(url_path='clients', detail=False, methods=['get'])
def stats_by_clients(self, request, *args, **kwargs): def stats_by_clients(self, request, *args, **kwargs):
def _create_stats(data: dict): options = {
return { "moreone": 1,
"moreone": data.get('moreone', 0), "moretwo": 2,
"moretwo": data.get('moretwo', 0), "morethree": 3,
"morethree": data.get('morethree', 0), "morefour": 4,
"morefour": data.get('morefour', 0), "morefive": 5,
"morefive": data.get('morefive', 0), "moreten": 10,
"moreten": data.get('moreten', 0), "moretwentyfive": 25,
"moretwentyfive": data.get('moretwentyfive', 0), "morefifty": 50,
"morefifty": data.get('morefifty', 0), }
}
def _filter_for_count(count): def _create_empty_stats():
return Count('buyer_phone', return {k: set() for k in options.keys()}
filter=Q(buyer_phone__in=Checklist.objects.values('buyer_phone')
.annotate(total_orders=Count('id'))
.filter(total_orders__gt=count)
.values('buyer_phone')
))
qs = self.get_queryset() \ qs = self.get_queryset() \
.values('month') \ .filter(buyer_phone__isnull=False) \
.annotate( .values('month', 'buyer_phone') \
moreone=_filter_for_count(1), .annotate(order_count=Count('id')) \
moretwo=_filter_for_count(2), .filter(order_count__gt=1) \
morethree=_filter_for_count(3), .order_by('month')
morefour=_filter_for_count(4),
morefive=_filter_for_count(5),
moreten=_filter_for_count(10),
moretwentyfive=_filter_for_count(25),
morefifty=_filter_for_count(50),
)
result = {} result = {}
# Add empty stats # Add empty stats
for i in range(1, 13): for i in range(1, 13):
month = calendar.month_name[i] month_name = calendar.month_name[i]
result[month] = _create_stats(dict()) result[month_name] = _create_empty_stats()
# Add actual stats # Add actual stats
for stat in qs: for stat in qs:
month = calendar.month_name[stat['month']] month_name = calendar.month_name[stat['month']]
result[month] = _create_stats(stat)
for key, size in reversed(options.items()):
if stat['order_count'] > size:
result[month_name][key].add(stat['buyer_phone'])
break
return Response(result) return Response(result)
class CDEKAPI(viewsets.GenericViewSet):
client = CDEKClient(settings.CDEK_CLIENT_ID, settings.CDEK_CLIENT_SECRET)
@action(url_path='orders', detail=False, methods=['get'])
def get_order_info(self, request, *args, **kwargs):
im_number = request.query_params.get('im_number')
if not im_number:
raise CRMException('im_number is required')
r = self.client.get_order_info(im_number)
return Response(r.json())
@action(url_path='orders', detail=False, methods=['post'])
def create_order(self, request, *args, **kwargs):
order_data = request.data
if not order_data:
raise CRMException('json data is required')
r = self.client.create_order(order_data)
return Response(r.json())
@action(url_path='calculator/tariff', detail=False, methods=['post'])
def calculate_tariff(self, request, *args, **kwargs):
data = request.data
if not data:
raise CRMException('json data is required')
r = self.client.calculate_tariff(data)
return Response(r.json())

View File

@ -1,8 +1,14 @@
from rest_framework import permissions from rest_framework import permissions
from rest_framework.authentication import SessionAuthentication from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import BasePermission, SAFE_METHODS
class CsrfExemptSessionAuthentication(SessionAuthentication): class CsrfExemptSessionAuthentication(SessionAuthentication):
def enforce_csrf(self, request): def enforce_csrf(self, request):
# To not perform the csrf check previously happening # To not perform the csrf check previously happening
return return
class ReadOnly(BasePermission):
def has_permission(self, request, view):
return request.method in SAFE_METHODS