Files
hossein-por-shop/backend/order/models.py
T
Parsa Nazer d29ed8e35b 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.
2025-11-15 11:00:33 +03:30

251 lines
9.6 KiB
Python

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
from django.utils import timezone
from django_jalali.db import models as jmodels
from django.core.exceptions import ValidationError
from django.conf import settings
class DiscountCode(models.Model):
code = models.CharField(max_length=50, verbose_name='کد تخفیف')
percent = models.DecimalField(
max_digits=4, decimal_places=2, verbose_name='درصد')
quantity = models.PositiveIntegerField(verbose_name='تعداد')
expiration_date = models.DateTimeField(verbose_name='تاریخ انقضا')
def __str__(self):
return self.code
class Meta:
verbose_name = 'کد تخفیف'
verbose_name_plural = 'کد های تخفیف'
def is_valid(self):
return self.expiration_date > timezone.now() and self.quantity > 0
def not_valid_reason(self):
if self.expiration_date > timezone.now() and self.quantity > 0:
return 'این کد معتبر میباشد'
elif not self.expiration_date > timezone.now():
return 'تایم کد تخفیف تمام شده'
elif not self.quantity > 0:
return 'این کد تخفیف تمام شده است'
else:
print('log later bug')
class Cart(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='carts'
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
address = models.ForeignKey(UserAddressModel, on_delete=models.SET_NULL,
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 = 'سبد خرید'
verbose_name_plural = 'سبد های خرید'
def __str__(self):
return f"Cart for {self.user.email}"
def clear_cart(self):
self.items.all().delete()
self.discount_code = None
self.special_discount_code = None
self.save()
@property
def discount_code_amount(self):
if self.discount_code:
return int(int(self.cart_total - self.items_discount_amount) * (self.discount_code.percent / 100))
else:
return 0
@property
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 + self.special_discount_total)
@property
def tax_amount(self):
return int(self.total_before_tax * settings.DEFAULT_TAX_RATE / 100)
@property
def cart_total(self):
return sum(item.price_before_discount for item in self.items.all())
@property
def final_price(self):
return self.total_before_tax + self.tax_amount
class CartItem(models.Model):
cart = models.ForeignKey(
Cart,
on_delete=models.CASCADE,
related_name='items'
)
product_variant = models.ForeignKey(
ProductVariant,
on_delete=models.CASCADE,
related_name='cart_items'
)
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 = 'ایتم سبد خرید'
verbose_name_plural = 'ایتم های سبد خرید'
unique_together = ('cart', 'product_variant')
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 - self.item_special_discount_amount
@property
def discount(self):
return self.product_variant.discount
class OrderModel(models.Model):
objects = jmodels.jManager()
STATUS_CHOICES = [
('ADMIN_PENDING', 'در انتظار تایید'),
('PENDING', 'درحال پردازش'),
('POSTED', 'ارسال شده'),
('RECEIVED', 'تحویل شده'),
('CANCELED', 'لغو شده'),
('REFUNDED', 'مرجوع شده'),
]
user = models.ForeignKey(User, on_delete=models.SET_NULL,
null=True, related_name='orders', verbose_name='کاربر')
address = models.ForeignKey(UserAddressModel, on_delete=models.SET_NULL,
related_name='orders', null=True, verbose_name='ادرس')
created_at = jmodels.jDateField(
blank=True, null=True, verbose_name="تاریخ ثبت سفارش")
is_paid = models.BooleanField(default=False, verbose_name="وضعیت پرداخت")
discount_code = models.ForeignKey(
DiscountCode, on_delete=models.PROTECT, null=True, blank=True, verbose_name="کدتخفیف")
status = models.CharField(max_length=20, choices=STATUS_CHOICES,
default='ADMIN_PENDING', verbose_name="وضعیت سفارش")
discount_amount = models.BigIntegerField(
null=True, blank=True, verbose_name='مقدار کد تخفیف')
tax = models.BigIntegerField(null=True, blank=True, verbose_name='مالیات')
final_price = models.BigIntegerField(
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)
is_stock_rolled_back = models.BooleanField(
default=False, verbose_name="موجودی برگردانده شده")
def __str__(self):
return f'سفارش: {self.pk + 1000}'
class Meta:
verbose_name = 'سفارش'
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
class OrderItemModel(models.Model):
order = models.ForeignKey(
OrderModel, on_delete=models.CASCADE, related_name='items', verbose_name='سفارش')
quantity = models.PositiveSmallIntegerField(verbose_name="تعداد")
price = models.BigIntegerField(verbose_name='قیمت')
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 = 'ایتم سبد خرید'
verbose_name_plural = 'ایتم های سبد خرید'
def __str__(self):
return f'({self.product}) - ({self.order.user})'