from django.contrib.auth.models import AbstractUser from django.db import models from django.db.models.signals import post_save from django.dispatch import receiver class User(AbstractUser): pass class Profile(models.Model): THEME_CHOICES = ( ('light', 'Light'), ('dark', 'Dark'), ('compact', 'Compact'), ('expressive', 'Expressive'), ('technical', 'Technical'), ) user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile') is_pro = models.BooleanField(default=False) is_seller = models.BooleanField(default=False) theme_preference = models.CharField(max_length=10, choices=THEME_CHOICES, default='dark') # Keeping this simple text field for legacy/simple addressing, whilst adding robust Address model below shipping_address = models.TextField(blank=True) stripe_customer_id = models.CharField(max_length=100, blank=True, null=True) def __str__(self): return self.user.username @receiver(post_save, sender=User) def create_user_profile(sender, instance, created, **kwargs): if created: Profile.objects.create(user=instance) @receiver(post_save, sender=User) def save_user_profile(sender, instance, **kwargs): instance.profile.save() class Address(models.Model): ADDRESS_TYPE_CHOICES = ( ('shipping', 'Shipping'), ('billing', 'Billing'), ) user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='addresses') name = models.CharField(max_length=100) street = models.CharField(max_length=200) city = models.CharField(max_length=100) state = models.CharField(max_length=100) zip_code = models.CharField(max_length=20) address_type = models.CharField(max_length=20, choices=ADDRESS_TYPE_CHOICES, default='shipping') is_default = models.BooleanField(default=False) def __str__(self): return f"{self.name} - {self.street}" class PaymentMethod(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='payment_methods') billing_address = models.ForeignKey(Address, on_delete=models.SET_NULL, null=True, blank=True, related_name='payment_methods') brand = models.CharField(max_length=50) # Visa, Mastercard last4 = models.CharField(max_length=4) exp_month = models.PositiveIntegerField() exp_year = models.PositiveIntegerField() is_default = models.BooleanField(default=False) # Encrypted fields card_number_encrypted = models.BinaryField(blank=True, null=True) cvv_encrypted = models.BinaryField(blank=True, null=True) @property def card_number(self): try: from store.utils import Encryptor return Encryptor.decrypt(self.card_number_encrypted) except ImportError: # Fallback if store isn't ready or circular import return None @card_number.setter def card_number(self, value): from store.utils import Encryptor self.card_number_encrypted = Encryptor.encrypt(value) if value and len(str(value)) >= 4: self.last4 = str(value)[-4:] @property def cvv(self): from store.utils import Encryptor return Encryptor.decrypt(self.cvv_encrypted) @cvv.setter def cvv(self, value): from store.utils import Encryptor self.cvv_encrypted = Encryptor.encrypt(value) def __str__(self): return f"{self.brand} ending in {self.last4}" class Buyer(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='buyer_profile') created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return self.user.username @receiver(post_save, sender=User) def create_buyer_profile(sender, instance, created, **kwargs): # We only auto-create Buyer if NOT a seller? # Or do we create it manually in views? # The requirement says "/user/register creates a buy account and /sell/register creates a seller account" # So we probably shouldn't auto-create blindly for EVERY user if we want them distinct. # However, easy way is to create it manually in the view. # I will NOT add a signal here to avoid auto-creation when we might want a pure Seller. pass