1113 lines
46 KiB
Python
1113 lines
46 KiB
Python
import logging
|
|
import uuid
|
|
from uuid import UUID
|
|
|
|
from django.db.models import Q
|
|
from django.http import Http404
|
|
from django.shortcuts import get_object_or_404
|
|
from django.utils.decorators import method_decorator
|
|
from django.utils.translation import gettext_lazy as _
|
|
from django_filters.rest_framework import DjangoFilterBackend
|
|
from django_ratelimit.decorators import ratelimit
|
|
from djangorestframework_camel_case.render import CamelCaseJSONRenderer
|
|
from drf_spectacular.utils import extend_schema_view
|
|
from rest_framework import status
|
|
from rest_framework.decorators import action
|
|
from rest_framework.exceptions import PermissionDenied
|
|
from rest_framework.renderers import MultiPartRenderer
|
|
from rest_framework.response import Response
|
|
from rest_framework.viewsets import ModelViewSet
|
|
from rest_framework_xml.renderers import XMLRenderer
|
|
from rest_framework_yaml.renderers import YAMLRenderer
|
|
|
|
from core.docs.drf.viewsets import (
|
|
ADDRESS_SCHEMA,
|
|
ATTRIBUTE_GROUP_SCHEMA,
|
|
ATTRIBUTE_SCHEMA,
|
|
ATTRIBUTE_VALUE_SCHEMA,
|
|
CATEGORY_SCHEMA,
|
|
FEEDBACK_SCHEMA,
|
|
ORDER_PRODUCT_SCHEMA,
|
|
ORDER_SCHEMA,
|
|
PRODUCT_SCHEMA,
|
|
WISHLIST_SCHEMA,
|
|
)
|
|
from core.filters import AddressFilter, BrandFilter, CategoryFilter, FeedbackFilter, OrderFilter, ProductFilter
|
|
from core.models import (
|
|
Address,
|
|
Attribute,
|
|
AttributeGroup,
|
|
AttributeValue,
|
|
Brand,
|
|
Category,
|
|
Feedback,
|
|
Order,
|
|
OrderProduct,
|
|
Product,
|
|
ProductImage,
|
|
ProductTag,
|
|
PromoCode,
|
|
Promotion,
|
|
Stock,
|
|
Vendor,
|
|
Wishlist,
|
|
)
|
|
from core.permissions import EvibesPermission
|
|
from core.serializers import (
|
|
AddOrderProductSerializer,
|
|
AddressAutocompleteInputSerializer,
|
|
AddressCreateSerializer,
|
|
AddressSerializer,
|
|
AddressSuggestionSerializer,
|
|
AddWishlistProductSerializer,
|
|
AttributeDetailSerializer,
|
|
AttributeGroupDetailSerializer,
|
|
AttributeGroupSimpleSerializer,
|
|
AttributeSimpleSerializer,
|
|
AttributeValueDetailSerializer,
|
|
AttributeValueSimpleSerializer,
|
|
BrandDetailSerializer,
|
|
BrandSimpleSerializer,
|
|
BulkAddOrderProductsSerializer,
|
|
BulkAddWishlistProductSerializer,
|
|
BulkRemoveOrderProductsSerializer,
|
|
BulkRemoveWishlistProductSerializer,
|
|
BuyOrderSerializer,
|
|
BuyUnregisteredOrderSerializer,
|
|
CategoryDetailSerializer,
|
|
CategorySimpleSerializer,
|
|
DoFeedbackSerializer,
|
|
FeedbackDetailSerializer,
|
|
FeedbackSimpleSerializer,
|
|
OrderDetailSerializer,
|
|
OrderProductSimpleSerializer,
|
|
OrderSimpleSerializer,
|
|
ProductDetailSerializer,
|
|
ProductImageDetailSerializer,
|
|
ProductImageSimpleSerializer,
|
|
ProductSimpleSerializer,
|
|
ProductTagDetailSerializer,
|
|
ProductTagSimpleSerializer,
|
|
PromoCodeDetailSerializer,
|
|
PromoCodeSimpleSerializer,
|
|
PromotionDetailSerializer,
|
|
PromotionSimpleSerializer,
|
|
RemoveOrderProductSerializer,
|
|
RemoveWishlistProductSerializer,
|
|
StockDetailSerializer,
|
|
StockSimpleSerializer,
|
|
VendorDetailSerializer,
|
|
VendorSimpleSerializer,
|
|
WishlistDetailSerializer,
|
|
WishlistSimpleSerializer,
|
|
)
|
|
from core.utils import format_attributes
|
|
from core.utils.messages import permission_denied_message
|
|
from core.utils.nominatim import fetch_address_suggestions
|
|
from evibes.settings import DEBUG
|
|
from payments.serializers import TransactionProcessSerializer
|
|
|
|
logger = logging.getLogger("evibes")
|
|
|
|
|
|
class EvibesViewSet(ModelViewSet):
|
|
"""
|
|
Defines a viewset for managing Evibes-related operations.
|
|
|
|
The EvibesViewSet class inherits from ModelViewSet and provides functionality
|
|
for handling actions and operations on Evibes entities. It includes support
|
|
for dynamic serializer classes based on the current action, customizable
|
|
permissions, and rendering formats.
|
|
|
|
Attributes:
|
|
action_serializer_classes: Dictionary mapping action names to their specific
|
|
serializer classes.
|
|
additional: Dictionary to hold additional data related to the view.
|
|
permission_classes: List of permission classes applicable to this viewset.
|
|
renderer_classes: List of renderer classes supported for response formatting.
|
|
|
|
Methods:
|
|
get_serializer_class(self):
|
|
Returns the serializer class for the current action or the default
|
|
serializer class from the parent ModelViewSet.
|
|
"""
|
|
|
|
action_serializer_classes: dict = {}
|
|
additional: dict = {}
|
|
permission_classes = [EvibesPermission]
|
|
renderer_classes = [CamelCaseJSONRenderer, MultiPartRenderer, XMLRenderer, YAMLRenderer]
|
|
|
|
def get_serializer_class(self):
|
|
return self.action_serializer_classes.get(self.action, super().get_serializer_class())
|
|
|
|
|
|
@extend_schema_view(**ATTRIBUTE_GROUP_SCHEMA)
|
|
class AttributeGroupViewSet(EvibesViewSet):
|
|
"""
|
|
Represents a viewset for managing AttributeGroup objects.
|
|
|
|
Handles operations related to AttributeGroup, including filtering,
|
|
serialization, and retrieval of data. This class is part of the
|
|
application's API layer and provides a standardized way to process
|
|
requests and responses for AttributeGroup data.
|
|
|
|
Attributes:
|
|
queryset (QuerySet): QuerySet for retrieving all AttributeGroup objects.
|
|
filter_backends (list): List of filter backends used to process filters
|
|
in requests.
|
|
filterset_fields (list): List of fields on which filtering operations
|
|
can be performed.
|
|
serializer_class (Serializer): Default serializer class used for
|
|
processing AttributeGroup data during non-list view operations.
|
|
action_serializer_classes (dict): Mapping of view actions to their
|
|
specific serializer classes, allowing customization of serialization
|
|
behavior for certain actions.
|
|
"""
|
|
|
|
queryset = AttributeGroup.objects.all()
|
|
filter_backends = [DjangoFilterBackend]
|
|
filterset_fields = ["is_active"]
|
|
serializer_class = AttributeGroupDetailSerializer
|
|
action_serializer_classes = {
|
|
"list": AttributeGroupSimpleSerializer,
|
|
}
|
|
|
|
|
|
@extend_schema_view(**ATTRIBUTE_SCHEMA)
|
|
class AttributeViewSet(EvibesViewSet):
|
|
"""
|
|
Handles operations related to Attribute objects within the application.
|
|
|
|
Provides a set of API endpoints to interact with Attribute data. This class
|
|
manages querying, filtering, and serialization of Attribute objects, allowing
|
|
dynamic control over the data returned, such as filtering by specific fields
|
|
or retrieving detailed versus simplified information depending on the request.
|
|
|
|
Attributes:
|
|
queryset: The base QuerySet used to represent the set of Attribute
|
|
objects available to this viewset.
|
|
filter_backends: Defines the backends used for filtering request data,
|
|
enabling query flexibility.
|
|
filterset_fields: A list of model fields that can be filtered via the API.
|
|
serializer_class: Represents the serializer used by default for
|
|
serialization and deserialization of Attribute data.
|
|
action_serializer_classes: A mapping that defines serializers used for
|
|
specific actions, such as returning less detailed data for a `list`
|
|
action.
|
|
"""
|
|
|
|
queryset = Attribute.objects.all()
|
|
filter_backends = [DjangoFilterBackend]
|
|
filterset_fields = ["group", "value_type", "is_active"]
|
|
serializer_class = AttributeDetailSerializer
|
|
action_serializer_classes = {
|
|
"list": AttributeSimpleSerializer,
|
|
}
|
|
|
|
|
|
@extend_schema_view(**ATTRIBUTE_VALUE_SCHEMA)
|
|
class AttributeValueViewSet(EvibesViewSet):
|
|
"""
|
|
A viewset for managing AttributeValue objects.
|
|
|
|
This viewset provides functionality for listing, retrieving, creating, updating, and deleting
|
|
AttributeValue objects. It integrates with Django REST Framework's viewset mechanisms and uses
|
|
appropriate serializers for different actions. Filtering capabilities are provided through the
|
|
DjangoFilterBackend.
|
|
|
|
Attributes:
|
|
queryset (QuerySet): The base queryset for AttributeValue objects.
|
|
filter_backends (list): A list of filtering backends applied to the viewset.
|
|
filterset_fields (list): Fields of the model that can be used for filtering.
|
|
serializer_class (Serializer): The default serializer class for the viewset.
|
|
action_serializer_classes (dict): A dictionary mapping action names to their corresponding
|
|
serializer classes.
|
|
"""
|
|
|
|
queryset = AttributeValue.objects.all()
|
|
filter_backends = [DjangoFilterBackend]
|
|
filterset_fields = ["attribute", "is_active"]
|
|
serializer_class = AttributeValueDetailSerializer
|
|
action_serializer_classes = {
|
|
"list": AttributeValueSimpleSerializer,
|
|
}
|
|
|
|
|
|
@extend_schema_view(**CATEGORY_SCHEMA)
|
|
class CategoryViewSet(EvibesViewSet):
|
|
"""
|
|
Manages views for Category-related operations.
|
|
|
|
The CategoryViewSet class is responsible for handling operations related to
|
|
the Category model in the system. It supports retrieving, filtering, and
|
|
serializing category data. The viewset also enforces permissions to ensure
|
|
that only authorized users can access specific data.
|
|
|
|
Attributes:
|
|
queryset: The base queryset used to retrieve category data, including
|
|
prefetching related objects such as parents, children, attributes,
|
|
and tags.
|
|
filter_backends: A list of backends for applying filters to the category
|
|
data.
|
|
filterset_class: The filter class used to define filtering behavior for
|
|
the category queryset.
|
|
serializer_class: The default serializer class used for category objects
|
|
when no specific action serializer is applied.
|
|
action_serializer_classes: A dictionary mapping specific viewset actions
|
|
(e.g., "list") to their corresponding serializer classes.
|
|
|
|
Methods:
|
|
get_queryset():
|
|
Retrieves the queryset for the viewset, applying permission checks
|
|
and filtering out inactive categories for users without sufficient
|
|
permissions.
|
|
"""
|
|
|
|
queryset = Category.objects.all().prefetch_related("parent", "children", "attributes", "tags")
|
|
filter_backends = [DjangoFilterBackend]
|
|
filterset_class = CategoryFilter
|
|
serializer_class = CategoryDetailSerializer
|
|
action_serializer_classes = {
|
|
"list": CategorySimpleSerializer,
|
|
}
|
|
|
|
def get_queryset(self):
|
|
qs = super().get_queryset()
|
|
if self.request.user.has_perm("core.view_category"):
|
|
return qs
|
|
return qs.filter(is_active=True)
|
|
|
|
|
|
class BrandViewSet(EvibesViewSet):
|
|
"""
|
|
Represents a viewset for managing Brand instances.
|
|
|
|
This class provides functionality for querying, filtering, and
|
|
serializing Brand objects. It uses Django's ViewSet framework
|
|
to simplify the implementation of API endpoints for Brand objects.
|
|
|
|
Attributes:
|
|
queryset: The base queryset containing all Brand instances.
|
|
filter_backends: A list of filtering backends to apply to the
|
|
queryset. The default is [DjangoFilterBackend].
|
|
filterset_class: The filter class used to define the filtering
|
|
logic for this viewset. The default is BrandFilter.
|
|
serializer_class: The default serializer class used for the
|
|
detailed representation of Brand objects. The default is
|
|
BrandDetailSerializer.
|
|
action_serializer_classes: A dictionary mapping specific actions
|
|
to their corresponding serializer classes. The "list" action
|
|
uses the BrandSimpleSerializer class.
|
|
"""
|
|
|
|
queryset = Brand.objects.all()
|
|
filter_backends = [DjangoFilterBackend]
|
|
filterset_class = BrandFilter
|
|
serializer_class = BrandDetailSerializer
|
|
action_serializer_classes = {
|
|
"list": BrandSimpleSerializer,
|
|
}
|
|
|
|
|
|
@extend_schema_view(**PRODUCT_SCHEMA)
|
|
class ProductViewSet(EvibesViewSet):
|
|
"""
|
|
Manages operations related to the `Product` model in the system.
|
|
|
|
This class provides a viewset for managing products, including their filtering, serialization,
|
|
and operations on specific instances. It extends from `EvibesViewSet` to use common
|
|
functionality and integrates with the Django REST framework for RESTful API operations.
|
|
Includes methods for retrieving product details, applying permissions, and accessing
|
|
related feedback of a product.
|
|
|
|
Attributes:
|
|
queryset: The base queryset to retrieve `Product` objects with prefetch optimization.
|
|
filter_backends: Specifies the filtering mechanism for the list views.
|
|
filterset_class: Defines the filter class to be used for filtering products.
|
|
serializer_class: The default serializer class for product details.
|
|
action_serializer_classes: Specific serializer mappings for action methods.
|
|
lookup_field: Field representing the object's lookup value in URLs.
|
|
lookup_url_kwarg: Field key used to extract the object's lookup value from URL.
|
|
|
|
Methods:
|
|
get_queryset: Retrieves the queryset with user-specific filtering applied.
|
|
get_object: Fetches a single object based on its identifier, applying permissions.
|
|
feedbacks: Fetches feedback associated with a specific product.
|
|
"""
|
|
|
|
queryset = Product.objects.prefetch_related("tags", "attributes", "stocks", "images").all()
|
|
filter_backends = [DjangoFilterBackend]
|
|
filterset_class = ProductFilter
|
|
serializer_class = ProductDetailSerializer
|
|
action_serializer_classes = {
|
|
"list": ProductSimpleSerializer,
|
|
}
|
|
lookup_field = "lookup_value"
|
|
lookup_url_kwarg = "lookup_value"
|
|
|
|
def get_queryset(self):
|
|
qs = super().get_queryset()
|
|
if self.request.user.has_perm("core.view_product"):
|
|
return qs
|
|
return qs.filter(is_active=True)
|
|
|
|
def get_object(self):
|
|
queryset = self.filter_queryset(self.get_queryset())
|
|
lookup_value = self.kwargs[self.lookup_url_kwarg]
|
|
|
|
obj = None
|
|
|
|
try:
|
|
uuid_obj = UUID(lookup_value)
|
|
obj = queryset.filter(uuid=uuid_obj).first()
|
|
except (ValueError, TypeError):
|
|
pass
|
|
|
|
if not obj:
|
|
obj = queryset.filter(slug=lookup_value).first()
|
|
|
|
if not obj:
|
|
name = "Product"
|
|
raise Http404(f"{name} does not exist: {lookup_value}")
|
|
|
|
self.check_object_permissions(self.request, obj)
|
|
return obj
|
|
|
|
@action(detail=True, methods=["get"], url_path="feedbacks")
|
|
def feedbacks(self, request, **kwargs):
|
|
lookup_val = kwargs.get(self.lookup_field)
|
|
try:
|
|
product = Product.objects.get(uuid=lookup_val)
|
|
feedbacks = (
|
|
Feedback.objects.filter(order_product__product=product)
|
|
if request.user.has_perm("core.view_feedback")
|
|
else Feedback.objects.filter(order_product__product=product, is_active=True)
|
|
)
|
|
# noinspection PyTypeChecker
|
|
return Response(data=FeedbackDetailSerializer(feedbacks, many=True).data)
|
|
except Product.DoesNotExist:
|
|
name = "Product"
|
|
return Response(status=status.HTTP_404_NOT_FOUND, data={"detail": _(f"{name} does not exist: {uuid}")})
|
|
|
|
|
|
class VendorViewSet(EvibesViewSet):
|
|
"""
|
|
Represents a viewset for managing Vendor objects.
|
|
|
|
This viewset allows fetching, filtering, and serializing Vendor data.
|
|
It defines the queryset, filter configurations, and serializer classes
|
|
used to handle different actions. The purpose of this class is to
|
|
provide streamlined access to Vendor-related resources through the
|
|
Django REST framework.
|
|
|
|
Attributes:
|
|
queryset: A QuerySet containing all Vendor objects.
|
|
filter_backends: A list containing configured filter backends.
|
|
filterset_fields: A list of fields that can be used for filtering
|
|
Vendor records.
|
|
serializer_class: The default serializer class used for this
|
|
viewset.
|
|
action_serializer_classes: A dictionary mapping specific actions
|
|
(e.g., "list") to custom serializer classes for those actions.
|
|
"""
|
|
|
|
queryset = Vendor.objects.all()
|
|
filter_backends = [DjangoFilterBackend]
|
|
filterset_fields = ["name", "markup_percent", "is_active"]
|
|
serializer_class = VendorDetailSerializer
|
|
action_serializer_classes = {
|
|
"list": VendorSimpleSerializer,
|
|
}
|
|
|
|
|
|
@extend_schema_view(**FEEDBACK_SCHEMA)
|
|
class FeedbackViewSet(EvibesViewSet):
|
|
"""
|
|
Representation of a view set handling Feedback objects.
|
|
|
|
This class manages operations related to Feedback objects, including listing,
|
|
filtering, and retrieving details. The purpose of this view set is to provide
|
|
different serializers for different actions and implement permission-based
|
|
handling of accessible Feedback objects. It extends the base `EvibesViewSet`
|
|
and makes use of Django's filtering system for querying data.
|
|
|
|
Attributes:
|
|
queryset: The base queryset for Feedback objects used in this view set.
|
|
filter_backends: List of filter backends to apply, specifically
|
|
`DjangoFilterBackend` for this view set.
|
|
filterset_class: Class specifying the filter set used for querying
|
|
Feedback objects.
|
|
serializer_class: Default serializer class used for this view set.
|
|
action_serializer_classes: A dictionary mapping action names to specific
|
|
serializer classes. For example, the "list" action uses
|
|
`FeedbackSimpleSerializer`.
|
|
"""
|
|
|
|
queryset = Feedback.objects.all()
|
|
filter_backends = [DjangoFilterBackend]
|
|
filterset_class = FeedbackFilter
|
|
serializer_class = FeedbackDetailSerializer
|
|
action_serializer_classes = {
|
|
"list": FeedbackSimpleSerializer,
|
|
}
|
|
|
|
def get_queryset(self):
|
|
qs = super().get_queryset()
|
|
if self.request.user.has_perm("core.view_feedback"):
|
|
return qs
|
|
return qs.filter(is_active=True)
|
|
|
|
|
|
@extend_schema_view(**ORDER_SCHEMA)
|
|
class OrderViewSet(EvibesViewSet):
|
|
"""
|
|
ViewSet for managing orders and related operations.
|
|
|
|
This class provides functionality to retrieve, modify, and manage order objects.
|
|
It includes various endpoints for handling order operations such as adding or
|
|
removing products, performing purchases for registered as well as unregistered
|
|
users, and retrieving the current authenticated user's pending orders.
|
|
|
|
The ViewSet uses multiple serializers based on the specific action being
|
|
performed and enforces permissions accordingly while interacting with order data.
|
|
|
|
Attributes:
|
|
lookup_field (str): Field name used for performing object lookup.
|
|
lookup_url_kwarg (str): URL keyword argument used for object lookup. Defaults
|
|
to `lookup_field`.
|
|
queryset (QuerySet): Default queryset for retrieving order objects, with
|
|
prefetched related order products.
|
|
filter_backends (list): List of backends applied for filtering the queryset.
|
|
filterset_class (type): Filtering class applied to the queryset for request-based
|
|
customizations.
|
|
serializer_class (type): Default serializer used if no specific serializer is
|
|
defined for an action.
|
|
action_serializer_classes (dict): Mapping of actions to their respective serializers.
|
|
Used to determine the serializer dynamically based on the requested action.
|
|
additional (dict): Additional settings for specific actions.
|
|
|
|
Methods:
|
|
get_serializer_class: Returns the serializer class based on the specific
|
|
action being requested.
|
|
get_queryset: Adjusts the queryset based on the request user's permissions,
|
|
favoring anonymous or limited query access for unauthenticated users.
|
|
get_object: Retrieves a specific order object based on the lookup value, either
|
|
its UUID or a human-readable ID.
|
|
current: Retrieves the authenticated user's current pending order.
|
|
buy: Processes an order purchase for an authenticated user with optional parameters
|
|
such as balance and payment overrides, promocodes, and billing/shipping addresses.
|
|
buy_unregistered: Processes an order purchase for unauthenticated users with product,
|
|
customer details, and payment information.
|
|
add_order_product: Adds a product, with optional attributes, to an order specified by UUID.
|
|
remove_order_product: Removes a product, with optional attributes, from an order specified
|
|
by UUID.
|
|
bulk_add_order_products: Adds multiple products with optional attributes to an order.
|
|
bulk_remove_order_products: Removes multiple products with optional attributes from
|
|
an order.
|
|
"""
|
|
|
|
lookup_field = "lookup_value"
|
|
lookup_url_kwarg = "lookup_value"
|
|
queryset = Order.objects.prefetch_related("order_products").all()
|
|
filter_backends = [DjangoFilterBackend]
|
|
filterset_class = OrderFilter
|
|
serializer_class = OrderDetailSerializer
|
|
action_serializer_classes = {
|
|
"list": OrderSimpleSerializer,
|
|
"buy": OrderDetailSerializer,
|
|
"add_order_product": AddOrderProductSerializer,
|
|
"remove_order_product": RemoveOrderProductSerializer,
|
|
}
|
|
additional = {"retrieve": "ALLOW"}
|
|
|
|
def get_serializer_class(self):
|
|
return self.action_serializer_classes.get(self.action, super().get_serializer_class())
|
|
|
|
def get_queryset(self):
|
|
qs = super().get_queryset()
|
|
user = self.request.user
|
|
|
|
if not user.is_authenticated:
|
|
return qs.filter(user__isnull=True)
|
|
|
|
if user.has_perm("core.view_order"):
|
|
return qs
|
|
|
|
return qs.filter(user=user)
|
|
|
|
def get_object(self):
|
|
lookup_val = self.kwargs[self.lookup_field]
|
|
qs = self.get_queryset()
|
|
|
|
try:
|
|
uuid.UUID(lookup_val)
|
|
uuid_q = Q(uuid=lookup_val)
|
|
except ValueError:
|
|
uuid_q = Q()
|
|
|
|
obj = get_object_or_404(qs, uuid_q | Q(human_readable_id=lookup_val))
|
|
self.check_object_permissions(self.request, obj)
|
|
return obj
|
|
|
|
@action(detail=False, methods=["get"], url_path="current")
|
|
def current(self, request):
|
|
if not request.user.is_authenticated:
|
|
raise PermissionDenied(permission_denied_message)
|
|
order = Order.objects.get(user=request.user, status="PENDING")
|
|
return Response(
|
|
status=status.HTTP_200_OK,
|
|
data=OrderDetailSerializer(order).data,
|
|
)
|
|
|
|
@action(detail=True, methods=["post"], url_path="buy")
|
|
def buy(self, request, **kwargs):
|
|
serializer = BuyOrderSerializer(data=request.data)
|
|
serializer.is_valid(raise_exception=True)
|
|
lookup_val = kwargs.get(self.lookup_field)
|
|
try:
|
|
order = Order.objects.get(user=request.user, uuid=lookup_val)
|
|
instance = order.buy(
|
|
force_balance=serializer.validated_data.get("force_balance"),
|
|
force_payment=serializer.validated_data.get("force_payment"),
|
|
promocode_uuid=serializer.validated_data.get("promocode_uuid"),
|
|
shipping_address=serializer.validated_data.get("shipping_address_uuid"),
|
|
billing_address=serializer.validated_data.get("billing_address_uuid"),
|
|
)
|
|
match str(type(instance)):
|
|
case "<class 'payments.models.Transaction'>":
|
|
return Response(status=status.HTTP_202_ACCEPTED, data=TransactionProcessSerializer(instance).data)
|
|
case "<class 'core.models.Order'>":
|
|
return Response(status=status.HTTP_200_OK, data=OrderDetailSerializer(instance).data)
|
|
case _:
|
|
raise TypeError(_(f"wrong type came from order.buy() method: {type(instance)!s}"))
|
|
except Order.DoesNotExist:
|
|
name = "Order"
|
|
return Response(status=status.HTTP_404_NOT_FOUND, data={"detail": _(f"{name} does not exist: {uuid}")})
|
|
|
|
@action(detail=False, methods=["post"], url_path="buy_unregistered")
|
|
@method_decorator(ratelimit(key="ip", rate="5/h" if not DEBUG else "888/h"))
|
|
def buy_unregistered(self, request):
|
|
serializer = BuyUnregisteredOrderSerializer(data=request.data)
|
|
serializer.is_valid(raise_exception=True)
|
|
order = Order.objects.create(status="MOMENTAL")
|
|
products = [p["product_uuid"] for p in serializer.validated_data["products"]]
|
|
transaction = order.buy_without_registration(
|
|
products=products,
|
|
promocode_uuid=serializer.validated_data.get("promocode_uuid"),
|
|
customer_name=serializer.validated_data.get("customer_name"),
|
|
customer_email=serializer.validated_data.get("customer_email"),
|
|
customer_phone_number=serializer.validated_data.get("customer_phone_number"),
|
|
billing_customer_address=serializer.validated_data.get("billing_customer_address_uuid"),
|
|
shipping_customer_address=serializer.validated_data.get("shipping_customer_address_uuid"),
|
|
payment_method=serializer.validated_data.get("payment_method"),
|
|
)
|
|
return Response(status=status.HTTP_202_ACCEPTED, data=TransactionProcessSerializer(transaction).data)
|
|
|
|
@action(detail=True, methods=["post"], url_path="add_order_product")
|
|
def add_order_product(self, request, **kwargs):
|
|
serializer = AddOrderProductSerializer(data=request.data)
|
|
serializer.is_valid(raise_exception=True)
|
|
lookup_val = kwargs.get(self.lookup_field)
|
|
try:
|
|
order = Order.objects.get(uuid=lookup_val)
|
|
if not (request.user.has_perm("core.add_orderproduct") or request.user == order.user):
|
|
raise PermissionDenied(permission_denied_message)
|
|
|
|
order = order.add_product(
|
|
product_uuid=serializer.validated_data.get("product_uuid"),
|
|
attributes=format_attributes(serializer.validated_data.get("attributes")),
|
|
)
|
|
return Response(status=status.HTTP_200_OK, data=OrderDetailSerializer(order).data)
|
|
except Order.DoesNotExist:
|
|
return Response(status=status.HTTP_404_NOT_FOUND)
|
|
|
|
@action(detail=True, methods=["post"], url_path="remove_order_product")
|
|
def remove_order_product(self, request, **kwargs):
|
|
serializer = RemoveOrderProductSerializer(data=request.data)
|
|
serializer.is_valid(raise_exception=True)
|
|
lookup_val = kwargs.get(self.lookup_field)
|
|
try:
|
|
order = Order.objects.get(uuid=lookup_val)
|
|
if not (request.user.has_perm("core.delete_orderproduct") or request.user == order.user):
|
|
raise PermissionDenied(permission_denied_message)
|
|
|
|
order = order.remove_product(
|
|
product_uuid=serializer.validated_data.get("product_uuid"),
|
|
attributes=format_attributes(serializer.validated_data.get("attributes")),
|
|
)
|
|
return Response(status=status.HTTP_200_OK, data=OrderDetailSerializer(order).data)
|
|
except Order.DoesNotExist:
|
|
return Response(status=status.HTTP_404_NOT_FOUND)
|
|
|
|
@action(detail=True, methods=["post"], url_path="bulk_add_order_products")
|
|
def bulk_add_order_products(self, request, **kwargs):
|
|
serializer = BulkAddOrderProductsSerializer(data=request.data)
|
|
serializer.is_valid(raise_exception=True)
|
|
lookup_val = kwargs.get(self.lookup_field)
|
|
try:
|
|
order = Order.objects.get(uuid=lookup_val)
|
|
if not (request.user.has_perm("core.add_orderproduct") or request.user == order.user):
|
|
raise PermissionDenied(permission_denied_message)
|
|
|
|
order = order.bulk_add_products(
|
|
products=serializer.validated_data.get("products"),
|
|
)
|
|
return Response(status=status.HTTP_200_OK, data=OrderDetailSerializer(order).data)
|
|
except Order.DoesNotExist:
|
|
return Response(status=status.HTTP_404_NOT_FOUND)
|
|
|
|
@action(detail=True, methods=["post"], url_path="bulk_remove_order_products")
|
|
def bulk_remove_order_products(self, request, **kwargs):
|
|
serializer = BulkRemoveOrderProductsSerializer(data=request.data)
|
|
serializer.is_valid(raise_exception=True)
|
|
lookup_val = kwargs.get(self.lookup_field)
|
|
try:
|
|
order = Order.objects.get(uuid=lookup_val)
|
|
if not (request.user.has_perm("core.delete_orderproduct") or request.user == order.user):
|
|
raise PermissionDenied(permission_denied_message)
|
|
|
|
order = order.bulk_remove_products(
|
|
products=serializer.validated_data.get("products"),
|
|
)
|
|
return Response(status=status.HTTP_200_OK, data=OrderDetailSerializer(order).data)
|
|
except Order.DoesNotExist:
|
|
return Response(status=status.HTTP_404_NOT_FOUND)
|
|
|
|
|
|
@extend_schema_view(**ORDER_PRODUCT_SCHEMA)
|
|
class OrderProductViewSet(EvibesViewSet):
|
|
"""
|
|
Provides a viewset for managing OrderProduct entities.
|
|
|
|
This viewset enables CRUD operations and custom actions specific to the
|
|
OrderProduct model. It includes filtering, permission checks, and
|
|
serializer switching based on the requested action. Additionally, it
|
|
provides a detailed action for handling feedback on OrderProduct
|
|
instances.
|
|
|
|
Attributes:
|
|
queryset (QuerySet): The base queryset for OrderProduct objects.
|
|
filter_backends (list): Backends responsible for handling filtering
|
|
mechanisms.
|
|
filterset_fields (list[str]): Fields available for API filtering.
|
|
serializer_class (Serializer): Default serializer class for CRUD
|
|
operations.
|
|
action_serializer_classes (dict[str, Serializer]): Mapping of
|
|
specific actions to their corresponding serializer classes.
|
|
|
|
Methods:
|
|
get_queryset: Overrides the default queryset to enforce user
|
|
permissions.
|
|
|
|
Actions:
|
|
do_feedback: Custom action to add, remove, or manage feedback for
|
|
an OrderProduct instance.
|
|
"""
|
|
|
|
queryset = OrderProduct.objects.all()
|
|
filter_backends = [DjangoFilterBackend]
|
|
filterset_fields = ["order", "product", "status", "is_active"]
|
|
serializer_class = AttributeGroupDetailSerializer
|
|
action_serializer_classes = {
|
|
"list": OrderProductSimpleSerializer,
|
|
"do_feedback": DoFeedbackSerializer,
|
|
}
|
|
|
|
def get_queryset(self):
|
|
qs = super().get_queryset()
|
|
user = self.request.user
|
|
|
|
if user.has_perm("core.view_orderproduct"):
|
|
return qs
|
|
|
|
return qs.filter(user=user)
|
|
|
|
@action(detail=True, methods=["post"], url_path="do_feedback")
|
|
def do_feedback(self, request, **kwargs):
|
|
serializer = self.get_serializer(request.data)
|
|
serializer.is_valid(raise_exception=True)
|
|
try:
|
|
order_product = OrderProduct.objects.get(uuid=kwargs.get("pk"))
|
|
if not (request.user.has_perm("core.change_orderproduct") or request.user == order_product.order.user):
|
|
raise PermissionDenied(permission_denied_message)
|
|
feedback = order_product.do_feedback(
|
|
rating=serializer.validated_data.get("rating"),
|
|
comment=serializer.validated_data.get("comment"),
|
|
action=serializer.validated_data.get("action"),
|
|
)
|
|
match serializer.validated_data.get("action"):
|
|
case "add":
|
|
return Response(data=FeedbackDetailSerializer(feedback).data, status=status.HTTP_201_CREATED)
|
|
case "remove":
|
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
|
case _:
|
|
return Response(status=status.HTTP_400_BAD_REQUEST)
|
|
except OrderProduct.DoesNotExist:
|
|
return Response(status=status.HTTP_404_NOT_FOUND)
|
|
|
|
|
|
class ProductImageViewSet(EvibesViewSet):
|
|
"""
|
|
Manages operations related to Product images in the application.
|
|
|
|
This class-based view set provides endpoints to manage and access ProductImage
|
|
objects. It supports filtering, serialization, and customized serializers for
|
|
different actions to handle ProductImage data.
|
|
|
|
Attributes:
|
|
queryset (QuerySet): A Django QuerySet consisting of all ProductImage
|
|
instances within the system.
|
|
filter_backends (list): A list of filter backends that determine the
|
|
filtering behavior on querysets. Set to [DjangoFilterBackend].
|
|
filterset_fields (list): Fields that can be used for filtering data.
|
|
Includes "product", "priority", and "is_active".
|
|
serializer_class (Serializer): The default serializer class used for
|
|
serializing and deserializing ProductImage data. Set to
|
|
ProductImageDetailSerializer.
|
|
action_serializer_classes (dict): A mapping of action names to specific
|
|
serializer classes. For the "list" action, ProductImageSimpleSerializer
|
|
is used.
|
|
"""
|
|
|
|
queryset = ProductImage.objects.all()
|
|
filter_backends = [DjangoFilterBackend]
|
|
filterset_fields = ["product", "priority", "is_active"]
|
|
serializer_class = ProductImageDetailSerializer
|
|
action_serializer_classes = {
|
|
"list": ProductImageSimpleSerializer,
|
|
}
|
|
|
|
|
|
class PromoCodeViewSet(EvibesViewSet):
|
|
"""
|
|
Manages the retrieval and handling of PromoCode instances through various
|
|
API actions.
|
|
|
|
This class extends the functionality of the EvibesViewSet to provide a
|
|
customized view set for PromoCode objects. It includes filtering capabilities,
|
|
uses specific serializers for different actions, and limits data access
|
|
based on user permissions. The primary purpose is to enable API operations
|
|
related to PromoCodes while enforcing security and filtering.
|
|
|
|
Attributes:
|
|
queryset: A queryset of all PromoCode objects in the database.
|
|
filter_backends: Backend classes responsible for filtering queryset data.
|
|
filterset_fields: Fields supported for filtering PromoCode data.
|
|
serializer_class: The default serializer class used for instances when no
|
|
specific action-based serializer is defined.
|
|
action_serializer_classes: A dictionary mapping specific actions (like
|
|
"list") to their corresponding serializer classes.
|
|
"""
|
|
|
|
queryset = PromoCode.objects.all()
|
|
filter_backends = [DjangoFilterBackend]
|
|
filterset_fields = ["code", "discount_amount", "discount_percent", "start_time", "end_time", "used_on", "is_active"]
|
|
serializer_class = PromoCodeDetailSerializer
|
|
action_serializer_classes = {
|
|
"list": PromoCodeSimpleSerializer,
|
|
}
|
|
|
|
def get_queryset(self):
|
|
qs = super().get_queryset()
|
|
user = self.request.user
|
|
|
|
if user.has_perm("core.view_promocode"):
|
|
return qs
|
|
|
|
return qs.filter(user=user)
|
|
|
|
|
|
class PromotionViewSet(EvibesViewSet):
|
|
"""
|
|
Represents a view set for managing promotions.
|
|
|
|
This class provides operations to handle retrieval, filtering, and serialization
|
|
of promotion objects. It leverages Django REST framework capabilities such as
|
|
queryset management, filter backends, and serializer customization for handling
|
|
different views or actions efficiently.
|
|
"""
|
|
|
|
queryset = Promotion.objects.all()
|
|
filter_backends = [DjangoFilterBackend]
|
|
filterset_fields = ["name", "discount_percent", "is_active"]
|
|
serializer_class = PromotionDetailSerializer
|
|
action_serializer_classes = {
|
|
"list": PromotionSimpleSerializer,
|
|
}
|
|
|
|
|
|
class StockViewSet(EvibesViewSet):
|
|
"""
|
|
Handles operations related to Stock data in the system.
|
|
|
|
The StockViewSet class is a viewset that provides methods for retrieving,
|
|
filtering, and serializing Stock data. It uses Django's filter
|
|
backends to enable filtering based on specified fields and supports
|
|
custom serializers for different actions.
|
|
|
|
Attributes:
|
|
queryset (QuerySet): A queryset of all Stock objects.
|
|
filter_backends (list): A list of filter backends to be applied.
|
|
filterset_fields (list of str): Fields on which the filtering
|
|
is permitted. These fields include "vendor", "product", "sku",
|
|
and "is_active".
|
|
serializer_class (Serializer): The primary serializer used
|
|
for Stock detail representation.
|
|
action_serializer_classes (dict): A dictionary mapping action names
|
|
to their respective serializers. For the "list" action,
|
|
StockSimpleSerializer is used.
|
|
"""
|
|
|
|
queryset = Stock.objects.all()
|
|
filter_backends = [DjangoFilterBackend]
|
|
filterset_fields = ["vendor", "product", "sku", "is_active"]
|
|
serializer_class = StockDetailSerializer
|
|
action_serializer_classes = {
|
|
"list": StockSimpleSerializer,
|
|
}
|
|
|
|
|
|
@extend_schema_view(**WISHLIST_SCHEMA)
|
|
class WishlistViewSet(EvibesViewSet):
|
|
"""
|
|
ViewSet for managing Wishlist operations.
|
|
|
|
The WishlistViewSet provides endpoints for interacting with a user's wish list,
|
|
allowing for the retrieval, modification, and customization of products within
|
|
the wish list. This ViewSet facilitates functionality such as adding, removing,
|
|
and bulk actions for wishlist products. Permission checks are integrated to
|
|
ensure that users can only manage their own wishlists unless explicit permissions
|
|
are granted.
|
|
|
|
Attributes
|
|
----------
|
|
queryset : QuerySet
|
|
The base queryset for retrieving wishlist objects.
|
|
filter_backends : list
|
|
List of backend filters to apply to the queryset.
|
|
filterset_fields : list
|
|
Fields for which filtering is allowed in queries.
|
|
serializer_class : Serializer
|
|
The default serializer class used for wishlist objects.
|
|
action_serializer_classes : dict
|
|
A map of serializers used for specific actions.
|
|
|
|
Methods
|
|
-------
|
|
get_queryset()
|
|
Retrieves the queryset, filtered based on the user's permissions.
|
|
current(request)
|
|
Retrieves the currently authenticated user's wishlist.
|
|
add_wishlist_product(request, **kwargs)
|
|
Adds a product to a specific wishlist.
|
|
remove_wishlist_product(request, **kwargs)
|
|
Removes a product from a specific wishlist.
|
|
bulk_add_wishlist_products(request, **kwargs)
|
|
Adds multiple products to a specific wishlist.
|
|
bulk_remove_wishlist_products(request, **kwargs)
|
|
Removes multiple products from a specific wishlist.
|
|
"""
|
|
|
|
queryset = Wishlist.objects.all()
|
|
filter_backends = [DjangoFilterBackend]
|
|
filterset_fields = ["user", "is_active"]
|
|
serializer_class = WishlistDetailSerializer
|
|
action_serializer_classes = {
|
|
"list": WishlistSimpleSerializer,
|
|
}
|
|
|
|
def get_queryset(self):
|
|
qs = super().get_queryset()
|
|
user = self.request.user
|
|
|
|
if user.has_perm("core.view_wishlist"):
|
|
return qs
|
|
|
|
return qs.filter(user=user)
|
|
|
|
@action(detail=False, methods=["get"], url_path="current")
|
|
def current(self, request):
|
|
if not request.user.is_authenticated:
|
|
raise PermissionDenied(permission_denied_message)
|
|
wishlist = Wishlist.objects.get(user=request.user)
|
|
if not request.user == wishlist.user:
|
|
raise PermissionDenied(permission_denied_message)
|
|
return Response(
|
|
status=status.HTTP_200_OK,
|
|
data=WishlistDetailSerializer(wishlist).data,
|
|
)
|
|
|
|
@action(detail=True, methods=["post"], url_path="add_wishlist_product")
|
|
def add_wishlist_product(self, request, **kwargs):
|
|
serializer = AddWishlistProductSerializer(data=request.data)
|
|
serializer.is_valid(raise_exception=True)
|
|
try:
|
|
wishlist = Wishlist.objects.get(uuid=kwargs.get("pk"))
|
|
if not (request.user.has_perm("core.change_wishlist") or request.user == wishlist.user):
|
|
raise PermissionDenied(permission_denied_message)
|
|
|
|
wishlist = wishlist.add_product(
|
|
product_uuid=serializer.validated_data.get("product_uuid"),
|
|
)
|
|
|
|
return Response(status=status.HTTP_200_OK, data=WishlistDetailSerializer(wishlist).data)
|
|
except Wishlist.DoesNotExist:
|
|
return Response(status=status.HTTP_404_NOT_FOUND)
|
|
|
|
@action(detail=True, methods=["post"], url_path="remove_wishlist_product")
|
|
def remove_wishlist_product(self, request, **kwargs):
|
|
serializer = RemoveWishlistProductSerializer(data=request.data)
|
|
serializer.is_valid(raise_exception=True)
|
|
try:
|
|
wishlist = Wishlist.objects.get(uuid=kwargs.get("pk"))
|
|
if not (request.user.has_perm("core.change_wishlist") or request.user == wishlist.user):
|
|
raise PermissionDenied(permission_denied_message)
|
|
|
|
wishlist = wishlist.remove_product(
|
|
product_uuid=serializer.validated_data.get("product_uuid"),
|
|
)
|
|
|
|
return Response(status=status.HTTP_200_OK, data=WishlistDetailSerializer(wishlist).data)
|
|
except Wishlist.DoesNotExist:
|
|
return Response(status=status.HTTP_404_NOT_FOUND)
|
|
|
|
@action(detail=True, methods=["post"], url_path="bulk_add_wishlist_product")
|
|
def bulk_add_wishlist_products(self, request, **kwargs):
|
|
serializer = BulkAddWishlistProductSerializer(data=request.data)
|
|
serializer.is_valid(raise_exception=True)
|
|
try:
|
|
wishlist = Wishlist.objects.get(uuid=kwargs.get("pk"))
|
|
if not (request.user.has_perm("core.change_wishlist") or request.user == wishlist.user):
|
|
raise PermissionDenied(permission_denied_message)
|
|
|
|
wishlist = wishlist.bulk_add_products(
|
|
product_uuids=serializer.validated_data.get("product_uuids"),
|
|
)
|
|
|
|
return Response(status=status.HTTP_200_OK, data=WishlistDetailSerializer(wishlist).data)
|
|
except Order.DoesNotExist:
|
|
return Response(status=status.HTTP_404_NOT_FOUND)
|
|
|
|
@action(detail=True, methods=["post"], url_path="bulk_remove_wishlist_product")
|
|
def bulk_remove_wishlist_products(self, request, **kwargs):
|
|
serializer = BulkRemoveWishlistProductSerializer(data=request.data)
|
|
serializer.is_valid(raise_exception=True)
|
|
try:
|
|
wishlist = Wishlist.objects.get(uuid=kwargs.get("pk"))
|
|
if not (request.user.has_perm("core.change_wishlist") or request.user == wishlist.user):
|
|
raise PermissionDenied(permission_denied_message)
|
|
|
|
wishlist = wishlist.bulk_remove_products(
|
|
product_uuids=serializer.validated_data.get("product_uuids"),
|
|
)
|
|
|
|
return Response(status=status.HTTP_200_OK, data=WishlistDetailSerializer(wishlist).data)
|
|
except Order.DoesNotExist:
|
|
return Response(status=status.HTTP_404_NOT_FOUND)
|
|
|
|
|
|
@extend_schema_view(**ADDRESS_SCHEMA)
|
|
class AddressViewSet(EvibesViewSet):
|
|
"""
|
|
This class provides viewset functionality for managing `Address` objects.
|
|
|
|
The AddressViewSet class enables CRUD operations, filtering, and custom actions
|
|
related to address entities. It includes specialized behaviors for different HTTP
|
|
methods, serializer overrides, and permission handling based on the request context.
|
|
|
|
Attributes:
|
|
pagination_class: Specifies pagination class for the viewset, set to None.
|
|
filter_backends: List of backend classes for filtering querysets.
|
|
filterset_class: Specifies the filter class for filtering address objects.
|
|
queryset: Default queryset containing all Address objects.
|
|
serializer_class: Default serializer class for address objects.
|
|
additional: Dictionary of additional options for this viewset.
|
|
"""
|
|
|
|
pagination_class = None
|
|
filter_backends = [DjangoFilterBackend]
|
|
filterset_class = AddressFilter
|
|
queryset = Address.objects.all()
|
|
serializer_class = AddressSerializer
|
|
additional = {"create": "ALLOW"}
|
|
|
|
def get_serializer_class(self):
|
|
if self.action == "create":
|
|
return AddressCreateSerializer
|
|
if self.action == "autocomplete":
|
|
return AddressAutocompleteInputSerializer
|
|
return AddressSerializer
|
|
|
|
def get_queryset(self):
|
|
if self.request.user.has_perm("core.view_address"):
|
|
return super().get_queryset()
|
|
|
|
if self.request.user.is_authenticated:
|
|
return super().get_queryset().filter(user=self.request.user)
|
|
|
|
return Address.objects.none()
|
|
|
|
def create(self, request, **kwargs):
|
|
create_serializer = AddressCreateSerializer(data=request.data, context={"request": request})
|
|
create_serializer.is_valid(raise_exception=True)
|
|
|
|
address_obj = create_serializer.create(create_serializer.validated_data)
|
|
|
|
output_serializer = AddressSerializer(address_obj, context={"request": request})
|
|
|
|
return Response(
|
|
status=status.HTTP_201_CREATED,
|
|
data=output_serializer.data,
|
|
)
|
|
|
|
@action(detail=False, methods=["get"], url_path="autocomplete")
|
|
def autocomplete(self, request):
|
|
serializer = AddressAutocompleteInputSerializer(data=request.query_params)
|
|
serializer.is_valid(raise_exception=True)
|
|
|
|
q = serializer.validated_data["q"]
|
|
limit = serializer.validated_data["limit"]
|
|
|
|
try:
|
|
suggestions = fetch_address_suggestions(query=q, limit=limit)
|
|
serializer = AddressSuggestionSerializer(suggestions, many=True)
|
|
return Response(
|
|
serializer.data,
|
|
status=status.HTTP_200_OK,
|
|
)
|
|
except Exception as e:
|
|
return Response(
|
|
{"detail": _(f"Geocoding error: {e}")},
|
|
status=status.HTTP_502_BAD_GATEWAY,
|
|
)
|
|
|
|
|
|
class ProductTagViewSet(EvibesViewSet):
|
|
"""
|
|
Handles operations related to Product Tags within the application.
|
|
|
|
This class provides functionality for retrieving, filtering, and serializing
|
|
Product Tag objects. It supports flexible filtering on specific attributes
|
|
using the specified filter backend and dynamically uses different serializers
|
|
based on the action being performed.
|
|
|
|
Attributes:
|
|
queryset: The base queryset containing all ProductTag objects.
|
|
filter_backends: A list of backends used to filter the queryset.
|
|
filterset_fields: Fields available for filtering the queryset. Includes
|
|
'tag_name' for filtering by the name of the tag, and 'is_active' for
|
|
filtering active/inactive tags.
|
|
serializer_class: The default serializer class is used when no specific
|
|
serializer is defined for an action.
|
|
action_serializer_classes: A dictionary mapping specific actions (e.g.,
|
|
'list') to custom serializers. Uses ProductTagSimpleSerializer
|
|
for the 'list' action.
|
|
"""
|
|
|
|
queryset = ProductTag.objects.all()
|
|
filter_backends = [DjangoFilterBackend]
|
|
filterset_fields = ["tag_name", "is_active"]
|
|
serializer_class = ProductTagDetailSerializer
|
|
action_serializer_classes = {
|
|
"list": ProductTagSimpleSerializer,
|
|
}
|