Features: Added feedbacks support for OrderProducts

This commit is contained in:
Egor Pavlovich Gorbunov 2025-06-08 14:18:26 +03:00
parent b876983ef3
commit 4148d9e02c
4 changed files with 199 additions and 63 deletions

View file

@ -24,6 +24,8 @@ from core.serializers import (
FeedbackDetailSerializer, FeedbackDetailSerializer,
FeedbackSimpleSerializer, FeedbackSimpleSerializer,
OrderDetailSerializer, OrderDetailSerializer,
OrderProductDetailSerializer,
OrderProductSimpleSerializer,
OrderSimpleSerializer, OrderSimpleSerializer,
ProductDetailSerializer, ProductDetailSerializer,
ProductSimpleSerializer, ProductSimpleSerializer,
@ -32,7 +34,7 @@ from core.serializers import (
WishlistDetailSerializer, WishlistDetailSerializer,
WishlistSimpleSerializer, WishlistSimpleSerializer,
) )
from core.serializers.utility import AddressCreateSerializer, AddressSuggestionSerializer from core.serializers.utility import AddressCreateSerializer, AddressSuggestionSerializer, DoFeedbackSerializer
from payments.serializers import TransactionProcessSerializer from payments.serializers import TransactionProcessSerializer
ATTRIBUTE_GROUP_SCHEMA = { ATTRIBUTE_GROUP_SCHEMA = {
@ -42,11 +44,11 @@ ATTRIBUTE_GROUP_SCHEMA = {
), ),
"retrieve": extend_schema( "retrieve": extend_schema(
summary=_("retrieve a single attribute group (detailed view)"), summary=_("retrieve a single attribute group (detailed view)"),
responses={status.HTTP_200_OK: AttributeGroupDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: AttributeGroupDetailSerializer(), **BASE_ERRORS},
), ),
"create": extend_schema( "create": extend_schema(
summary=_("create an attribute group"), summary=_("create an attribute group"),
responses={status.HTTP_201_CREATED: AttributeGroupDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_201_CREATED: AttributeGroupDetailSerializer(), **BASE_ERRORS},
), ),
"destroy": extend_schema( "destroy": extend_schema(
summary=_("delete an attribute group"), summary=_("delete an attribute group"),
@ -54,11 +56,11 @@ ATTRIBUTE_GROUP_SCHEMA = {
), ),
"update": extend_schema( "update": extend_schema(
summary=_("rewrite an existing attribute group saving non-editables"), summary=_("rewrite an existing attribute group saving non-editables"),
responses={status.HTTP_200_OK: AttributeGroupDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: AttributeGroupDetailSerializer(), **BASE_ERRORS},
), ),
"partial_update": extend_schema( "partial_update": extend_schema(
summary=_("rewrite some fields of an existing attribute group saving non-editables"), summary=_("rewrite some fields of an existing attribute group saving non-editables"),
responses={status.HTTP_200_OK: AttributeGroupDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: AttributeGroupDetailSerializer(), **BASE_ERRORS},
), ),
} }
@ -69,11 +71,11 @@ ATTRIBUTE_SCHEMA = {
), ),
"retrieve": extend_schema( "retrieve": extend_schema(
summary=_("retrieve a single attribute (detailed view)"), summary=_("retrieve a single attribute (detailed view)"),
responses={status.HTTP_200_OK: AttributeDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: AttributeDetailSerializer(), **BASE_ERRORS},
), ),
"create": extend_schema( "create": extend_schema(
summary=_("create an attribute"), summary=_("create an attribute"),
responses={status.HTTP_201_CREATED: AttributeDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_201_CREATED: AttributeDetailSerializer(), **BASE_ERRORS},
), ),
"destroy": extend_schema( "destroy": extend_schema(
summary=_("delete an attribute"), summary=_("delete an attribute"),
@ -81,11 +83,11 @@ ATTRIBUTE_SCHEMA = {
), ),
"update": extend_schema( "update": extend_schema(
summary=_("rewrite an existing attribute saving non-editables"), summary=_("rewrite an existing attribute saving non-editables"),
responses={status.HTTP_200_OK: AttributeDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: AttributeDetailSerializer(), **BASE_ERRORS},
), ),
"partial_update": extend_schema( "partial_update": extend_schema(
summary=_("rewrite some fields of an existing attribute saving non-editables"), summary=_("rewrite some fields of an existing attribute saving non-editables"),
responses={status.HTTP_200_OK: AttributeDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: AttributeDetailSerializer(), **BASE_ERRORS},
), ),
} }
@ -96,11 +98,11 @@ ATTRIBUTE_VALUE_SCHEMA = {
), ),
"retrieve": extend_schema( "retrieve": extend_schema(
summary=_("retrieve a single attribute value (detailed view)"), summary=_("retrieve a single attribute value (detailed view)"),
responses={status.HTTP_200_OK: AttributeValueDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: AttributeValueDetailSerializer(), **BASE_ERRORS},
), ),
"create": extend_schema( "create": extend_schema(
summary=_("create an attribute value"), summary=_("create an attribute value"),
responses={status.HTTP_201_CREATED: AttributeValueDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_201_CREATED: AttributeValueDetailSerializer(), **BASE_ERRORS},
), ),
"destroy": extend_schema( "destroy": extend_schema(
summary=_("delete an attribute value"), summary=_("delete an attribute value"),
@ -108,11 +110,11 @@ ATTRIBUTE_VALUE_SCHEMA = {
), ),
"update": extend_schema( "update": extend_schema(
summary=_("rewrite an existing attribute value saving non-editables"), summary=_("rewrite an existing attribute value saving non-editables"),
responses={status.HTTP_200_OK: AttributeValueDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: AttributeValueDetailSerializer(), **BASE_ERRORS},
), ),
"partial_update": extend_schema( "partial_update": extend_schema(
summary=_("rewrite some fields of an existing attribute value saving non-editables"), summary=_("rewrite some fields of an existing attribute value saving non-editables"),
responses={status.HTTP_200_OK: AttributeValueDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: AttributeValueDetailSerializer(), **BASE_ERRORS},
), ),
} }
@ -123,11 +125,11 @@ CATEGORY_SCHEMA = {
), ),
"retrieve": extend_schema( "retrieve": extend_schema(
summary=_("retrieve a single category (detailed view)"), summary=_("retrieve a single category (detailed view)"),
responses={status.HTTP_200_OK: CategoryDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: CategoryDetailSerializer(), **BASE_ERRORS},
), ),
"create": extend_schema( "create": extend_schema(
summary=_("create a category"), summary=_("create a category"),
responses={status.HTTP_201_CREATED: CategoryDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_201_CREATED: CategoryDetailSerializer(), **BASE_ERRORS},
), ),
"destroy": extend_schema( "destroy": extend_schema(
summary=_("delete a category"), summary=_("delete a category"),
@ -135,11 +137,11 @@ CATEGORY_SCHEMA = {
), ),
"update": extend_schema( "update": extend_schema(
summary=_("rewrite an existing category saving non-editables"), summary=_("rewrite an existing category saving non-editables"),
responses={status.HTTP_200_OK: CategoryDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: CategoryDetailSerializer(), **BASE_ERRORS},
), ),
"partial_update": extend_schema( "partial_update": extend_schema(
summary=_("rewrite some fields of an existing category saving non-editables"), summary=_("rewrite some fields of an existing category saving non-editables"),
responses={status.HTTP_200_OK: CategoryDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: CategoryDetailSerializer(), **BASE_ERRORS},
), ),
} }
@ -151,12 +153,12 @@ ORDER_SCHEMA = {
), ),
"retrieve": extend_schema( "retrieve": extend_schema(
summary=_("retrieve a single order (detailed view)"), summary=_("retrieve a single order (detailed view)"),
responses={status.HTTP_200_OK: OrderDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: OrderDetailSerializer(), **BASE_ERRORS},
), ),
"create": extend_schema( "create": extend_schema(
summary=_("create an order"), summary=_("create an order"),
description=_("doesn't work for non-staff users."), description=_("doesn't work for non-staff users."),
responses={status.HTTP_201_CREATED: OrderDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_201_CREATED: OrderDetailSerializer(), **BASE_ERRORS},
), ),
"destroy": extend_schema( "destroy": extend_schema(
summary=_("delete an order"), summary=_("delete an order"),
@ -164,11 +166,11 @@ ORDER_SCHEMA = {
), ),
"update": extend_schema( "update": extend_schema(
summary=_("rewrite an existing order saving non-editables"), summary=_("rewrite an existing order saving non-editables"),
responses={status.HTTP_200_OK: OrderDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: OrderDetailSerializer(), **BASE_ERRORS},
), ),
"partial_update": extend_schema( "partial_update": extend_schema(
summary=_("rewrite some fields of an existing order saving non-editables"), summary=_("rewrite some fields of an existing order saving non-editables"),
responses={status.HTTP_200_OK: OrderDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: OrderDetailSerializer(), **BASE_ERRORS},
), ),
"buy": extend_schema( "buy": extend_schema(
summary=_("purchase an order"), summary=_("purchase an order"),
@ -177,45 +179,45 @@ ORDER_SCHEMA = {
" the purchase is completed using the user's balance;" " the purchase is completed using the user's balance;"
" if `force_payment` is used, a transaction is initiated." " if `force_payment` is used, a transaction is initiated."
), ),
request=BuyOrderSerializer, request=BuyOrderSerializer(),
responses={ responses={
status.HTTP_200_OK: OrderDetailSerializer, status.HTTP_200_OK: OrderDetailSerializer(),
status.HTTP_202_ACCEPTED: TransactionProcessSerializer, status.HTTP_202_ACCEPTED: TransactionProcessSerializer(),
**BASE_ERRORS, **BASE_ERRORS,
}, },
), ),
"buy_unregistered": extend_schema( "buy_unregistered": extend_schema(
summary=_("purchase an order without account creation"), summary=_("purchase an order without account creation"),
description=_("finalizes the order purchase for a non-registered user."), description=_("finalizes the order purchase for a non-registered user."),
request=BuyUnregisteredOrderSerializer, request=BuyUnregisteredOrderSerializer(),
responses={ responses={
status.HTTP_202_ACCEPTED: TransactionProcessSerializer, status.HTTP_202_ACCEPTED: TransactionProcessSerializer(),
**BASE_ERRORS, **BASE_ERRORS,
}, },
), ),
"add_order_product": extend_schema( "add_order_product": extend_schema(
summary=_("add product to order"), summary=_("add product to order"),
description=_("adds a product to an order using the provided `product_uuid` and `attributes`."), description=_("adds a product to an order using the provided `product_uuid` and `attributes`."),
request=AddOrderProductSerializer, request=AddOrderProductSerializer(),
responses={status.HTTP_200_OK: OrderDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: OrderDetailSerializer(), **BASE_ERRORS},
), ),
"bulk_add_order_products": extend_schema( "bulk_add_order_products": extend_schema(
summary=_("add a list of products to order, quantities will not count"), summary=_("add a list of products to order, quantities will not count"),
description=_("adds a list of products to an order using the provided `product_uuid` and `attributes`."), description=_("adds a list of products to an order using the provided `product_uuid` and `attributes`."),
request=BulkAddOrderProductsSerializer, request=BulkAddOrderProductsSerializer(),
responses={status.HTTP_200_OK: OrderDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: OrderDetailSerializer(), **BASE_ERRORS},
), ),
"remove_order_product": extend_schema( "remove_order_product": extend_schema(
summary=_("remove product from order"), summary=_("remove product from order"),
description=_("removes a product from an order using the provided `product_uuid` and `attributes`."), description=_("removes a product from an order using the provided `product_uuid` and `attributes`."),
request=RemoveOrderProductSerializer, request=RemoveOrderProductSerializer(),
responses={status.HTTP_200_OK: OrderDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: OrderDetailSerializer(), **BASE_ERRORS},
), ),
"bulk_remove_order_products": extend_schema( "bulk_remove_order_products": extend_schema(
summary=_("remove product from order, quantities will not count"), summary=_("remove product from order, quantities will not count"),
description=_("removes a list of products from an order using the provided `product_uuid` and `attributes`"), description=_("removes a list of products from an order using the provided `product_uuid` and `attributes`"),
request=BulkRemoveOrderProductsSerializer, request=BulkRemoveOrderProductsSerializer(),
responses={status.HTTP_200_OK: OrderDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: OrderDetailSerializer(), **BASE_ERRORS},
), ),
} }
@ -227,12 +229,12 @@ WISHLIST_SCHEMA = {
), ),
"retrieve": extend_schema( "retrieve": extend_schema(
summary=_("retrieve a single wishlist (detailed view)"), summary=_("retrieve a single wishlist (detailed view)"),
responses={status.HTTP_200_OK: WishlistDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: WishlistDetailSerializer(), **BASE_ERRORS},
), ),
"create": extend_schema( "create": extend_schema(
summary=_("create an wishlist"), summary=_("create an wishlist"),
description=_("Doesn't work for non-staff users."), description=_("Doesn't work for non-staff users."),
responses={status.HTTP_201_CREATED: WishlistDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_201_CREATED: WishlistDetailSerializer(), **BASE_ERRORS},
), ),
"destroy": extend_schema( "destroy": extend_schema(
summary=_("delete an wishlist"), summary=_("delete an wishlist"),
@ -240,35 +242,35 @@ WISHLIST_SCHEMA = {
), ),
"update": extend_schema( "update": extend_schema(
summary=_("rewrite an existing wishlist saving non-editables"), summary=_("rewrite an existing wishlist saving non-editables"),
responses={status.HTTP_200_OK: WishlistDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: WishlistDetailSerializer(), **BASE_ERRORS},
), ),
"partial_update": extend_schema( "partial_update": extend_schema(
summary=_("rewrite some fields of an existing wishlist saving non-editables"), summary=_("rewrite some fields of an existing wishlist saving non-editables"),
responses={status.HTTP_200_OK: WishlistDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: WishlistDetailSerializer(), **BASE_ERRORS},
), ),
"add_wishlist_product": extend_schema( "add_wishlist_product": extend_schema(
summary=_("add product to wishlist"), summary=_("add product to wishlist"),
description=_("adds a product to an wishlist using the provided `product_uuid`"), description=_("adds a product to an wishlist using the provided `product_uuid`"),
request=AddWishlistProductSerializer, request=AddWishlistProductSerializer(),
responses={status.HTTP_200_OK: WishlistDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: WishlistDetailSerializer(), **BASE_ERRORS},
), ),
"remove_wishlist_product": extend_schema( "remove_wishlist_product": extend_schema(
summary=_("remove product from wishlist"), summary=_("remove product from wishlist"),
description=_("removes a product from an wishlist using the provided `product_uuid`"), description=_("removes a product from an wishlist using the provided `product_uuid`"),
request=RemoveWishlistProductSerializer, request=RemoveWishlistProductSerializer(),
responses={status.HTTP_200_OK: WishlistDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: WishlistDetailSerializer(), **BASE_ERRORS},
), ),
"bulk_add_wishlist_products": extend_schema( "bulk_add_wishlist_products": extend_schema(
summary=_("add many products to wishlist"), summary=_("add many products to wishlist"),
description=_("adds many products to an wishlist using the provided `product_uuids`"), description=_("adds many products to an wishlist using the provided `product_uuids`"),
request=BulkAddWishlistProductSerializer, request=BulkAddWishlistProductSerializer(),
responses={status.HTTP_200_OK: WishlistDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: WishlistDetailSerializer(), **BASE_ERRORS},
), ),
"bulk_remove_wishlist_products": extend_schema( "bulk_remove_wishlist_products": extend_schema(
summary=_("remove many products from wishlist"), summary=_("remove many products from wishlist"),
description=_("removes many products from an wishlist using the provided `product_uuids`"), description=_("removes many products from an wishlist using the provided `product_uuids`"),
request=BulkRemoveWishlistProductSerializer, request=BulkRemoveWishlistProductSerializer(),
responses={status.HTTP_200_OK: WishlistDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: WishlistDetailSerializer(), **BASE_ERRORS},
), ),
} }
@ -397,14 +399,14 @@ PRODUCT_SCHEMA = {
), ),
], ],
responses={ responses={
status.HTTP_200_OK: ProductDetailSerializer, status.HTTP_200_OK: ProductDetailSerializer(),
**BASE_ERRORS, **BASE_ERRORS,
}, },
), ),
"create": extend_schema( "create": extend_schema(
summary=_("create a product"), summary=_("create a product"),
responses={ responses={
status.HTTP_201_CREATED: ProductDetailSerializer, status.HTTP_201_CREATED: ProductDetailSerializer(),
**BASE_ERRORS, **BASE_ERRORS,
}, },
), ),
@ -419,7 +421,7 @@ PRODUCT_SCHEMA = {
), ),
], ],
responses={ responses={
status.HTTP_200_OK: ProductDetailSerializer, status.HTTP_200_OK: ProductDetailSerializer(),
**BASE_ERRORS, **BASE_ERRORS,
}, },
), ),
@ -434,7 +436,7 @@ PRODUCT_SCHEMA = {
), ),
], ],
responses={ responses={
status.HTTP_200_OK: ProductDetailSerializer, status.HTTP_200_OK: ProductDetailSerializer(),
**BASE_ERRORS, **BASE_ERRORS,
}, },
), ),
@ -453,6 +455,21 @@ PRODUCT_SCHEMA = {
**BASE_ERRORS, **BASE_ERRORS,
}, },
), ),
"feedbacks": extend_schema(
summary=_("lists all permitted feedbacks for a product"),
parameters=[
OpenApiParameter(
name="lookup",
location="path",
description=_("Product UUID or slug"),
type=str,
),
],
responses={
status.HTTP_200_OK: FeedbackDetailSerializer(many=True),
**BASE_ERRORS,
},
),
} }
ADDRESS_SCHEMA = { ADDRESS_SCHEMA = {
@ -466,15 +483,15 @@ ADDRESS_SCHEMA = {
"retrieve": extend_schema( "retrieve": extend_schema(
summary=_("retrieve a single address"), summary=_("retrieve a single address"),
responses={ responses={
status.HTTP_200_OK: AddressSerializer, status.HTTP_200_OK: AddressSerializer(),
**BASE_ERRORS, **BASE_ERRORS,
}, },
), ),
"create": extend_schema( "create": extend_schema(
summary=_("create a new address"), summary=_("create a new address"),
request=AddressCreateSerializer, request=AddressCreateSerializer(),
responses={ responses={
status.HTTP_201_CREATED: AddressSerializer, status.HTTP_201_CREATED: AddressSerializer(),
**BASE_ERRORS, **BASE_ERRORS,
}, },
), ),
@ -487,17 +504,17 @@ ADDRESS_SCHEMA = {
), ),
"update": extend_schema( "update": extend_schema(
summary=_("update an entire address"), summary=_("update an entire address"),
request=AddressSerializer, request=AddressSerializer(),
responses={ responses={
status.HTTP_200_OK: AddressSerializer, status.HTTP_200_OK: AddressSerializer(),
**BASE_ERRORS, **BASE_ERRORS,
}, },
), ),
"partial_update": extend_schema( "partial_update": extend_schema(
summary=_("partially update an address"), summary=_("partially update an address"),
request=AddressSerializer, request=AddressSerializer(),
responses={ responses={
status.HTTP_200_OK: AddressSerializer, status.HTTP_200_OK: AddressSerializer(),
**BASE_ERRORS, **BASE_ERRORS,
}, },
), ),
@ -531,11 +548,11 @@ FEEDBACK_SCHEMA = {
), ),
"retrieve": extend_schema( "retrieve": extend_schema(
summary=_("retrieve a single feedback (detailed view)"), summary=_("retrieve a single feedback (detailed view)"),
responses={status.HTTP_200_OK: FeedbackDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: FeedbackDetailSerializer(), **BASE_ERRORS},
), ),
"create": extend_schema( "create": extend_schema(
summary=_("create a feedback"), summary=_("create a feedback"),
responses={status.HTTP_201_CREATED: FeedbackDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_201_CREATED: FeedbackDetailSerializer(), **BASE_ERRORS},
), ),
"destroy": extend_schema( "destroy": extend_schema(
summary=_("delete a feedback"), summary=_("delete a feedback"),
@ -543,10 +560,66 @@ FEEDBACK_SCHEMA = {
), ),
"update": extend_schema( "update": extend_schema(
summary=_("rewrite an existing feedback saving non-editables"), summary=_("rewrite an existing feedback saving non-editables"),
responses={status.HTTP_200_OK: FeedbackDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: FeedbackDetailSerializer(), **BASE_ERRORS},
), ),
"partial_update": extend_schema( "partial_update": extend_schema(
summary=_("rewrite some fields of an existing feedback saving non-editables"), summary=_("rewrite some fields of an existing feedback saving non-editables"),
responses={status.HTTP_200_OK: FeedbackDetailSerializer, **BASE_ERRORS}, responses={status.HTTP_200_OK: FeedbackDetailSerializer(), **BASE_ERRORS},
),
}
ORDER_PRODUCT_SCHEMA = {
"list": extend_schema(
summary=_("list all orderproduct relations (simple view)"),
responses={
status.HTTP_200_OK: OrderProductSimpleSerializer(many=True),
**BASE_ERRORS,
},
),
"retrieve": extend_schema(
summary=_("retrieve a single orderproduct relation (detailed view)"),
responses={
status.HTTP_200_OK: OrderProductDetailSerializer(),
**BASE_ERRORS,
},
),
"create": extend_schema(
summary=_("create a new orderproduct relation"),
responses={
status.HTTP_201_CREATED: OrderProductDetailSerializer(),
**BASE_ERRORS,
},
),
"update": extend_schema(
summary=_("replace an existing orderproduct relation"),
responses={
status.HTTP_200_OK: OrderProductDetailSerializer(),
**BASE_ERRORS,
},
),
"partial_update": extend_schema(
summary=_("partially update an existing orderproduct relation"),
responses={
status.HTTP_200_OK: OrderProductDetailSerializer(),
**BASE_ERRORS,
},
),
"destroy": extend_schema(
summary=_("delete an orderproduct relation"),
responses={
status.HTTP_204_NO_CONTENT: {},
**BASE_ERRORS,
},
),
"do_feedback": extend_schema(
summary=_("add or remove feedback on an orderproduct relation"),
request=DoFeedbackSerializer,
responses={
status.HTTP_201_CREATED: FeedbackDetailSerializer(),
status.HTTP_204_NO_CONTENT: {},
status.HTTP_400_BAD_REQUEST: {},
status.HTTP_404_NOT_FOUND: {},
**BASE_ERRORS,
},
), ),
} }

View file

@ -913,6 +913,16 @@ class OrderProduct(ExportModelOperationsMixin("order_product"), NiceModel):
return self.download.url return self.download.url
return "" return ""
def do_feedback(self, rating: int = 10, comment: str = "", action: str = "add"):
if action not in ["add", "remove"]:
raise ValueError(_(f"wrong action specified for feedback: {action}"))
if action == "remove" and self.feedback:
self.feedback.delete()
return None
if action == "add" and not self.feedback:
return Feedback.objects.create(rating=rating, comment=comment, order_product=self)
return None
class ProductTag(ExportModelOperationsMixin("product_tag"), NiceModel): class ProductTag(ExportModelOperationsMixin("product_tag"), NiceModel):
is_publicly_visible = True is_publicly_visible = True

View file

@ -1,3 +1,4 @@
from rest_framework.exceptions import ValidationError
from rest_framework.fields import CharField, DictField, FloatField, IntegerField from rest_framework.fields import CharField, DictField, FloatField, IntegerField
from rest_framework.serializers import ModelSerializer, Serializer from rest_framework.serializers import ModelSerializer, Serializer
@ -62,3 +63,13 @@ class AddressCreateSerializer(ModelSerializer):
if self.context["request"].user.is_authenticated: if self.context["request"].user.is_authenticated:
user = self.context["request"].user user = self.context["request"].user
return Address.objects.create(raw_data=raw, user=user, **validated_data) return Address.objects.create(raw_data=raw, user=user, **validated_data)
class DoFeedbackSerializer(Serializer):
comment = CharField(required=True)
rating = IntegerField(required=False, min_value=1, max_value=10, default=10)
action = CharField(choices=["add", "remove"], required=True, default="add")
def validate(self, data):
if data["action"] == "add" and not all([data["comment"], data["rating"]]):
raise ValidationError(_("you must provide a comment, rating, and order product uuid to add feedback."))

View file

@ -26,6 +26,7 @@ from core.docs.drf.viewsets import (
ATTRIBUTE_VALUE_SCHEMA, ATTRIBUTE_VALUE_SCHEMA,
CATEGORY_SCHEMA, CATEGORY_SCHEMA,
FEEDBACK_SCHEMA, FEEDBACK_SCHEMA,
ORDER_PRODUCT_SCHEMA,
ORDER_SCHEMA, ORDER_SCHEMA,
PRODUCT_SCHEMA, PRODUCT_SCHEMA,
WISHLIST_SCHEMA, WISHLIST_SCHEMA,
@ -99,6 +100,7 @@ from core.serializers.utility import (
AddressAutocompleteInputSerializer, AddressAutocompleteInputSerializer,
AddressCreateSerializer, AddressCreateSerializer,
AddressSuggestionSerializer, AddressSuggestionSerializer,
DoFeedbackSerializer,
) )
from core.utils import format_attributes from core.utils import format_attributes
from core.utils.messages import permission_denied_message from core.utils.messages import permission_denied_message
@ -213,11 +215,25 @@ class ProductViewSet(EvibesViewSet):
obj = queryset.filter(slug=lookup_value).first() obj = queryset.filter(slug=lookup_value).first()
if not obj: if not obj:
raise Http404(f"No Product found matching uuid or slug '{lookup_value}'") name = "Product"
raise Http404(f"{name} does not exist: {uuid}")
self.check_object_permissions(self.request, obj) self.check_object_permissions(self.request, obj)
return 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)
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): class VendorViewSet(EvibesViewSet):
queryset = Vendor.objects.all() queryset = Vendor.objects.all()
@ -322,7 +338,8 @@ class OrderViewSet(EvibesViewSet):
case _: case _:
raise TypeError(_(f"wrong type came from order.buy() method: {type(instance)!s}")) raise TypeError(_(f"wrong type came from order.buy() method: {type(instance)!s}"))
except Order.DoesNotExist: except Order.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND, data={"detail": _(f"order {lookup_val} not found")}) 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") @action(detail=False, methods=["post"], url_path="buy_unregistered")
@method_decorator(ratelimit(key="ip", rate="5/h" if not DEBUG else "888/h")) @method_decorator(ratelimit(key="ip", rate="5/h" if not DEBUG else "888/h"))
@ -414,6 +431,7 @@ class OrderViewSet(EvibesViewSet):
return Response(status=status.HTTP_404_NOT_FOUND) return Response(status=status.HTTP_404_NOT_FOUND)
@extend_schema_view(**ORDER_PRODUCT_SCHEMA)
class OrderProductViewSet(EvibesViewSet): class OrderProductViewSet(EvibesViewSet):
queryset = OrderProduct.objects.all() queryset = OrderProduct.objects.all()
filter_backends = [DjangoFilterBackend] filter_backends = [DjangoFilterBackend]
@ -421,6 +439,7 @@ class OrderProductViewSet(EvibesViewSet):
serializer_class = AttributeGroupDetailSerializer serializer_class = AttributeGroupDetailSerializer
action_serializer_classes = { action_serializer_classes = {
"list": OrderProductSimpleSerializer, "list": OrderProductSimpleSerializer,
"do_feedback": DoFeedbackSerializer,
} }
def get_queryset(self): def get_queryset(self):
@ -432,6 +451,29 @@ class OrderProductViewSet(EvibesViewSet):
return qs.filter(user=user) 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): class ProductImageViewSet(EvibesViewSet):
queryset = ProductImage.objects.all() queryset = ProductImage.objects.all()