Compare commits

...

6 Commits

6 changed files with 55 additions and 13 deletions

View File

@ -26,6 +26,12 @@ class UserSerializer(serializers.ModelSerializer):
return obj.invited_users_with_orders.count()
class UserSimpleSerializer(UserSerializer):
class Meta:
model = UserSerializer.Meta.model
fields = ('id', 'email', 'phone', 'role', 'name', 'lastname', 'surname')
def non_zero_validator(value):
if value == 0:
raise serializers.ValidationError("Value cannot be zero")

View File

@ -1,5 +1,6 @@
from functools import reduce
from django_filters import DateFromToRangeFilter as _DateFromToRangeFilter
from rest_framework.fields import DecimalField
@ -11,3 +12,11 @@ class PriceField(DecimalField):
def deep_get(dictionary, *keys, default=None):
"""Get value from a nested dictionary (JSON)"""
return reduce(lambda d, key: d.get(key, None) if isinstance(d, dict) else default, keys, dictionary)
class DateFromToRangeFilter(_DateFromToRangeFilter):
""" DateFromToRangeFilter with replaced after/before suffixes to from/to """
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.field.widget.suffixes = ['from', 'to']

View File

@ -1,5 +1,6 @@
from django_filters import rest_framework as filters
from poizonstore.utils import DateFromToRangeFilter
from .models import Checklist, Gift
@ -18,9 +19,12 @@ class ChecklistFilter(filters.FilterSet):
status = filters.MultipleChoiceFilter(choices=Checklist.Status.CHOICES)
delivery_code = filters.CharFilter(method='filter_delivery_code')
created_at = DateFromToRangeFilter()
status_updated_at = DateFromToRangeFilter()
class Meta:
model = Checklist
fields = ('status', 'delivery_code')
fields = ('status', 'delivery_code', 'created_at', 'status_updated_at')
def filter_delivery_code(self, queryset, name, value):
return queryset.filter(poizon_tracking__iendswith=value)

View File

@ -3,7 +3,7 @@ from drf_extra_fields.fields import Base64ImageField
from rest_framework import serializers
from bonus_program.models import BonusProgram
from account.serializers import UserSerializer
from account.serializers import UserSimpleSerializer
from utils.exceptions import CRMException
from store.models import Checklist, Category, PaymentMethod, Promocode, Image, Gift
from store.utils import get_primary_key_related_model
@ -94,7 +94,7 @@ class ChecklistSerializer(serializers.ModelSerializer):
commission_rub = PriceField(read_only=True)
customer = get_primary_key_related_model(UserSerializer, required=False, allow_null=True)
customer = get_primary_key_related_model(UserSimpleSerializer, required=False, allow_null=True)
receiver_name = serializers.CharField(required=False, allow_null=True)
receiver_phone = serializers.CharField(required=False, allow_null=True)
@ -193,6 +193,17 @@ class ChecklistSerializer(serializers.ModelSerializer):
)
class ChecklistListSerializer(ChecklistSerializer):
class Meta:
model = ChecklistSerializer.Meta.model
fields = ('id', 'status',
'category', 'brand', 'model', 'size', 'preview_image_url',
'price_yuan', 'price_rub', 'full_price', 'real_price',
'comment',
'poizon_tracking', 'cdek_tracking', 'delivery', 'delivery_display',
'created_at', 'status_updated_at')
class ClientChecklistSerializerMixin:
def validate(self, attrs):
gift = attrs.get('gift')

View File

@ -14,13 +14,13 @@ from rest_framework.response import Response
from external_api.cdek import CDEKClient, CDEKWebhookTypes, CDEK_STATUS_TO_ORDER_STATUS
from external_api.poizon import PoizonClient
from utils.exceptions import CRMException
from utils.exceptions import CRMException, Forbidden
from store.filters import GiftFilter, ChecklistFilter
from store.models import Checklist, Category, PaymentMethod, Promocode, Gift
from core.models import GlobalSettings
from store.serializers import (ChecklistSerializer, CategorySerializer, CategoryFullSerializer,
PaymentMethodSerializer, PromocodeSerializer, ClientUpdateChecklistSerializer,
GiftSerializer, ClientCreateChecklistSerializer)
GiftSerializer, ClientCreateChecklistSerializer, ChecklistListSerializer)
from account.permissions import ReadOnly, IsManager, IsAdmin
@ -63,7 +63,7 @@ class ChecklistAPI(viewsets.ModelViewSet):
super().permission_denied(request, **kwargs)
def get_serializer_class(self):
# Managers have a full set of fields
# Managers have a full set of fields for editing
if getattr(self.request.user, 'is_manager', False) or self.action == 'retrieve':
return ChecklistSerializer
@ -75,15 +75,17 @@ class ChecklistAPI(viewsets.ModelViewSet):
elif self.action in ['update', 'partial_update', 'destroy']:
return ClientUpdateChecklistSerializer
# Simplified list serializer
elif self.action == "list":
return ChecklistListSerializer
# Fallback to error
self.permission_denied(self.request, **self.kwargs)
def get_permissions(self):
if self.action in ['list', 'update', 'partial_update']:
self.permission_classes = [IsManager]
elif self.action == 'retrieve':
if self.action == 'retrieve':
self.permission_classes = [AllowAny]
elif self.action in ['create', 'destroy']:
elif self.action in ['create', 'list', 'update', 'partial_update', 'destroy']:
self.permission_classes = [IsAuthenticated]
return super().get_permissions()
@ -92,15 +94,21 @@ class ChecklistAPI(viewsets.ModelViewSet):
# Non-managers can cancel orders only in certain statuses
if (not getattr(self.request.user, 'is_manager', False)
and obj.status not in Checklist.Status.CANCELLABLE_ORDER_STATUSES):
raise CRMException("Can't delete the order")
raise Forbidden("Can't delete the order")
obj.cancel()
def get_queryset(self):
return Checklist.objects.with_base_related() \
qs = Checklist.objects.with_base_related() \
.annotate_bonus_used() \
.default_ordering()
# Non-managers can list only their own orders
if not getattr(self.request.user, 'is_manager', False):
qs = qs.filter(customer_id=self.request.user.id)
return qs
def get_object(self):
obj: Checklist = super().get_object()
@ -126,9 +134,10 @@ class CategoryAPI(mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.Updat
class PaymentMethodsAPI(mixins.ListModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet):
serializer_class = PaymentMethodSerializer
permission_classes = [IsManager | ReadOnly]
permission_classes = [IsAdmin | ReadOnly]
lookup_field = 'slug'
queryset = PaymentMethod.objects.all()
pagination_class = None
class PromoCodeAPI(viewsets.ModelViewSet):

View File

@ -12,4 +12,7 @@ class CRMException(APIException):
self.detail = {'error': detail}
class Forbidden(CRMException):
status_code = status.HTTP_403_FORBIDDEN
# TODO: exceptions with a same template: ok / error_code / error_message