Skip to content

Commit 9ff8eeb

Browse files
authored
Create crm_models.py
Add CRM data models for Trinity Strategy & Marketing including: - Contact model with marketing automation fields - Lead model with sales pipeline stages - Business model with service tracking - Flexible custom_data fields for different business types - Utility functions for creating marketing contacts, sales leads, and client businesses
1 parent 30ab79a commit 9ff8eeb

1 file changed

Lines changed: 327 additions & 0 deletions

File tree

crm/crm_models.py

Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
"""CRM Data Models for Trinity Strategy & Marketing
2+
3+
This module contains the core data models for the CRM system, designed to be
4+
flexible enough for marketing automation services while maintaining the ability
5+
to adapt to different business types.
6+
"""
7+
8+
from dataclasses import dataclass, field
9+
from datetime import datetime
10+
from typing import Optional, Dict, List, Any
11+
from enum import Enum
12+
import json
13+
14+
15+
class ContactStatus(Enum):
16+
"""Contact status enumeration"""
17+
ACTIVE = "active"
18+
INACTIVE = "inactive"
19+
BLOCKED = "blocked"
20+
PENDING = "pending"
21+
22+
23+
class LeadStatus(Enum):
24+
"""Lead status enumeration"""
25+
NEW = "new"
26+
CONTACTED = "contacted"
27+
QUALIFIED = "qualified"
28+
PROPOSAL = "proposal"
29+
NEGOTIATION = "negotiation"
30+
CLOSED_WON = "closed_won"
31+
CLOSED_LOST = "closed_lost"
32+
NURTURING = "nurturing"
33+
34+
35+
class PipelineStage(Enum):
36+
"""Sales pipeline stages for marketing services"""
37+
AWARENESS = "awareness"
38+
INTEREST = "interest"
39+
CONSIDERATION = "consideration"
40+
INTENT = "intent"
41+
EVALUATION = "evaluation"
42+
PURCHASE = "purchase"
43+
RETENTION = "retention"
44+
ADVOCACY = "advocacy"
45+
46+
47+
class BusinessType(Enum):
48+
"""Business type classification"""
49+
MARKETING_AGENCY = "marketing_agency"
50+
ECOMMERCE = "ecommerce"
51+
SAAS = "saas"
52+
CONSULTING = "consulting"
53+
RETAIL = "retail"
54+
HEALTHCARE = "healthcare"
55+
FINANCE = "finance"
56+
REAL_ESTATE = "real_estate"
57+
EDUCATION = "education"
58+
OTHER = "other"
59+
60+
61+
@dataclass
62+
class Contact:
63+
"""Core contact model for CRM system"""
64+
65+
# Basic identification
66+
contact_id: Optional[str] = None
67+
email: str = ""
68+
phone: str = ""
69+
first_name: str = ""
70+
last_name: str = ""
71+
company: str = ""
72+
73+
# Contact metadata
74+
source: str = "" # Where did they come from (website, referral, ad, etc.)
75+
status: ContactStatus = ContactStatus.ACTIVE
76+
77+
# Marketing automation fields
78+
tags: List[str] = field(default_factory=list)
79+
notes: str = ""
80+
81+
# Flexible data field for business-specific information
82+
custom_data: Dict[str, Any] = field(default_factory=dict)
83+
84+
# Timestamps
85+
created_at: Optional[datetime] = None
86+
updated_at: Optional[datetime] = None
87+
last_contacted: Optional[datetime] = None
88+
89+
def __post_init__(self):
90+
"""Initialize timestamps if not provided"""
91+
if self.created_at is None:
92+
self.created_at = datetime.now()
93+
if self.updated_at is None:
94+
self.updated_at = datetime.now()
95+
96+
@property
97+
def full_name(self) -> str:
98+
"""Get full name of contact"""
99+
return f"{self.first_name} {self.last_name}".strip()
100+
101+
def add_tag(self, tag: str) -> None:
102+
"""Add a tag to the contact"""
103+
if tag not in self.tags:
104+
self.tags.append(tag)
105+
self.updated_at = datetime.now()
106+
107+
def remove_tag(self, tag: str) -> None:
108+
"""Remove a tag from the contact"""
109+
if tag in self.tags:
110+
self.tags.remove(tag)
111+
self.updated_at = datetime.now()
112+
113+
def update_custom_data(self, key: str, value: Any) -> None:
114+
"""Update custom data field"""
115+
self.custom_data[key] = value
116+
self.updated_at = datetime.now()
117+
118+
119+
@dataclass
120+
class Lead:
121+
"""Lead model for sales pipeline management"""
122+
123+
# Basic identification
124+
lead_id: Optional[str] = None
125+
contact_id: Optional[str] = None # Reference to associated contact
126+
127+
# Lead details
128+
title: str = ""
129+
description: str = ""
130+
value: float = 0.0 # Estimated deal value
131+
probability: float = 0.0 # Win probability (0-100)
132+
133+
# Pipeline management
134+
status: LeadStatus = LeadStatus.NEW
135+
pipeline_stage: PipelineStage = PipelineStage.AWARENESS
136+
137+
# Activity tracking
138+
scheduled_activity: Optional[Dict[str, Any]] = None # Next scheduled activity
139+
activities: List[Dict[str, Any]] = field(default_factory=list) # Activity history
140+
141+
# Marketing fields
142+
source: str = ""
143+
campaign: str = ""
144+
145+
# Flexible data
146+
custom_data: Dict[str, Any] = field(default_factory=dict)
147+
148+
# Timestamps
149+
created_at: Optional[datetime] = None
150+
updated_at: Optional[datetime] = None
151+
expected_close_date: Optional[datetime] = None
152+
153+
def __post_init__(self):
154+
"""Initialize timestamps if not provided"""
155+
if self.created_at is None:
156+
self.created_at = datetime.now()
157+
if self.updated_at is None:
158+
self.updated_at = datetime.now()
159+
160+
def add_activity(self, activity_type: str, description: str,
161+
scheduled_for: Optional[datetime] = None) -> None:
162+
"""Add an activity to the lead"""
163+
activity = {
164+
"type": activity_type,
165+
"description": description,
166+
"created_at": datetime.now().isoformat(),
167+
"scheduled_for": scheduled_for.isoformat() if scheduled_for else None,
168+
"completed": False
169+
}
170+
self.activities.append(activity)
171+
self.updated_at = datetime.now()
172+
173+
def schedule_next_activity(self, activity_type: str, description: str,
174+
scheduled_for: datetime) -> None:
175+
"""Schedule the next activity"""
176+
self.scheduled_activity = {
177+
"type": activity_type,
178+
"description": description,
179+
"scheduled_for": scheduled_for.isoformat()
180+
}
181+
self.updated_at = datetime.now()
182+
183+
def advance_pipeline_stage(self) -> bool:
184+
"""Advance to next pipeline stage"""
185+
stages = list(PipelineStage)
186+
current_index = stages.index(self.pipeline_stage)
187+
188+
if current_index < len(stages) - 1:
189+
self.pipeline_stage = stages[current_index + 1]
190+
self.updated_at = datetime.now()
191+
return True
192+
return False
193+
194+
195+
@dataclass
196+
class Business:
197+
"""Business/Company model for client management"""
198+
199+
# Basic information
200+
business_id: Optional[str] = None
201+
name: str = ""
202+
website: str = ""
203+
industry: str = ""
204+
business_type: BusinessType = BusinessType.OTHER
205+
206+
# Contact information
207+
primary_email: str = ""
208+
primary_phone: str = ""
209+
address: Dict[str, str] = field(default_factory=dict) # street, city, state, zip, country
210+
211+
# Business details
212+
employee_count: Optional[int] = None
213+
annual_revenue: Optional[float] = None
214+
215+
# Relationship management
216+
client_since: Optional[datetime] = None
217+
account_manager: str = ""
218+
219+
# Marketing automation specific
220+
marketing_segments: List[str] = field(default_factory=list)
221+
automation_preferences: Dict[str, Any] = field(default_factory=dict)
222+
223+
# Service tracking
224+
active_services: List[str] = field(default_factory=list)
225+
service_history: List[Dict[str, Any]] = field(default_factory=list)
226+
227+
# Flexible data for different business types
228+
custom_data: Dict[str, Any] = field(default_factory=dict)
229+
230+
# Timestamps
231+
created_at: Optional[datetime] = None
232+
updated_at: Optional[datetime] = None
233+
234+
def __post_init__(self):
235+
"""Initialize timestamps if not provided"""
236+
if self.created_at is None:
237+
self.created_at = datetime.now()
238+
if self.updated_at is None:
239+
self.updated_at = datetime.now()
240+
241+
def add_service(self, service_name: str, start_date: Optional[datetime] = None) -> None:
242+
"""Add an active service"""
243+
if service_name not in self.active_services:
244+
self.active_services.append(service_name)
245+
246+
# Add to service history
247+
service_record = {
248+
"service": service_name,
249+
"start_date": (start_date or datetime.now()).isoformat(),
250+
"status": "active"
251+
}
252+
self.service_history.append(service_record)
253+
self.updated_at = datetime.now()
254+
255+
def remove_service(self, service_name: str, end_date: Optional[datetime] = None) -> None:
256+
"""Remove an active service"""
257+
if service_name in self.active_services:
258+
self.active_services.remove(service_name)
259+
260+
# Update service history
261+
for record in reversed(self.service_history):
262+
if record["service"] == service_name and record["status"] == "active":
263+
record["end_date"] = (end_date or datetime.now()).isoformat()
264+
record["status"] = "completed"
265+
break
266+
267+
self.updated_at = datetime.now()
268+
269+
def add_marketing_segment(self, segment: str) -> None:
270+
"""Add business to a marketing segment"""
271+
if segment not in self.marketing_segments:
272+
self.marketing_segments.append(segment)
273+
self.updated_at = datetime.now()
274+
275+
def set_automation_preference(self, key: str, value: Any) -> None:
276+
"""Set automation preference"""
277+
self.automation_preferences[key] = value
278+
self.updated_at = datetime.now()
279+
280+
281+
# Utility functions for working with models
282+
283+
def create_marketing_contact(email: str, first_name: str = "", last_name: str = "",
284+
company: str = "", source: str = "website") -> Contact:
285+
"""Create a contact optimized for marketing automation"""
286+
contact = Contact(
287+
email=email,
288+
first_name=first_name,
289+
last_name=last_name,
290+
company=company,
291+
source=source
292+
)
293+
# Add default marketing tags
294+
contact.add_tag("marketing_lead")
295+
contact.add_tag(f"source_{source}")
296+
297+
return contact
298+
299+
300+
def create_sales_lead(title: str, contact_id: str, value: float = 0.0,
301+
source: str = "") -> Lead:
302+
"""Create a sales lead"""
303+
return Lead(
304+
title=title,
305+
contact_id=contact_id,
306+
value=value,
307+
source=source,
308+
status=LeadStatus.NEW,
309+
pipeline_stage=PipelineStage.AWARENESS
310+
)
311+
312+
313+
def create_client_business(name: str, business_type: BusinessType = BusinessType.OTHER,
314+
primary_email: str = "", website: str = "") -> Business:
315+
"""Create a business/client record"""
316+
business = Business(
317+
name=name,
318+
business_type=business_type,
319+
primary_email=primary_email,
320+
website=website
321+
)
322+
323+
# Set client relationship
324+
business.client_since = datetime.now()
325+
business.add_marketing_segment("active_client")
326+
327+
return business

0 commit comments

Comments
 (0)