diff --git a/core/filters.py b/core/filters.py index 3122fdf9..d9706a30 100644 --- a/core/filters.py +++ b/core/filters.py @@ -16,6 +16,7 @@ from django.db.models import ( When, Max, Prefetch, + BooleanField, ) from django.db.models.functions import Coalesce from django.utils.http import urlsafe_base64_decode @@ -92,6 +93,7 @@ class ProductFilter(FilterSet): ("price_order", "price"), ("sku", "sku"), ("?", "random"), + ("personal_order_only", "personal_order_only"), ), initial="uuid", ) @@ -279,8 +281,8 @@ class ProductFilter(FilterSet): ordering_param = self.data.get("order_by", "") if ordering_param: - order_fields = [field.strip() for field in ordering_param.split(",")] - if any(field.lstrip("-") == "rating" for field in order_fields): + order_fields_no_sign = [field.strip("-") for field in ordering_param.split(",")] + if "rating" in order_fields_no_sign: feedback_qs = ( Feedback.objects.filter(order_product__product_id=OuterRef("pk")) .values("order_product__product_id") @@ -293,6 +295,58 @@ class ProductFilter(FilterSet): Value(0, output_field=FloatField()), ) ) + + if ordering_param and any(f.lstrip("-") == "price" for f in ordering_param.split(",")): + qs = qs.annotate( + price_order=Coalesce( + Max("stocks__price"), + Value(0.0), + output_field=FloatField(), + ) + ) + + qs = qs.annotate( + has_stock=Max( + Case( + When(stocks__quantity__gt=0, then=Value(1)), + default=Value(0), + output_field=IntegerField(), + ) + ), + has_price=Max( + Case( + When(stocks__price__gt=0, then=Value(1)), + default=Value(0), + output_field=IntegerField(), + ) + ), + ).annotate( + personal_order_only=Case( + When(has_stock=0, has_price=1, then=Value(True)), + default=Value(False), + output_field=BooleanField(), + ) + ) + + requested = [part.strip() for part in ordering_param.split(",") if part.strip()] if ordering_param else [] + mapped_requested = [] + for part in requested: + desc = part.startswith("-") + key = part.lstrip("-") + if key == "price": + key = "price_order" + if key == "random": + key = "?" + mapped_requested.append(key) + continue + mapped_requested.append(f"-{key}" if desc else key) + + has_personal_in_request = any(p.lstrip("-") == "personal_order_only" for p in mapped_requested) + final_ordering = (["personal_order_only"] if not has_personal_in_request else []) + mapped_requested + + if final_ordering: + qs = qs.order_by(*final_ordering) + return qs.distinct() diff --git a/core/graphene/object_types.py b/core/graphene/object_types.py index 1106fd63..d0e60f40 100644 --- a/core/graphene/object_types.py +++ b/core/graphene/object_types.py @@ -501,6 +501,7 @@ class ProductType(DjangoObjectType): feedbacks_count = Int(description=_("number of feedbacks")) personal_orders_only = Boolean(description=_("only available for personal orders")) seo_meta = Field(SEOMetaType, description=_("SEO Meta snapshot")) + rating = Float(description=_("rating value from 1 to 10, inclusive, or 0 if not set.")) class Meta: model = Product @@ -521,6 +522,7 @@ class ProductType(DjangoObjectType): "attribute_groups", "images", "price", + "rating", ) filter_fields = ["uuid", "name"] description = _("products") @@ -528,6 +530,9 @@ class ProductType(DjangoObjectType): def resolve_price(self: Product, _info) -> float: return self.price or 0.0 + def resolve_rating(self: Product, _info) -> float: + return self.rating or 0.0 + def resolve_feedbacks(self: Product, _info): if _info.context.user.has_perm("core.view_feedback"): return Feedback.objects.filter(order_product__product=self)