Add shopping cart and product review features
Introduces Cart and CartItem models, admin, serializers, and API endpoints for shopping cart management for both authenticated and anonymous users. Adds Review model, serializers, and endpoints for product reviews, including public creation and retrieval. Updates ProductImage ordering, enhances order save logic with notification, and improves product and order endpoints with new actions and filters. Includes related migrations for commerce, configuration, social chat, and Deutsche Post integration.
This commit is contained in:
@@ -94,6 +94,10 @@ class ProductImage(models.Model):
|
||||
|
||||
alt_text = models.CharField(max_length=150, blank=True)
|
||||
is_main = models.BooleanField(default=False)
|
||||
order = models.PositiveIntegerField(default=0, help_text="Display order (lower numbers first)")
|
||||
|
||||
class Meta:
|
||||
ordering = ['order', '-is_main', 'id']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.product.name} image"
|
||||
@@ -196,11 +200,18 @@ class Order(models.Model):
|
||||
# Keep total_price always in sync with items and discount
|
||||
self.total_price = self.calculate_total_price()
|
||||
|
||||
if self.user and self.pk is None:
|
||||
is_new = self.pk is None
|
||||
|
||||
if self.user and is_new:
|
||||
self.import_data_from_user()
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
# Send email notification for new orders
|
||||
if is_new and self.user:
|
||||
from .tasks import notify_order_successfuly_created
|
||||
notify_order_successfuly_created.delay(order=self, user=self.user)
|
||||
|
||||
|
||||
# ------------------ DOPRAVCI A ZPŮSOBY DOPRAVY ------------------
|
||||
|
||||
@@ -634,4 +645,74 @@ class Review(models.Model):
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"Review for {self.product.name} by {self.user.username}"
|
||||
return f"Review for {self.product.name} by {self.user.username}"
|
||||
|
||||
|
||||
# ------------------ SHOPPING CART ------------------
|
||||
|
||||
class Cart(models.Model):
|
||||
"""Shopping cart for both authenticated and anonymous users"""
|
||||
user = models.OneToOneField(
|
||||
settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="cart"
|
||||
)
|
||||
session_key = models.CharField(
|
||||
max_length=40,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Session key for anonymous users"
|
||||
)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Cart"
|
||||
verbose_name_plural = "Carts"
|
||||
|
||||
def __str__(self):
|
||||
if self.user:
|
||||
return f"Cart for {self.user.email}"
|
||||
return f"Anonymous cart ({self.session_key})"
|
||||
|
||||
def get_total(self):
|
||||
"""Calculate total price of all items in cart"""
|
||||
total = Decimal('0.0')
|
||||
for item in self.items.all():
|
||||
total += item.get_subtotal()
|
||||
return total
|
||||
|
||||
def get_items_count(self):
|
||||
"""Get total number of items in cart"""
|
||||
return sum(item.quantity for item in self.items.all())
|
||||
|
||||
|
||||
class CartItem(models.Model):
|
||||
"""Individual items in a shopping cart"""
|
||||
cart = models.ForeignKey(Cart, related_name='items', on_delete=models.CASCADE)
|
||||
product = models.ForeignKey(Product, on_delete=models.CASCADE)
|
||||
quantity = models.PositiveIntegerField(default=1)
|
||||
added_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Cart Item"
|
||||
verbose_name_plural = "Cart Items"
|
||||
unique_together = ('cart', 'product') # Prevent duplicate products in same cart
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.quantity}x {self.product.name} in cart"
|
||||
|
||||
def get_subtotal(self):
|
||||
"""Calculate subtotal for this cart item"""
|
||||
return self.product.price * self.quantity
|
||||
|
||||
def clean(self):
|
||||
"""Validate that product has enough stock"""
|
||||
if self.product.stock < self.quantity:
|
||||
raise ValidationError(f"Not enough stock for {self.product.name}. Available: {self.product.stock}")
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.clean()
|
||||
super().save(*args, **kwargs)
|
||||
Reference in New Issue
Block a user