feat(leads): add Lead Manager module with full admin and consumer endpoints
- Lead model in admin_api with status/priority/source/assigned_to fields - Admin API: metrics, list, detail, update views at /api/v1/leads/ - Consumer API: public ScheduleCallView at /api/leads/schedule-call/ - RBAC: 'leads' module registered in ALL_MODULES and StaffProfile scopes - Migration 0003_lead with indexes on status, priority, created_at, email
This commit is contained in:
@@ -130,7 +130,7 @@ class StaffProfile(models.Model):
|
||||
def get_allowed_modules(self):
|
||||
scopes = self.get_effective_scopes()
|
||||
if '*' in scopes:
|
||||
return ['dashboard', 'partners', 'events', 'ad-control', 'users', 'reviews', 'contributions', 'financials', 'settings']
|
||||
return ['dashboard', 'partners', 'events', 'ad-control', 'users', 'reviews', 'contributions', 'leads', 'financials', 'settings']
|
||||
SCOPE_TO_MODULE = {
|
||||
'users': 'users',
|
||||
'events': 'events',
|
||||
@@ -140,6 +140,7 @@ class StaffProfile(models.Model):
|
||||
'settings': 'settings',
|
||||
'ads': 'ad-control',
|
||||
'contributions': 'contributions',
|
||||
'leads': 'leads',
|
||||
}
|
||||
modules = {'dashboard'}
|
||||
for scope in scopes:
|
||||
@@ -181,3 +182,61 @@ class AuditLog(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.action} by {self.user} at {self.created_at}"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Lead Manager
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class Lead(models.Model):
|
||||
EVENT_TYPE_CHOICES = [
|
||||
('private', 'Private Event'),
|
||||
('ticketed', 'Ticketed Event'),
|
||||
('corporate', 'Corporate Event'),
|
||||
('wedding', 'Wedding'),
|
||||
('other', 'Other'),
|
||||
]
|
||||
STATUS_CHOICES = [
|
||||
('new', 'New'),
|
||||
('contacted', 'Contacted'),
|
||||
('qualified', 'Qualified'),
|
||||
('converted', 'Converted'),
|
||||
('closed', 'Closed'),
|
||||
]
|
||||
SOURCE_CHOICES = [
|
||||
('schedule_call', 'Schedule a Call'),
|
||||
('website', 'Website'),
|
||||
('manual', 'Manual'),
|
||||
]
|
||||
PRIORITY_CHOICES = [
|
||||
('low', 'Low'),
|
||||
('medium', 'Medium'),
|
||||
('high', 'High'),
|
||||
]
|
||||
|
||||
name = models.CharField(max_length=200)
|
||||
email = models.EmailField()
|
||||
phone = models.CharField(max_length=20)
|
||||
event_type = models.CharField(max_length=20, choices=EVENT_TYPE_CHOICES, default='private')
|
||||
message = models.TextField(blank=True, default='')
|
||||
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='new')
|
||||
source = models.CharField(max_length=20, choices=SOURCE_CHOICES, default='schedule_call')
|
||||
priority = models.CharField(max_length=10, choices=PRIORITY_CHOICES, default='medium')
|
||||
assigned_to = models.ForeignKey(
|
||||
User, on_delete=models.SET_NULL, null=True, blank=True, related_name='assigned_leads'
|
||||
)
|
||||
notes = models.TextField(blank=True, default='')
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ['-created_at']
|
||||
indexes = [
|
||||
models.Index(fields=['status']),
|
||||
models.Index(fields=['priority']),
|
||||
models.Index(fields=['created_at']),
|
||||
models.Index(fields=['email']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f'Lead #{self.pk} — {self.name} ({self.status})'
|
||||
|
||||
Reference in New Issue
Block a user