bottleneckcrm/backend/leads/services.py

155 lines
5.4 KiB
Python

"""
Lead conversion service functions
"""
from django.contrib.contenttypes.models import ContentType
from accounts.models import Account
from common.models import Attachments, Comment
from contacts.models import Contact
from opportunity.models import Opportunity
def convert_lead_to_account(lead_obj, request, create_opportunity=True):
"""
Convert a Lead to Account, Contact, and optionally Opportunity.
This function:
1. Creates an Account from lead data
2. Creates a Contact from lead data (if lead has email)
3. Optionally creates an Opportunity (if create_opportunity=True and lead has opportunity_amount)
4. Copies tags, assigned_to, teams
5. Migrates Comments and Attachments
6. Auto-links existing Lead.contacts to the new Account
7. Sets lead status to "converted"
Args:
lead_obj: The Lead instance to convert
request: The HTTP request (for user/org context)
create_opportunity: Whether to create an Opportunity (default: True)
Returns:
tuple: (account, contact, opportunity) - the created entities (Contact/Opportunity may be None)
"""
# Create Account
account = Account.objects.create(
created_by=request.profile.user,
name=(
lead_obj.company_name
if lead_obj.company_name
else f"{lead_obj.first_name} {lead_obj.last_name}"
),
email=lead_obj.email,
phone=lead_obj.phone,
description=lead_obj.description,
website=lead_obj.website,
org=request.profile.org,
is_active=True,
# Use correct address fields
address_line=lead_obj.address_line,
city=lead_obj.city,
state=lead_obj.state,
postcode=lead_obj.postcode,
country=lead_obj.country,
)
# Copy tags
for tag in lead_obj.tags.all():
account.tags.add(tag)
# Copy assigned users
for profile in lead_obj.assigned_to.all():
account.assigned_to.add(profile)
# Copy teams
for team in lead_obj.teams.all():
account.teams.add(team)
# Move comments from Lead to Account
lead_ct = ContentType.objects.get_for_model(lead_obj.__class__)
account_ct = ContentType.objects.get_for_model(Account)
Comment.objects.filter(
content_type=lead_ct,
object_id=lead_obj.id,
org=request.profile.org,
).update(content_type=account_ct, object_id=account.id)
# Move attachments from Lead to Account
Attachments.objects.filter(
content_type=lead_ct,
object_id=lead_obj.id,
org=request.profile.org,
).update(content_type=account_ct, object_id=account.id)
# Create Contact from Lead data (if lead has email)
contact = None
if lead_obj.email:
contact = Contact.objects.create(
first_name=lead_obj.first_name or "",
last_name=lead_obj.last_name or "",
email=lead_obj.email,
phone=lead_obj.phone,
organization=lead_obj.company_name or "",
title=lead_obj.job_title,
description=lead_obj.description,
address_line=lead_obj.address_line,
city=lead_obj.city,
state=lead_obj.state,
postcode=lead_obj.postcode,
country=lead_obj.country,
created_by=request.profile.user,
org=request.profile.org,
account=account, # Set the primary account relationship
)
contact.assigned_to.set(lead_obj.assigned_to.all())
# Also link contact to account via M2M (for backwards compatibility)
account.contacts.add(contact)
# Auto-link any existing contacts from Lead to the new Account
for existing_contact in lead_obj.contacts.all():
account.contacts.add(existing_contact)
# Create Opportunity if requested and lead has opportunity data
opportunity = None
if create_opportunity and (
lead_obj.opportunity_amount or lead_obj.first_name or lead_obj.company_name
):
# Construct opportunity name from available lead data
opp_name_parts = []
if lead_obj.company_name:
opp_name_parts.append(lead_obj.company_name)
if lead_obj.first_name or lead_obj.last_name:
name = " ".join(filter(None, [lead_obj.first_name, lead_obj.last_name]))
opp_name_parts.append(name)
opp_name = (
" - ".join(opp_name_parts)
if opp_name_parts
else f"Opportunity {lead_obj.id}"
)
opportunity = Opportunity.objects.create(
name=opp_name,
account=account,
amount=lead_obj.opportunity_amount,
probability=lead_obj.probability or 0,
closed_on=lead_obj.close_date,
lead_source=lead_obj.source,
description=lead_obj.description,
stage="QUALIFICATION", # Initial stage after lead conversion
created_by=request.profile.user,
org=request.profile.org,
)
# Copy assigned users to opportunity
opportunity.assigned_to.set(lead_obj.assigned_to.all())
opportunity.teams.set(lead_obj.teams.all())
opportunity.tags.set(lead_obj.tags.all())
# Link contact to opportunity
if contact:
opportunity.contacts.add(contact)
# Update lead status to converted
lead_obj.status = "converted"
lead_obj.save()
return account, contact, opportunity