Skip to content

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:

context = {
    "defaults": {
        "lead_id": int,              # ID of lead being converted
    }
}

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

  1. Opportunity Details (editable)
  2. Opportunity name (required)
  3. Contact selection (required)
  4. Assigned user (required, pre-filled)

  5. Action Buttons

  6. "Convert" - Executes do_copy()
  7. "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

# User clicks button → wizard opens → user reviews → creates opportunity

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:

{
    "defaults": {
        "lead_id": lead_id  # Must be passed
    }
}

"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

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.