35955-vm/core/models.py
Flatlogic Bot 7be9310a96 v1.1
2025-11-23 01:09:28 +00:00

205 lines
7.9 KiB
Python

from django.contrib.auth.models import AbstractUser, BaseUserManager
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.utils import timezone
class UserManager(BaseUserManager):
"""Define a model manager for User model with no username field."""
use_in_migrations = True
def _create_user(self, email, password, **extra_fields):
"""Create and save a User with the given email and password."""
if not email:
raise ValueError('The given email must be set')
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, email, password=None, **extra_fields):
"""Create and save a regular User with the given email and password."""
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email, password, **extra_fields):
"""Create and save a SuperUser with the given email and password."""
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
if extra_fields.get('is_staff') is not True:
raise ValueError('Superuser must have is_staff=True.')
if extra_fields.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True.')
return self._create_user(email, password, **extra_fields)
class Tenant(models.Model):
name = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class User(AbstractUser):
username = None
email = models.EmailField(unique=True)
tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE, null=True, blank=True)
role = models.CharField(max_length=100, blank=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = UserManager()
class BaseModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_deleted = models.BooleanField(default=False)
deleted_at = models.DateTimeField(null=True, blank=True)
created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='%(class)s_created_by')
updated_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='%(class)s_updated_by')
deleted_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='%(class)s_deleted_by')
restored_at = models.DateTimeField(null=True, blank=True)
restored_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='%(class)s_restored_by')
class Meta:
abstract = True
def delete(self, user=None, *args, **kwargs):
self.is_deleted = True
self.deleted_at = timezone.now()
if user:
self.deleted_by = user
self.save()
ActivityLog.objects.create(
actor=user,
action=f'deleted a {self.__class__.__name__}',
content_object=self,
details={'id': self.id, 'name': str(self)}
)
def restore(self, user=None, *args, **kwargs):
self.is_deleted = False
self.deleted_at = None
self.deleted_by = None
self.restored_at = timezone.now()
if user:
self.restored_by = user
self.save()
ActivityLog.objects.create(
actor=user,
action=f'restored a {self.__class__.__name__}',
content_object=self,
details={'id': self.id, 'name': str(self)}
)
class ActivityLog(models.Model):
actor = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
action = models.CharField(max_length=255)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
timestamp = models.DateTimeField(auto_now_add=True, db_index=True)
details = models.JSONField(null=True, blank=True)
class Meta:
ordering = ['-timestamp']
def get_action_badge(self):
if 'deleted' in self.action:
return ('bg-danger', 'deleted')
elif 'restored' in self.action:
return ('bg-success', 'restored')
elif 'created' in self.action:
return ('bg-primary', 'created')
elif 'updated' in self.action:
return ('bg-info', 'updated')
else:
action_verb = self.action.split(' ')[0]
return ('bg-secondary', action_verb)
class Customer(BaseModel):
STATUS_CHOICES = (
('active', 'Active'),
('inactive', 'Inactive'),
('lead', 'Lead'),
)
tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE)
name = models.CharField(max_length=255)
email = models.EmailField()
phone = models.CharField(max_length=50, blank=True)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='active', db_index=True)
owner = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='customers')
class Meta:
unique_together = ('tenant', 'email')
indexes = [
models.Index(fields=['tenant', 'status']),
models.Index(fields=['created_at']),
]
def __str__(self):
return self.name
class Lead(BaseModel):
STATUS_CHOICES = (
('new', 'New'),
('contacted', 'Contacted'),
('qualified', 'Qualified'),
('unqualified', 'Unqualified'),
)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='leads')
source = models.CharField(max_length=255, blank=True)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='new', db_index=True)
assigned_to = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='leads')
def __str__(self):
return f"Lead for {self.customer.name}"
class Opportunity(BaseModel):
STAGE_CHOICES = (
('prospecting', 'Prospecting'),
('qualification', 'Qualification'),
('proposal', 'Proposal'),
('negotiation', 'Negotiation'),
('closed_won', 'Closed Won'),
('closed_lost', 'Closed Lost'),
)
name = models.CharField(max_length=255, blank=True, null=True)
lead = models.ForeignKey(Lead, on_delete=models.CASCADE, related_name='opportunities')
value = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
stage = models.CharField(max_length=20, choices=STAGE_CHOICES, default='prospecting', db_index=True)
probability = models.FloatField(null=True, blank=True)
close_date = models.DateField(null=True, blank=True)
def __str__(self):
return self.name
class ContactHistory(BaseModel):
INTERACTION_CHOICES = (
('email', 'Email'),
('phone', 'Phone Call'),
('meeting', 'Meeting'),
('note', 'Note'),
)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='contact_history')
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
note = models.TextField()
interaction_type = models.CharField(max_length=20, choices=INTERACTION_CHOICES, default='note')
def __str__(self):
return f"Contact with {self.customer.name} on {self.created_at.date()}"
class Note(BaseModel):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
content = models.TextField()
def __str__(self):
return f"Note for {self.content_object}"