from django.db import models from django.db.models.signals import post_save, post_delete from django.dispatch import receiver from Accounts.models import Profile from Boxes.models import MysteryBox from django.utils.crypto import get_random_string from django.utils.timezone import now class Cart(models.Model): user = models.OneToOneField(Profile, on_delete=models.CASCADE, related_name="cart") created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return f"Cart of {self.user.user.email}" @property def total_amount(self): return sum(item.subtotal for item in self.items.all()) class CartItem(models.Model): cart = models.ForeignKey(Cart, on_delete=models.CASCADE, related_name="items") box = models.ForeignKey(MysteryBox, on_delete=models.CASCADE) quantity = models.PositiveIntegerField(default=1) def __str__(self): return f"{self.quantity} × {self.box.title}" @property def subtotal(self): return (self.box.price or 0) * (self.quantity or 0) class Order(models.Model): STATUS_CHOICES = [ ("pending", "Pending"), ("paid", "Paid"), ("shipped", "Shipped"), ("delivered", "Delivered"), ("cancelled", "Cancelled"), ] user = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name="orders") order_number = models.CharField(max_length=50, unique=True, editable=False) total_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0.00) status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="pending") created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return f"Order {self.order_number} ({self.user.user.email})" def save(self, *args, **kwargs): # Auto-generate order number if missing if not self.order_number: self.order_number = f"ORD{now().strftime('%Y%m%d')}{get_random_string(5).upper()}" super().save(*args, **kwargs) class OrderItem(models.Model): order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name="items") box = models.ForeignKey(MysteryBox, on_delete=models.CASCADE) quantity = models.PositiveIntegerField(default=1) unit_price = models.DecimalField(max_digits=10, decimal_places=2, default=0.00) def __str__(self): return f"{self.quantity} × {self.box.title}" @property def subtotal(self): return (self.unit_price or 0) * (self.quantity or 0) class Payment(models.Model): METHOD_CHOICES = [ ("stripe", "Stripe"), ("paypal", "PayPal"), ("razorpay", "Razorpay"), ("cod", "Cash on Delivery"), ] STATUS_CHOICES = [ ("pending", "Pending"), ("successful", "Successful"), ("failed", "Failed"), ] order = models.OneToOneField(Order, on_delete=models.CASCADE, related_name="payment") payment_method = models.CharField(max_length=20, choices=METHOD_CHOICES, default="cod") transaction_id = models.CharField(max_length=150, blank=True, null=True) status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="pending") paid_at = models.DateTimeField(blank=True, null=True) def __str__(self): return f"Payment {self.status} - {self.order.order_number}" # 🔹 Signals to auto-update Order total when OrderItems change @receiver([post_save, post_delete], sender=OrderItem) def update_order_total(sender, instance, **kwargs): order = instance.order order.total_amount = sum(item.subtotal for item in order.items.all()) order.save(update_fields=["total_amount"])