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 OrderModel(models.Model): objects = jmodels.jManager() STATUS_CHOICES = [ ('CART', 'در سبد خرید'), ('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='CART', 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='کل سبد خرید') def __str__(self): return f'سفارش: {self.pk + 1000}' class Meta: verbose_name = 'سفارش' verbose_name_plural = 'سفارشات' def _cal_discount_amount(self, cart_total): discount_percent = self.discount_code.percent if self.discount_code else 0 return int(cart_total * discount_percent / 100) def _cal_tax(self, cart_total, discount_amount): tax_rate = getattr(settings, 'DEFAULT_TAX_RATE', 20) return int((cart_total - discount_amount) * tax_rate / 100) def _cal_cart_total(self): from django.db.models import Sum, F, FloatField return self.items.aggregate( total=Sum(F('price') * (1 - F('discount_percent')/100) * F('quantity'), output_field=FloatField() )).get('total') or 0 def _cal_final_price(self, cart_total, discount_amount, tax): return cart_total - discount_amount + tax def update_order(self): if self.status == 'CART': cart_total = self._cal_cart_total() discount_amount = self._cal_discount_amount(cart_total) self.discount_amount = discount_amount self.cart_total = cart_total tax = self._cal_tax(cart_total, discount_amount) self.tax = tax self.final_price = self._cal_final_price(cart_total, discount_amount, tax) self.save() else: pass 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 set_or_update_fields(self): self.price = self.product.price self.discount_percent = self.product.discount def __str__(self): return f'({self.product}) - ({self.order.user})' def save(self, *args, **kwargs): self.clean() self.set_or_update_fields() super().save(*args, **kwargs) self.order.update_order() def delete(self, *args, **kwargs): self.clean() order = self.order super().delete(*args, **kwargs) order.update_order() def clean(self): if self.pk and self.order.status != "CART": raise ValidationError("ایتم ها فقط در حالت سبد خرید قابل ادیت هستند")