Lead to Opportunity Conversion Workflow in ERPNext: How Customer Data Flows Through the CRM Pipeline

The Lead to Opportunity conversion workflow in ERPNext maps core customer fields from the Lead DocType to a new Opportunity document using the make_opportunity function in erpnext/crm/doctype/lead/lead.py, then copies linked Address and Contact records via Dynamic Links to ensure complete customer data persistence.

The Lead to Opportunity conversion workflow is a critical CRM process in ERPNext that ensures sales prospects seamlessly transition through the pipeline. When a sales representative clicks Convert to Opportunity on a Lead record, ERPNext executes a server-side mapping operation that preserves customer identity, contact details, and ownership information. This article examines the exact mechanics of how the frappe/erpnext codebase handles this data transformation.

The Entry Point: make_opportunity Function

The workflow begins at the whitelisted make_opportunity function defined in erpnext/crm/doctype/lead/lead.py. This function serves as the gateway for converting Lead documents into Opportunities, accepting a source_name parameter that identifies the specific Lead to be converted and an optional target_doc parameter.

@frappe.whitelist()
def make_opportunity(source_name: str, target_doc: str | Document | None = None):

The implementation leverages frappe.model.mapper.get_mapped_doc to instantiate a new Opportunity document while applying a predefined field mapping configuration. This approach ensures type safety and consistent data structure across the conversion process.

Field Mapping Between Lead and Opportunity

The conversion relies on a dictionary-based field map that translates Lead schema fields into their Opportunity equivalents. This eliminates manual re-entry and maintains data integrity throughout the Lead to Opportunity conversion workflow.

Core Identity Fields

The mapping establishes the foundational relationship between the source Lead and the new Opportunity:

  • nameparty_name – Links the Opportunity to the Lead's unique identifier, establishing the party relationship.
  • company_namecustomer_name – Carries the organization name that will become the Customer name when the Opportunity is later converted.
  • doctypeopportunity_from – Sets the source document type indicator on the Opportunity.

Contact and Ownership Data

Customer communication details and responsibility assignments transfer through these mappings:

  • email_idcontact_email – Preserves the primary email address.
  • mobile_nocontact_mobile – Maintains mobile contact information.
  • lead_owneropportunity_owner – Transfers sales person responsibility to the Opportunity.
  • notesnotes – Carries forward any free-text annotations.

Copying Linked Address and Contact Records

Beyond simple field mapping, the workflow preserves relational data through the _set_missing_values helper method. This function queries the Dynamic Link table to locate Address and Contact documents linked to the original Lead, then assigns them to the new Opportunity's customer_address and contact_person fields.

address = frappe.get_all(
    "Dynamic Link",
    {"link_doctype": source.doctype, "link_name": source.name, "parenttype": "Address"},
    ["parent"],
    limit=1,
)
contact = frappe.get_all(
    "Dynamic Link",
    {"link_doctype": source.doctype, "link_name": source.name, "parenttype": "Contact"},
    ["parent"],
    limit=1,
)
if address: target.customer_address = address[0].parent
if contact: target.contact_person = contact[0].parent

This guarantees the Opportunity inherits the same billing/shipping addresses and primary contact records attached to the original Lead.

Pre-Conversion Validation Checks

The Lead class implements validation methods to prevent duplicate conversions and ensure data consistency. The has_opportunity method checks for existing active Opportunities linked to the Lead, while has_customer and has_quotation provide additional pipeline visibility.

The UI layer disables the Convert to Opportunity button when has_opportunity returns true for a non-Lost Opportunity, preventing users from creating multiple active Opportunities for the same Lead inadvertently.

From Opportunity to Customer

The same architectural pattern extends to the final conversion stage in the sales pipeline. The make_customer function (also located in erpnext/crm/doctype/lead/lead.py) reuses the mapping logic to transform Opportunity data into a Customer record. This creates a consistent data lineage from initial Lead through closed sale, ensuring the customer_name field populated during the first conversion becomes the actual Customer document name.

Implementation Examples

Server-Side Python Conversion

You can trigger the Lead to Opportunity conversion workflow programmatically from server scripts or custom apps:

import frappe

lead_name = "LEAD-00015"

# Convert the lead into an opportunity

opportunity_doc = frappe.get_doc("Lead", lead_name).make_opportunity(lead_name)

# Optionally, immediately convert to a customer

customer_doc = frappe.get_doc("Opportunity", opportunity_doc.name).make_customer(opportunity_doc.name)

Client-Side JavaScript API Call

For custom UI components or client-side automation, call the whitelisted method directly:

frappe.call({
    method: "erpnext.crm.doctype.lead.lead.make_opportunity",
    args: { source_name: "LEAD-00015" },
    callback: function(r) {
        if (r.message) {
            console.log("Created Opportunity:", r.message.name);
        }
    }
});

Summary

  • The conversion workflow initiates in erpnext/crm/doctype/lead/lead.py via the whitelisted make_opportunity function.
  • Field mapping uses get_mapped_doc to transfer Lead data to Opportunity fields including party_name, customer_name, contact_email, and opportunity_owner.
  • Address and Contact preservation occurs through Dynamic Link queries in the _set_missing_values helper method.
  • Validation methods (has_opportunity, has_customer) prevent duplicate records and maintain data integrity.
  • The same mapping pattern extends to Customer creation via the make_customer function, ensuring seamless CRM pipeline progression.

Frequently Asked Questions

What file contains the Lead to Opportunity conversion logic in ERPNext?

The core logic resides in erpnext/crm/doctype/lead/lead.py, specifically within the make_opportunity function and its helper _set_missing_values. This file also contains the validation methods and the field mapping configuration that drives the conversion process.

How does ERPNext handle existing addresses when converting a Lead to an Opportunity?

The system queries the Dynamic Link table for Address and Contact records linked to the Lead via the link_doctype and link_name filters. It assigns the first matching Address to customer_address and the first Contact to contact_person on the new Opportunity, ensuring billing and shipping information persists through the conversion.

Can a Lead be converted to an Opportunity if it already has an active Opportunity?

No. The has_opportunity method checks for existing Opportunities with a status other than "Lost". If an active Opportunity exists, the UI disables the conversion button. Programmatic implementations should call this validation method before invoking make_opportunity to prevent duplicate entries.

Which specific fields are mapped from Lead to Opportunity during conversion?

Key mappings include the Lead's name to the Opportunity's party_name, company_name to customer_name, email_id to contact_email, mobile_no to contact_mobile, and lead_owner to opportunity_owner. The system also maps doctype to opportunity_from and copies the notes field verbatim.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →