feat: add profit and special discount fields to ProductVariant model

- Updated ProductVariant model to include 'profit' and 'special_discount_percent' fields.
- Added corresponding fields in the admin interface for ProductVariant.
- Created migration to add new fields to the database.

feat: implement special discount code functionality in cart

- Added composables for submitting and deleting special discount codes.
- Updated CartSummary and CartItem components to handle special discount codes.
- Enhanced API endpoints to support special discount operations.
- Updated global types to include special discount code details in the cart.
This commit is contained in:
Parsa Nazer
2025-11-15 11:00:33 +03:30
parent 030976044c
commit d29ed8e35b
19 changed files with 718 additions and 208 deletions
+70 -37
View File
@@ -1,3 +1,4 @@
from account.models import SpecialDiscountCode
from django.db import models, transaction
from account.models import User, UserAddressModel, PushSubscription
from product.models import ProductModel, ProductVariant, ProductImageModel
@@ -47,6 +48,8 @@ class Cart(models.Model):
related_name='carts', null=True, verbose_name='ادرس')
discount_code = models.ForeignKey(
DiscountCode, on_delete=models.PROTECT, null=True, blank=True, verbose_name="کدتخفیف")
special_discount_code = models.ForeignKey(
SpecialDiscountCode, on_delete=models.PROTECT, null=True, blank=True, verbose_name="کدتخفیف خاص")
class Meta:
verbose_name = 'سبد خرید'
@@ -58,6 +61,7 @@ class Cart(models.Model):
def clear_cart(self):
self.items.all().delete()
self.discount_code = None
self.special_discount_code = None
self.save()
@property
@@ -71,10 +75,17 @@ class Cart(models.Model):
def items_discount_amount(self):
return int(sum(item.item_discount_amount for item in self.items.all()))
@property
def special_discount_total(self):
"""Sum of all special discounts from cart items when special_discount_code is applied."""
if self.special_discount_code:
return int(sum(item.item_special_discount_amount for item in self.items.all()))
return 0
@property
def total_before_tax(self):
return self.cart_total - (self.discount_code_amount + self.items_discount_amount)
return self.cart_total - (self.discount_code_amount + self.items_discount_amount + self.special_discount_total)
@property
def tax_amount(self):
return int(self.total_before_tax * settings.DEFAULT_TAX_RATE / 100)
@@ -102,6 +113,7 @@ class CartItem(models.Model):
quantity = models.PositiveIntegerField(default=1)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
# special_discount_amount = models.BigIntegerField(default=0, verbose_name='مقدار تخفیف ویژه', help_text='تخفیف محاسبه شده از سود تنوع')
class Meta:
verbose_name = 'ایتم سبد خرید'
@@ -111,22 +123,37 @@ class CartItem(models.Model):
def __str__(self):
return f"{self.quantity} x {self.product_variant.product.name} in cart {self.cart.id}"
@property
def special_discount_amount(self):
"""Calculate special discount for this cart item based on variant profit and special_discount_percent."""
if hasattr(self.cart, 'special_discount_code') and self.cart.special_discount_code:
return self.product_variant.special_discount_amount_per_unit * self.quantity
return 0
@property
def price_before_discount(self):
return self.quantity * self.product_variant.price_before_discount
@property
def item_discount_amount(self):
return self.product_variant.discount_amount * self.quantity
@property
def item_special_discount_amount(self):
"""Calculate special discount for this cart item based on variant profit and special_discount_percent."""
if hasattr(self.cart, 'special_discount_code') and self.cart.special_discount_code:
return self.product_variant.special_discount_amount_per_unit * self.quantity
return 0
@property
def price_after_discount(self):
return self.price_before_discount - self.item_discount_amount
return self.price_before_discount - self.item_discount_amount - self.item_special_discount_amount
@property
def discount(self):
return self.product_variant.discount
class OrderModel(models.Model):
objects = jmodels.jManager()
STATUS_CHOICES = [
@@ -155,9 +182,12 @@ class OrderModel(models.Model):
null=True, blank=True, verbose_name='قیمت نهایی')
cart_total = models.BigIntegerField(
null=True, blank=True, verbose_name='کل سبد خرید')
special_discount_total = models.BigIntegerField(
null=True, blank=True, verbose_name='مجموع تخفیف ویژه')
cart = models.ForeignKey(
Cart, on_delete=models.CASCADE, null=True, blank=True)
cart = models.ForeignKey(Cart, on_delete=models.CASCADE, null=True, blank=True)
is_stock_rolled_back = models.BooleanField(
default=False, verbose_name="موجودی برگردانده شده")
@@ -169,36 +199,37 @@ class OrderModel(models.Model):
verbose_name_plural = 'سفارشات'
def rollback_stock(self):
"""
Rollback stock quantities for all items in this order
Returns True if successful, False otherwise
"""
if self.is_stock_rolled_back:
return False
# if not self.cart:
# return False
try:
# Get all cart items and rollback their stock
for order_item in self.items.all():
product = order_item.product
# Add back the quantity to stock
product.in_stock += order_item.quantity
product.save()
# Mark as rolled back
self.is_stock_rolled_back = True
self.save(update_fields=['is_stock_rolled_back'])
self.status = 'CANCELED'
self.save()
return True
except Exception as e:
print(e)
# Log the error if you have logging setup
# logger.error(f"Failed to rollback stock for order {self.pk}: {e}")
return False
"""
Rollback stock quantities for all items in this order
Returns True if successful, False otherwise
"""
if self.is_stock_rolled_back:
return False
# if not self.cart:
# return False
try:
# Get all cart items and rollback their stock
for order_item in self.items.all():
product = order_item.product
# Add back the quantity to stock
product.in_stock += order_item.quantity
product.save()
# Mark as rolled back
self.is_stock_rolled_back = True
self.save(update_fields=['is_stock_rolled_back'])
self.status = 'CANCELED'
self.save()
return True
except Exception as e:
print(e)
# Log the error if you have logging setup
# logger.error(f"Failed to rollback stock for order {self.pk}: {e}")
return False
class OrderItemModel(models.Model):
order = models.ForeignKey(
@@ -208,6 +239,8 @@ class OrderItemModel(models.Model):
product = models.ForeignKey(
ProductVariant, on_delete=models.PROTECT, verbose_name="محصول")
discount_percent = models.SmallIntegerField(verbose_name='درصد تخفیف')
special_discount_amount = models.BigIntegerField(
default=0, verbose_name='مقدار تخفیف ویژه')
class Meta:
verbose_name = 'ایتم سبد خرید'