Merge branch 'master' into storefront-nuxt
This commit is contained in:
commit
1f39535f05
5 changed files with 64 additions and 52 deletions
|
|
@ -469,22 +469,16 @@ class Category(NiceModel, MPTTModel):
|
|||
).distinct()
|
||||
|
||||
@cached_property
|
||||
def min_price(self) -> float:
|
||||
return (
|
||||
self.products.filter(is_active=True, stocks__is_active=True).aggregate(
|
||||
Min("stocks__price")
|
||||
)["stocks__price__min"]
|
||||
or 0.0
|
||||
)
|
||||
def min_price(self) -> Decimal:
|
||||
return self.products.filter(is_active=True, stocks__is_active=True).aggregate(
|
||||
Min("stocks__price")
|
||||
)["stocks__price__min"] or Decimal("0.00")
|
||||
|
||||
@cached_property
|
||||
def max_price(self) -> float:
|
||||
return (
|
||||
self.products.filter(is_active=True, stocks__is_active=True).aggregate(
|
||||
Max("stocks__price")
|
||||
)["stocks__price__max"]
|
||||
or 0.0
|
||||
)
|
||||
def max_price(self) -> Decimal:
|
||||
return self.products.filter(is_active=True, stocks__is_active=True).aggregate(
|
||||
Max("stocks__price")
|
||||
)["stocks__price__max"] or Decimal("0.00")
|
||||
|
||||
@cached_property
|
||||
def seo_description(self) -> str:
|
||||
|
|
@ -776,7 +770,7 @@ class Product(NiceModel):
|
|||
).order_by("-discount_percent")
|
||||
|
||||
@cached_property
|
||||
def discount_price(self) -> float | None:
|
||||
def discount_price(self) -> Decimal | None:
|
||||
promo = self.promos.first()
|
||||
return (self.price / 100) * promo.discount_percent if promo else None
|
||||
|
||||
|
|
@ -795,11 +789,11 @@ class Product(NiceModel):
|
|||
return Feedback.objects.filter(order_product__product_id=self.pk).count()
|
||||
|
||||
@property
|
||||
def price(self: Self) -> float:
|
||||
def price(self: Self) -> Decimal:
|
||||
stock = (
|
||||
self.stocks.filter(is_active=True).only("price").order_by("-price").first()
|
||||
)
|
||||
return round(stock.price, 2) if stock else 0.0
|
||||
return Decimal(round(stock.price, 2)) if stock else Decimal("0.00")
|
||||
|
||||
@cached_property
|
||||
def quantity(self) -> int:
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from contextlib import suppress
|
|||
from typing import Any
|
||||
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
from rest_framework.fields import JSONField, SerializerMethodField
|
||||
from rest_framework.fields import DecimalField, JSONField, SerializerMethodField
|
||||
from rest_framework.serializers import ListSerializer, ModelSerializer
|
||||
from rest_framework_recursive.fields import RecursiveField
|
||||
|
||||
|
|
@ -64,8 +64,12 @@ class CategoryDetailSerializer(ModelSerializer):
|
|||
description = SerializerMethodField()
|
||||
filterable_attributes = SerializerMethodField()
|
||||
brands = BrandSimpleSerializer(many=True, read_only=True)
|
||||
min_price = SerializerMethodField()
|
||||
max_price = SerializerMethodField()
|
||||
min_price = DecimalField(
|
||||
max_digits=12, decimal_places=2, read_only=True, coerce_to_string=False
|
||||
)
|
||||
max_price = DecimalField(
|
||||
max_digits=12, decimal_places=2, read_only=True, coerce_to_string=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Category
|
||||
|
|
@ -107,12 +111,6 @@ class CategoryDetailSerializer(ModelSerializer):
|
|||
return list(serializer.data)
|
||||
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):
|
||||
categories = CategorySimpleSerializer(many=True)
|
||||
|
|
@ -281,10 +279,18 @@ class ProductDetailSerializer(ModelSerializer):
|
|||
|
||||
description = SerializerMethodField()
|
||||
rating = SerializerMethodField()
|
||||
price = SerializerMethodField()
|
||||
price = DecimalField(
|
||||
max_digits=12, decimal_places=2, read_only=True, coerce_to_string=False
|
||||
)
|
||||
quantity = SerializerMethodField()
|
||||
feedbacks_count = SerializerMethodField()
|
||||
discount_price = SerializerMethodField()
|
||||
discount_price = DecimalField(
|
||||
max_digits=12,
|
||||
decimal_places=2,
|
||||
read_only=True,
|
||||
allow_null=True,
|
||||
coerce_to_string=False,
|
||||
)
|
||||
personal_orders_only = SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
|
|
@ -319,9 +325,6 @@ class ProductDetailSerializer(ModelSerializer):
|
|||
def get_rating(self, obj: Product) -> float:
|
||||
return obj.rating
|
||||
|
||||
def get_price(self, obj: Product) -> float:
|
||||
return obj.price
|
||||
|
||||
def get_feedbacks_count(self, obj: Product) -> int:
|
||||
return obj.feedbacks_count
|
||||
|
||||
|
|
@ -331,9 +334,6 @@ class ProductDetailSerializer(ModelSerializer):
|
|||
def get_quantity(self, obj: Product) -> int:
|
||||
return obj.quantity
|
||||
|
||||
def get_discount_price(self, obj: Product) -> float | None:
|
||||
return obj.discount_price
|
||||
|
||||
|
||||
class PromotionDetailSerializer(ModelSerializer):
|
||||
products = ProductDetailSerializer(
|
||||
|
|
@ -416,7 +416,9 @@ class OrderDetailSerializer(ModelSerializer):
|
|||
order_products = OrderProductDetailSerializer(
|
||||
many=True,
|
||||
)
|
||||
total_price = SerializerMethodField(read_only=True)
|
||||
total_price = DecimalField(
|
||||
max_digits=12, decimal_places=2, read_only=True, coerce_to_string=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Order
|
||||
|
|
@ -433,6 +435,3 @@ class OrderDetailSerializer(ModelSerializer):
|
|||
"created",
|
||||
"modified",
|
||||
]
|
||||
|
||||
def get_total_price(self, obj: Order) -> float:
|
||||
return obj.total_price # ty: ignore[invalid-return-type]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from typing import Any
|
||||
|
||||
from rest_framework.fields import JSONField, SerializerMethodField
|
||||
from rest_framework.fields import DecimalField, JSONField, SerializerMethodField
|
||||
from rest_framework.relations import PrimaryKeyRelatedField
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
|
||||
|
|
@ -140,11 +140,19 @@ class ProductSimpleSerializer(ModelSerializer):
|
|||
|
||||
description = SerializerMethodField()
|
||||
rating = SerializerMethodField()
|
||||
price = SerializerMethodField()
|
||||
price = DecimalField(
|
||||
max_digits=12, decimal_places=2, read_only=True, coerce_to_string=False
|
||||
)
|
||||
quantity = SerializerMethodField()
|
||||
feedbacks_count = SerializerMethodField()
|
||||
personal_orders_only = SerializerMethodField()
|
||||
discount_price = SerializerMethodField()
|
||||
discount_price = DecimalField(
|
||||
max_digits=12,
|
||||
decimal_places=2,
|
||||
read_only=True,
|
||||
allow_null=True,
|
||||
coerce_to_string=False,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Product
|
||||
|
|
@ -176,9 +184,6 @@ class ProductSimpleSerializer(ModelSerializer):
|
|||
def get_rating(self, obj: Product) -> float:
|
||||
return obj.rating
|
||||
|
||||
def get_price(self, obj: Product) -> float:
|
||||
return obj.price
|
||||
|
||||
def get_feedbacks_count(self, obj: Product) -> int:
|
||||
return obj.feedbacks_count
|
||||
|
||||
|
|
@ -188,9 +193,6 @@ class ProductSimpleSerializer(ModelSerializer):
|
|||
def get_personal_orders_only(self, obj: Product) -> bool:
|
||||
return obj.personal_orders_only
|
||||
|
||||
def get_discount_price(self, obj: Product) -> float | None:
|
||||
return obj.discount_price
|
||||
|
||||
|
||||
class VendorSimpleSerializer(ModelSerializer):
|
||||
class Meta:
|
||||
|
|
@ -286,7 +288,9 @@ class OrderSimpleSerializer(ModelSerializer):
|
|||
billing_address = AddressSerializer(read_only=True, required=False)
|
||||
shipping_address = AddressSerializer(read_only=True, required=False)
|
||||
attributes = JSONField(required=False)
|
||||
total_price = SerializerMethodField(read_only=True)
|
||||
total_price = DecimalField(
|
||||
max_digits=12, decimal_places=2, read_only=True, coerce_to_string=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Order
|
||||
|
|
@ -305,6 +309,3 @@ class OrderSimpleSerializer(ModelSerializer):
|
|||
"created",
|
||||
"modified",
|
||||
]
|
||||
|
||||
def get_total_price(self, obj: Order) -> float:
|
||||
return obj.total_price # ty: ignore[invalid-return-type]
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ def product_schema(product, images, rating=None):
|
|||
offers.append(
|
||||
{
|
||||
"@type": "Offer",
|
||||
"price": round(stock.price, 2),
|
||||
"price": float(round(stock.price, 2)),
|
||||
"priceCurrency": settings.CURRENCY_CODE,
|
||||
"availability": "https://schema.org/InStock"
|
||||
if stock.quantity > 0
|
||||
|
|
|
|||
|
|
@ -135,6 +135,24 @@ class CustomGraphQLView(FileUploadGraphQLView):
|
|||
def get_context(self, 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)
|
||||
class CustomSpectacularAPIView(SpectacularAPIView):
|
||||
|
|
|
|||
Loading…
Reference in a new issue