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="کدتخفیف") 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.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 total_before_tax(self): return self.cart_total - (self.discount_code_amount + self.items_discount_amount) @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) 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 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 price_after_discount(self): return self.price_before_discount - self.item_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='کل سبد خرید') 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='درصد تخفیف') class Meta: verbose_name = 'ایتم سبد خرید' verbose_name_plural = 'ایتم های سبد خرید' def __str__(self): return f'({self.product}) - ({self.order.user})'