Compare commits

..

No commits in common. "1f39535f05fd5068dcdbed390cc30442c8dbc2be" and "18bb585a132f356e9dfa21aa409f9f3f7d521dd7" have entirely different histories.

5 changed files with 52 additions and 64 deletions

View file

@ -469,16 +469,22 @@ class Category(NiceModel, MPTTModel):
).distinct() ).distinct()
@cached_property @cached_property
def min_price(self) -> Decimal: def min_price(self) -> float:
return self.products.filter(is_active=True, stocks__is_active=True).aggregate( return (
Min("stocks__price") self.products.filter(is_active=True, stocks__is_active=True).aggregate(
)["stocks__price__min"] or Decimal("0.00") Min("stocks__price")
)["stocks__price__min"]
or 0.0
)
@cached_property @cached_property
def max_price(self) -> Decimal: def max_price(self) -> float:
return self.products.filter(is_active=True, stocks__is_active=True).aggregate( return (
Max("stocks__price") self.products.filter(is_active=True, stocks__is_active=True).aggregate(
)["stocks__price__max"] or Decimal("0.00") Max("stocks__price")
)["stocks__price__max"]
or 0.0
)
@cached_property @cached_property
def seo_description(self) -> str: def seo_description(self) -> str:
@ -770,7 +776,7 @@ class Product(NiceModel):
).order_by("-discount_percent") ).order_by("-discount_percent")
@cached_property @cached_property
def discount_price(self) -> Decimal | None: def discount_price(self) -> float | None:
promo = self.promos.first() promo = self.promos.first()
return (self.price / 100) * promo.discount_percent if promo else None return (self.price / 100) * promo.discount_percent if promo else None
@ -789,11 +795,11 @@ class Product(NiceModel):
return Feedback.objects.filter(order_product__product_id=self.pk).count() return Feedback.objects.filter(order_product__product_id=self.pk).count()
@property @property
def price(self: Self) -> Decimal: def price(self: Self) -> float:
stock = ( stock = (
self.stocks.filter(is_active=True).only("price").order_by("-price").first() self.stocks.filter(is_active=True).only("price").order_by("-price").first()
) )
return Decimal(round(stock.price, 2)) if stock else Decimal("0.00") return round(stock.price, 2) if stock else 0.0
@cached_property @cached_property
def quantity(self) -> int: def quantity(self) -> int:

View file

@ -3,7 +3,7 @@ from contextlib import suppress
from typing import Any from typing import Any
from drf_spectacular.utils import extend_schema_field from drf_spectacular.utils import extend_schema_field
from rest_framework.fields import DecimalField, JSONField, SerializerMethodField from rest_framework.fields import JSONField, SerializerMethodField
from rest_framework.serializers import ListSerializer, ModelSerializer from rest_framework.serializers import ListSerializer, ModelSerializer
from rest_framework_recursive.fields import RecursiveField from rest_framework_recursive.fields import RecursiveField
@ -64,12 +64,8 @@ class CategoryDetailSerializer(ModelSerializer):
description = SerializerMethodField() description = SerializerMethodField()
filterable_attributes = SerializerMethodField() filterable_attributes = SerializerMethodField()
brands = BrandSimpleSerializer(many=True, read_only=True) brands = BrandSimpleSerializer(many=True, read_only=True)
min_price = DecimalField( min_price = SerializerMethodField()
max_digits=12, decimal_places=2, read_only=True, coerce_to_string=False max_price = SerializerMethodField()
)
max_price = DecimalField(
max_digits=12, decimal_places=2, read_only=True, coerce_to_string=False
)
class Meta: class Meta:
model = Category model = Category
@ -111,6 +107,12 @@ class CategoryDetailSerializer(ModelSerializer):
return list(serializer.data) return list(serializer.data)
return [] return []
def get_min_price(self, obj: Category) -> float:
return obj.min_price
def get_max_price(self, obj: Category) -> float:
return obj.max_price
class BrandDetailSerializer(ModelSerializer): class BrandDetailSerializer(ModelSerializer):
categories = CategorySimpleSerializer(many=True) categories = CategorySimpleSerializer(many=True)
@ -279,18 +281,10 @@ class ProductDetailSerializer(ModelSerializer):
description = SerializerMethodField() description = SerializerMethodField()
rating = SerializerMethodField() rating = SerializerMethodField()
price = DecimalField( price = SerializerMethodField()
max_digits=12, decimal_places=2, read_only=True, coerce_to_string=False
)
quantity = SerializerMethodField() quantity = SerializerMethodField()
feedbacks_count = SerializerMethodField() feedbacks_count = SerializerMethodField()
discount_price = DecimalField( discount_price = SerializerMethodField()
max_digits=12,
decimal_places=2,
read_only=True,
allow_null=True,
coerce_to_string=False,
)
personal_orders_only = SerializerMethodField() personal_orders_only = SerializerMethodField()
class Meta: class Meta:
@ -325,6 +319,9 @@ class ProductDetailSerializer(ModelSerializer):
def get_rating(self, obj: Product) -> float: def get_rating(self, obj: Product) -> float:
return obj.rating return obj.rating
def get_price(self, obj: Product) -> float:
return obj.price
def get_feedbacks_count(self, obj: Product) -> int: def get_feedbacks_count(self, obj: Product) -> int:
return obj.feedbacks_count return obj.feedbacks_count
@ -334,6 +331,9 @@ class ProductDetailSerializer(ModelSerializer):
def get_quantity(self, obj: Product) -> int: def get_quantity(self, obj: Product) -> int:
return obj.quantity return obj.quantity
def get_discount_price(self, obj: Product) -> float | None:
return obj.discount_price
class PromotionDetailSerializer(ModelSerializer): class PromotionDetailSerializer(ModelSerializer):
products = ProductDetailSerializer( products = ProductDetailSerializer(
@ -416,9 +416,7 @@ class OrderDetailSerializer(ModelSerializer):
order_products = OrderProductDetailSerializer( order_products = OrderProductDetailSerializer(
many=True, many=True,
) )
total_price = DecimalField( total_price = SerializerMethodField(read_only=True)
max_digits=12, decimal_places=2, read_only=True, coerce_to_string=False
)
class Meta: class Meta:
model = Order model = Order
@ -435,3 +433,6 @@ class OrderDetailSerializer(ModelSerializer):
"created", "created",
"modified", "modified",
] ]
def get_total_price(self, obj: Order) -> float:
return obj.total_price # ty: ignore[invalid-return-type]

View file

@ -1,6 +1,6 @@
from typing import Any from typing import Any
from rest_framework.fields import DecimalField, JSONField, SerializerMethodField from rest_framework.fields import JSONField, SerializerMethodField
from rest_framework.relations import PrimaryKeyRelatedField from rest_framework.relations import PrimaryKeyRelatedField
from rest_framework.serializers import ModelSerializer from rest_framework.serializers import ModelSerializer
@ -140,19 +140,11 @@ class ProductSimpleSerializer(ModelSerializer):
description = SerializerMethodField() description = SerializerMethodField()
rating = SerializerMethodField() rating = SerializerMethodField()
price = DecimalField( price = SerializerMethodField()
max_digits=12, decimal_places=2, read_only=True, coerce_to_string=False
)
quantity = SerializerMethodField() quantity = SerializerMethodField()
feedbacks_count = SerializerMethodField() feedbacks_count = SerializerMethodField()
personal_orders_only = SerializerMethodField() personal_orders_only = SerializerMethodField()
discount_price = DecimalField( discount_price = SerializerMethodField()
max_digits=12,
decimal_places=2,
read_only=True,
allow_null=True,
coerce_to_string=False,
)
class Meta: class Meta:
model = Product model = Product
@ -184,6 +176,9 @@ class ProductSimpleSerializer(ModelSerializer):
def get_rating(self, obj: Product) -> float: def get_rating(self, obj: Product) -> float:
return obj.rating return obj.rating
def get_price(self, obj: Product) -> float:
return obj.price
def get_feedbacks_count(self, obj: Product) -> int: def get_feedbacks_count(self, obj: Product) -> int:
return obj.feedbacks_count return obj.feedbacks_count
@ -193,6 +188,9 @@ class ProductSimpleSerializer(ModelSerializer):
def get_personal_orders_only(self, obj: Product) -> bool: def get_personal_orders_only(self, obj: Product) -> bool:
return obj.personal_orders_only return obj.personal_orders_only
def get_discount_price(self, obj: Product) -> float | None:
return obj.discount_price
class VendorSimpleSerializer(ModelSerializer): class VendorSimpleSerializer(ModelSerializer):
class Meta: class Meta:
@ -288,9 +286,7 @@ class OrderSimpleSerializer(ModelSerializer):
billing_address = AddressSerializer(read_only=True, required=False) billing_address = AddressSerializer(read_only=True, required=False)
shipping_address = AddressSerializer(read_only=True, required=False) shipping_address = AddressSerializer(read_only=True, required=False)
attributes = JSONField(required=False) attributes = JSONField(required=False)
total_price = DecimalField( total_price = SerializerMethodField(read_only=True)
max_digits=12, decimal_places=2, read_only=True, coerce_to_string=False
)
class Meta: class Meta:
model = Order model = Order
@ -309,3 +305,6 @@ class OrderSimpleSerializer(ModelSerializer):
"created", "created",
"modified", "modified",
] ]
def get_total_price(self, obj: Order) -> float:
return obj.total_price # ty: ignore[invalid-return-type]

View file

@ -56,7 +56,7 @@ def product_schema(product, images, rating=None):
offers.append( offers.append(
{ {
"@type": "Offer", "@type": "Offer",
"price": float(round(stock.price, 2)), "price": round(stock.price, 2),
"priceCurrency": settings.CURRENCY_CODE, "priceCurrency": settings.CURRENCY_CODE,
"availability": "https://schema.org/InStock" "availability": "https://schema.org/InStock"
if stock.quantity > 0 if stock.quantity > 0

View file

@ -135,24 +135,6 @@ class CustomGraphQLView(FileUploadGraphQLView):
def get_context(self, request): def get_context(self, request):
return request return request
@staticmethod
def json_encode(request, d, pretty=False):
from decimal import Decimal
import orjson
def _default(obj):
if isinstance(obj, Decimal):
return float(obj)
raise TypeError(
f"Object of type {type(obj).__name__} is not JSON serializable"
)
opts = orjson.OPT_NON_STR_KEYS
if pretty or request.GET.get("pretty"):
opts |= orjson.OPT_INDENT_2 | orjson.OPT_SORT_KEYS
return orjson.dumps(d, default=_default, option=opts).decode("utf-8")
@extend_schema_view(**CUSTOM_OPENAPI_SCHEMA) @extend_schema_view(**CUSTOM_OPENAPI_SCHEMA)
class CustomSpectacularAPIView(SpectacularAPIView): class CustomSpectacularAPIView(SpectacularAPIView):