Convert Lead Wizard Documentation¶
Overview¶
The Convert Lead module (convert.lead) is a transient wizard model that provides a user-friendly interface for converting qualified leads into sales opportunities. This wizard streamlines the lead-to-opportunity conversion process by presenting a form where users can review and adjust key fields before creating the opportunity record. As a transient model, it exists only during the conversion process and doesn't persist data in the database.
Model Information¶
Model Name: convert.lead
Display Name: Convert Lead (wizard)
Key Fields: None (transient model)
Features¶
- ❌ Audit logging enabled (
_audit_log) - ❌ Multi-company support (
company_id) - ❌ Full-text content search (
_content_search) - ❌ Unique key constraint
- ✅ Transient model - Data exists only in session, not persisted
Understanding Transient Models¶
What are Transient Models?¶
Transient models in Netforce are temporary models used for wizards and forms that: - Exist only during user interaction - Don't create permanent database tables - Are used to collect user input for operations - Automatically clean up after completion
For the convert.lead model:
- Data is stored in session/memory only
- No database persistence
- Perfect for step-by-step conversion workflows
- Reduces database clutter
Why Use a Wizard for Lead Conversion?¶
Rather than directly converting leads with a simple method call, the wizard provides: 1. User Review: Sales reps can verify and adjust details before creating opportunity 2. Field Mapping: Intelligent pre-population of fields from lead data 3. Contact Association: Select or create the right contact for the opportunity 4. User Assignment: Confirm or change the assigned sales representative 5. Validation: Catch missing or incorrect data before creating records
Key Fields Reference¶
Wizard Fields¶
| Field | Type | Required | Description |
|---|---|---|---|
lead_id |
Many2One | ✅ | The lead being converted (cascade delete) |
user_id |
Many2One | ✅ | User who will own the opportunity (cascade delete) |
contact_id |
Many2One | ✅ | Contact associated with opportunity (cascade delete) |
name |
Char | ✅ | Name/title of the opportunity |
uuid |
Char | ❌ | Unique identifier for wizard session |
Note: All fields marked with on_delete="cascade" ensure wizard data is properly cleaned up when related records are deleted.
Wizard Workflow¶
Lead Selected
↓
Wizard Opened → default_get() Pre-populates Form
↓
User Reviews/Edits:
- Opportunity Name
- Contact Selection
- Assigned User
↓
User Clicks "Convert"
↓
do_copy() Creates Opportunity
↓
Navigate to New Opportunity
API Methods¶
1. Default Get (Form Pre-population)¶
Method: default_get(field_names, context, **kw)
Automatically pre-populates the wizard form with intelligent defaults from the lead.
Parameters:
- field_names (list): Fields to populate (optional)
- context (dict): Context containing defaults including lead_id
Context Structure:
Behavior:
- Retrieves lead data from lead_id in context
- Searches for existing contact matching lead's company name
- Pre-populates contact_id if found
- Copies user_id from lead
- Inherits defaults from parent class
Example:
# When wizard is opened with lead_id=123
context = {
"defaults": {
"lead_id": 123
}
}
# Wizard automatically populates:
# - lead_id: 123
# - user_id: lead.user_id.id
# - contact_id: existing_contact_id (if found)
# - name: (left blank for user to fill)
Returns: dict - Default values for wizard form fields
2. Do Copy (Execute Conversion)¶
Method: do_copy(ids, context)
Executes the actual lead-to-opportunity conversion and creates the opportunity record.
Parameters:
- ids (list): Wizard record IDs (usually single ID)
- context (dict): Execution context
Behavior:
1. Retrieves wizard form data
2. Constructs opportunity values from wizard fields
3. Creates new sale.opportunity record
4. Returns navigation to newly created opportunity
Values Transferred:
opportunity_vals = {
"user_id": wizard.user_id.id, # From wizard selection
"name": wizard.name, # From wizard input
"contact_id": wizard.contact_id.id # From wizard selection
}
Returns: dict - Navigation directive to open new opportunity
Return Structure:
{
"next": {
"name": "opport", # View name
"mode": "form", # Open in form mode
"active_id": opportunity_id # ID of created opportunity
}
}
Example:
# User fills wizard form and clicks "Convert"
wizard_id = 1 # Wizard session ID
result = get_model("convert.lead").do_copy([wizard_id])
# Returns:
# {
# "next": {
# "name": "opport",
# "mode": "form",
# "active_id": 456 # New opportunity ID
# }
# }
# UI automatically navigates to opportunity form
UI Integration¶
Opening the Wizard¶
The wizard is typically opened from the lead form view via a button:
<!-- In lead form XML -->
<button string="Convert to Opportunity"
action="convert_lead"
states="qualified,preparing_quotation"/>
This opens the wizard with the current lead pre-selected.
Wizard Form Layout¶
The wizard form typically includes: 1. Lead Information (read-only display) - Lead number - Contact name - Company - Email/Phone
- Opportunity Details (editable)
- Opportunity name (required)
- Contact selection (required)
-
Assigned user (required, pre-filled)
-
Action Buttons
- "Convert" - Executes do_copy()
- "Cancel" - Closes wizard
Common Use Cases¶
Use Case 1: Standard Lead Conversion¶
# 1. User opens lead form for qualified lead
lead_id = 123
lead = get_model("sale.lead").browse(lead_id)
# 2. User clicks "Convert to Opportunity" button
# System automatically opens wizard with context
wizard_action = {
"name": "convert_lead_wizard",
"type": "form",
"model": "convert.lead",
"defaults": {
"lead_id": lead_id
}
}
# 3. Wizard pre-populates from default_get():
# - lead_id: 123
# - user_id: 5 (from lead.user_id)
# - contact_id: 789 (found existing contact "Acme Corp")
# - name: "" (user fills in)
# 4. User enters opportunity name: "Acme Corp - CRM Implementation"
# 5. User clicks "Convert", which calls do_copy()
opportunity_id = get_model("sale.opportunity").create({
"user_id": 5,
"name": "Acme Corp - CRM Implementation",
"contact_id": 789
})
# 6. UI navigates to new opportunity form
Use Case 2: Conversion with Contact Creation¶
# Scenario: Lead has company but no existing contact
# 1. Wizard opens for lead with company="New Startup Inc"
lead = get_model("sale.lead").browse(lead_id)
# lead.company = "New Startup Inc"
# 2. default_get() searches for contact
existing_contacts = get_model("contact").search([["name", "=", "New Startup Inc"]])
# Returns: [] (no existing contact)
# 3. Wizard shows contact_id field empty - user must select/create
# 4. User creates new contact in wizard or selects from dropdown
# Option A: Use lead.copy_to_contact() first to create contact
contact_id = get_model("contact").create({
"name": "New Startup Inc",
"type": "org",
"phone": lead.phone,
"email": lead.email
})
# Option B: Select different existing contact
# 5. User completes wizard with newly created/selected contact
# 6. Opportunity created with proper contact association
Use Case 3: Reassigning Opportunity Owner¶
# Scenario: Lead owner wants to hand off opportunity to account manager
# 1. Lead is owned by SDR (user_id=5)
lead = get_model("sale.lead").browse(lead_id)
# lead.user_id.id = 5 (Sales Development Rep)
# 2. Wizard pre-fills user_id=5
# 3. SDR changes user_id to 12 (Account Executive)
# Wizard field user_id: 12
# 4. Conversion creates opportunity assigned to AE
opportunity_vals = {
"user_id": 12, # Account Executive
"name": "Enterprise Deal",
"contact_id": 789
}
# 5. AE receives new opportunity, SDR's lead is marked converted
Integration with Lead Model¶
Complete Conversion Flow¶
The wizard integrates with the parent sale.lead model's conversion methods:
# Full conversion process combining wizard and lead model
# Step 1: User initiates conversion via wizard
wizard_id = get_model("convert.lead").create({
"lead_id": lead_id,
"user_id": 5,
"contact_id": 789,
"name": "Big Deal Opportunity"
})
# Step 2: Wizard creates opportunity
result = get_model("convert.lead").do_copy([wizard_id])
opportunity_id = result["next"]["active_id"]
# Step 3: Update lead status (typically done in sale.lead model)
get_model("sale.lead").write([lead_id], {
"state": "converted"
})
# Step 4: Link opportunity back to lead (in sale.opportunity.create)
get_model("sale.opportunity").write([opportunity_id], {
"lead_id": lead_id
})
# Result:
# - Lead marked as "converted"
# - Opportunity created and linked
# - User navigated to opportunity form
Best Practices¶
1. Pre-Create Contacts for Smooth Conversion¶
# Bad: Forcing user to deal with contact creation during conversion
# (Wizard shows empty contact_id, user confused)
# Good: Create contact from lead first, then convert
# Option 1: Use lead.copy_to_contact() before opening wizard
get_model("sale.lead").copy_to_contact([lead_id])
# Now contact exists, wizard auto-populates contact_id
# Option 2: Provide "Create Contact" button in wizard interface
2. Validate Required Fields¶
# Good: Wizard should validate before calling do_copy()
def do_copy(self, ids, context={}):
obj = self.browse(ids)[0]
# Validate required fields
if not obj.name:
raise Exception("Opportunity name is required")
if not obj.contact_id:
raise Exception("Please select a contact")
if not obj.user_id:
raise Exception("Please assign a user")
# Proceed with creation
vals = {
"user_id": obj.user_id.id,
"name": obj.name,
"contact_id": obj.contact_id.id
}
opp_id = get_model("sale.opportunity").create(vals)
return {
"next": {
"name": "opport",
"mode": "form",
"active_id": opp_id
}
}
3. Provide Intelligent Defaults¶
# Good: Smart opportunity naming
def default_get(self, field_names=None, context={}, **kw):
defaults = context.get("defaults", {})
if defaults:
lead_id = int(defaults["lead_id"])
lead = get_model("sale.lead").browse(lead_id)
# Find or suggest contact
res = get_model("contact").search([["name", "=", lead.company]])
if res:
defaults["contact_id"] = res[0]
# Copy user
defaults["user_id"] = lead.user_id.id
# Suggest opportunity name from lead title
if lead.title:
defaults["name"] = lead.title
elif lead.company:
defaults["name"] = f"{lead.company} - Opportunity"
return super().default_get(field_names, context, **kw)
Comparison: Wizard vs Direct Conversion¶
Using Wizard (convert.lead)¶
Pros: - User can review and modify fields - Clear visual confirmation before creating opportunity - Allows reassignment of owner - Can adjust opportunity name - Better user experience
Cons: - Extra click required - Slightly slower for bulk conversions
When to Use: - Manual, one-by-one conversions - When user judgment is needed - UI-driven workflows
Direct Conversion (sale.lead.copy_to_opport)¶
Pros: - Faster for bulk operations - Can be scripted/automated - No UI interaction needed
Cons: - No user review - No field adjustments - Assumes lead data is complete
When to Use: - Automated workflows - Bulk conversions - API/script-driven processes
# Script directly converts multiple leads
get_model("sale.lead").copy_to_opport([lead_id_1, lead_id_2, lead_id_3])
Troubleshooting¶
"Contact is required"¶
Cause: User didn't select a contact in wizard
Solution: Ensure contact field is populated. Either:
1. Use lead.copy_to_contact() first to create contact automatically
2. Allow user to create contact in wizard
3. Provide dropdown of existing contacts
Wizard form shows no data¶
Cause: Context defaults not passed correctly when opening wizard Solution: Ensure wizard action includes proper context:
"Opportunity created but lead not marked converted"¶
Cause: Wizard only creates opportunity, doesn't update lead state
Solution: This is expected behavior. The sale.lead.copy_to_opport() method handles both. For wizard-based conversion, add post-conversion logic to update lead state.
Testing Examples¶
Unit Test: Wizard Conversion¶
def test_convert_lead_wizard():
# Create a test lead
lead_id = get_model("sale.lead").create({
"title": "Test Lead for Conversion",
"contact_name": "John Doe",
"email": "john@example.com",
"company": "Test Corp",
"state": "qualified"
})
# Create contact
contact_id = get_model("contact").create({
"name": "Test Corp",
"type": "org"
})
# Create wizard with defaults
wizard_id = get_model("convert.lead").create({
"lead_id": lead_id,
"user_id": 1,
"contact_id": contact_id,
"name": "Test Corp - New Opportunity"
})
# Execute conversion
result = get_model("convert.lead").do_copy([wizard_id])
# Verify opportunity created
assert "next" in result
assert result["next"]["name"] == "opport"
opp_id = result["next"]["active_id"]
# Verify opportunity data
opp = get_model("sale.opportunity").browse(opp_id)
assert opp.name == "Test Corp - New Opportunity"
assert opp.contact_id.id == contact_id
assert opp.user_id.id == 1
Related Models¶
| Model | Relationship | Description |
|---|---|---|
sale.lead |
Many2One | Lead being converted |
sale.opportunity |
Created | Opportunity created by wizard |
contact |
Many2One | Contact associated with opportunity |
base.user |
Many2One | User assigned to opportunity |
Version History¶
Last Updated: 2025-01-05 Model Version: convert_lead.py Framework: Netforce
Additional Resources¶
- Sale Lead Documentation:
sale.lead - Sale Opportunity Documentation:
sale.opportunity - Contact Management:
contact - Transient Model Patterns: Netforce developer guide
Support & Feedback¶
For issues or questions about this module:
1. Check that lead has required data (email, company) before conversion
2. Verify contact exists or create it before using wizard
3. Ensure wizard context includes proper defaults
4. Test wizard flow in development environment first
5. Review related sale.lead.copy_to_opport() for direct conversion alternative
This documentation is generated for developer onboarding and reference purposes.