function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
L0bster1L0bster1 

Bulk Lead convert 200 leads at a time: Is it possible?

I want to automatically convert new leads into contacts. I have a trigger on Lead after update that will bulk convert 100 leads at a time. The problem I have  is that my marketing automation tool pushes new leads into Salesforce in batches of 200 at a time. When I try to import 200 leads, the bulk convert fails due to a too many DML 151 error.

 

I read that the convertlead function can only handle 100 records at a time. How can I edit the code to handle 200 imports at a time? Is it possible?

 

Thanks in advance.

 

trigger AutoConvert on Lead (after update) {

for(Lead myLead: Trigger.new){ if(Trigger.new[0].isConverted == false) { Database.LeadConvert lc = new database.LeadConvert(); lc.setLeadId(myLead.Id); lc.convertedStatus = 'Qualified'; //Database.ConvertLead(lc,true); lc.setDoNotCreateOpportunity(true); Database.LeadConvertResult lcr = Database.convertLead(lc); System.assert(lcr.isSuccess()); }}}

 

Best Answer chosen by Admin (Salesforce Developers) 
Jerun JoseJerun Jose

Instead of passing one lead value at a time to the convertLead method, you can pass a list of lead values. From the documentation at

http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_dml_convertLead.htm
you can do something like

LeadConvertResult[] Database.convertLead(LeadConvert[] leadsToConvert, Boolean opt_allOrNone)

 

Also, you trigger is not designed for bulk operations. Use the updated trigger below and modify it as necessary.

 

trigger AutoConvert on Lead (after update) {
	list<Lead> LeadsToConvert = new list<Lead>();
	for(Lead myLead: Trigger.new){
		if(!myLead.isConverted)
			LeadsToConvert.add(myLead);
	}

	list<Database.LeadConvert> leadConverts = new list<Database.LeadConvert>();
	for(Lead myLead : LeadsToConvert){
		Database.LeadConvert lc = new database.LeadConvert();
		lc.setLeadId(myLead.Id);
		lc.convertedStatus = 'Qualified';
		//Database.ConvertLead(lc,true);
		lc.setDoNotCreateOpportunity(true);
		leadConverts.add(lc);
	}

	if(!leadConverts.isEmpty()){
		for(Integer i = 0; i <= leadConverts.size()/100 ; i++){
			list<Database.LeadConvert> tempList = new list<Database.LeadConvert>();
			Integer startIndex = i*100;
			Integer endIndex = ((startIndex+100) < leadConverts.size()) ? startIndex+100: leadConverts.size();
			for(Integer j=startIndex;j<endIndex;j++){
				tempList.add(leadConverts[j]);
			}
			Database.LeadConvertResult[] lcrList = Database.convertLead(tempList, false);
			for(Database.LeadConvertResult lcr : lcrList)
				System.assert(lcr.isSuccess());
		}
	}
}

 

 Edit - forgot the part about max 100 records for convertLead. Updated the apex code for it

All Answers

Jerun JoseJerun Jose

Instead of passing one lead value at a time to the convertLead method, you can pass a list of lead values. From the documentation at

http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_dml_convertLead.htm
you can do something like

LeadConvertResult[] Database.convertLead(LeadConvert[] leadsToConvert, Boolean opt_allOrNone)

 

Also, you trigger is not designed for bulk operations. Use the updated trigger below and modify it as necessary.

 

trigger AutoConvert on Lead (after update) {
	list<Lead> LeadsToConvert = new list<Lead>();
	for(Lead myLead: Trigger.new){
		if(!myLead.isConverted)
			LeadsToConvert.add(myLead);
	}

	list<Database.LeadConvert> leadConverts = new list<Database.LeadConvert>();
	for(Lead myLead : LeadsToConvert){
		Database.LeadConvert lc = new database.LeadConvert();
		lc.setLeadId(myLead.Id);
		lc.convertedStatus = 'Qualified';
		//Database.ConvertLead(lc,true);
		lc.setDoNotCreateOpportunity(true);
		leadConverts.add(lc);
	}

	if(!leadConverts.isEmpty()){
		for(Integer i = 0; i <= leadConverts.size()/100 ; i++){
			list<Database.LeadConvert> tempList = new list<Database.LeadConvert>();
			Integer startIndex = i*100;
			Integer endIndex = ((startIndex+100) < leadConverts.size()) ? startIndex+100: leadConverts.size();
			for(Integer j=startIndex;j<endIndex;j++){
				tempList.add(leadConverts[j]);
			}
			Database.LeadConvertResult[] lcrList = Database.convertLead(tempList, false);
			for(Database.LeadConvertResult lcr : lcrList)
				System.assert(lcr.isSuccess());
		}
	}
}

 

 Edit - forgot the part about max 100 records for convertLead. Updated the apex code for it

This was selected as the best answer
L0bster1L0bster1

Your code worked flawlessly. Thank you so much. I've seen a lot of posts on this issue but you were the first that I know of to find a solution.

 

I added some code to check if the web domain of a lead matches any accounts, and if so, associate the lead with that account at conversion. I think my SOQL statements are outside of the loops, but I'm still getting a too many SOQL statements error.

 

I've bolded the code that I added. Do you see what I've done wrong?

 

trigger AutoConvert on Lead (after update) {
    list<Lead> LeadsToConvert = new list<Lead>();
 set<id> triggerlead = new set<id>();
    
    for(Lead myLead: Trigger.new){
        if(!myLead.isConverted)
            LeadsToConvert.add(myLead);
            triggerlead.add(mylead.Id);
    }
    
Map<string,Id> leadDomainMap = new Map<string,Id>(); { for (lead le: [select id, domain__c from lead where id in :triggerlead]) {leadDomainMap.put(le.domain__c, null);} for (Account a :[SELECT ID, domain__c FROM Account WHERE domain__c in :leadDomainMap.keySet()]) {leadDomainMap.put(a.domain__c, a.Id);} }

    list<Database.LeadConvert> leadConverts = new list<Database.LeadConvert>();
    for(Lead myLead : LeadsToConvert){
        Database.LeadConvert lc = new database.LeadConvert();
        lc.setLeadId(myLead.Id);
        lc.convertedStatus = 'Qualified';
        //Database.ConvertLead(lc,true);
        lc.setDoNotCreateOpportunity(true);
            If(leadDomainMap.get(mylead.Domain__c)==null){} else{ID IDs = leadDomainMap.get(mylead.domain__c); lc.setAccountId(ids);}
        leadConverts.add(lc);
    }

    if(!leadConverts.isEmpty()){
        for(Integer i = 0; i <= leadConverts.size()/100 ; i++){
            list<Database.LeadConvert> tempList = new list<Database.LeadConvert>();
            Integer startIndex = i*100;
            Integer endIndex = ((startIndex+100) < leadConverts.size()) ? startIndex+100: leadConverts.size();
            for(Integer j=startIndex;j<endIndex;j++){
                tempList.add(leadConverts[j]);
            }
            Database.LeadConvertResult[] lcrList = Database.convertLead(tempList, false);

        }
    }

 

Jerun JoseJerun Jose

Your trigger is not so bad. It shouldnt be the real reason for the SOQL limit problem. Anyway, I noticed that you were doing a query on the lead object which I did not feel necessary as you could use trigger.new information itself for that. The updated code is below.

 

trigger AutoConvert on Lead (after update) {
    list<Lead> LeadsToConvert = new list<Lead>();
	set<String> LeadDomains = new set<String>();

    for(Lead myLead: Trigger.new){
        if(!myLead.isConverted)
            LeadsToConvert.add(myLead);
			LeadDomains.add(le.Domain__c);
    }
    
	Map<string,Id> DomainAccountMap = new Map<string,Id>();
	for (Account a :[SELECT ID, domain__c FROM Account WHERE domain__c != null and domain__c in :LeadDomains]){
		DomainAccountMap.put(a.domain__c, a.Id);
	}

    list<Database.LeadConvert> leadConverts = new list<Database.LeadConvert>();
    for(Lead myLead : LeadsToConvert){
        Database.LeadConvert lc = new database.LeadConvert();
        lc.setLeadId(myLead.Id);
        lc.convertedStatus = 'Qualified';
        //Database.ConvertLead(lc,true);
        lc.setDoNotCreateOpportunity(true);
		if(DomainAccountMap.containsKey(mylead.Domain__c)){
			ID IDs = DomainAccountMap.get(mylead.domain__c);
			lc.setAccountId(ids);
		}
        leadConverts.add(lc);
    }

    if(!leadConverts.isEmpty()){
        for(Integer i = 0; i <= leadConverts.size()/100 ; i++){
            list<Database.LeadConvert> tempList = new list<Database.LeadConvert>();
            Integer startIndex = i*100;
            Integer endIndex = ((startIndex+100) < leadConverts.size()) ? startIndex+100: leadConverts.size();
            for(Integer j=startIndex;j<endIndex;j++){
                tempList.add(leadConverts[j]);
            }
            Database.LeadConvertResult[] lcrList = Database.convertLead(tempList, false);

        }
    }
}

 

As for the limits part. Check the debug logs for an execution where you get this exception. This trigger should only use 1 query, and since you can call the same trigger for a max of 10 times, I am pretty sure that this trigger is not the root cause.

 

The reason could be because your trigger does a lead convert, which can fire account and contact triggers

Check the link

http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_triggers_ignoring_operations.htm

to know if the before triggers will be called as well.

 

Anyway, scouring through the debug log, you can look at the apex profiling sections to see how the SOQL limit keeps increasing. You can then identify the rogue code and clean it up.

L0bster1L0bster1

Jerun, thanks for all of your help. I tweaked the code a bit to make sure that the assignment rules would run against the lead before converting it. As part of this solution, I had to create a workflow that checked the Convert Lead checkbox when a lead is created. This step with the code below ensures that the lead is not converted before the assignment rules are run.

The code below is working great for me now.

 

trigger AutoConvert on Lead (after update) {
    list<Lead> LeadsToConvert = new list<Lead>();
    set<String> LeadDomains = new set<String>();

    for(Lead myLead: Trigger.new){
        if(!myLead.isConverted && mylead.convertlead__c == true)
            LeadsToConvert.add(myLead);
            LeadDomains.add(mylead.Domain__c);
    }
if(!leadstoConvert.isEmpty()){  
    Map<string,Id> DomainAccountMap = new Map<string,Id>();
    for (Account a :[SELECT ID, domain__c FROM Account WHERE domain__c != null and domain__c in :LeadDomains]){
        DomainAccountMap.put(a.domain__c, a.Id);
    }

    list<Database.LeadConvert> leadConverts = new list<Database.LeadConvert>();
    for(Lead myLead : LeadsToConvert){
    
        Database.LeadConvert lc = new database.LeadConvert();
        lc.setLeadId(myLead.Id);
        lc.convertedStatus = 'Qualified';
        //Database.ConvertLead(lc,true);
        lc.setDoNotCreateOpportunity(true);
        if(DomainAccountMap.containsKey(mylead.Domain__c)){
            ID IDs = DomainAccountMap.get(mylead.domain__c);
            lc.setAccountId(ids);
        }
        leadConverts.add(lc);
    }

    if(!leadConverts.isEmpty()){
        for(Integer i = 0; i <= leadConverts.size()/100 ; i++){
            list<Database.LeadConvert> tempList = new list<Database.LeadConvert>();
            Integer startIndex = i*100;
            Integer endIndex = ((startIndex+100) < leadConverts.size()) ? startIndex+100: leadConverts.size();
            for(Integer j=startIndex;j<endIndex;j++){
                tempList.add(leadConverts[j]);
            }
            Database.LeadConvertResult[] lcrList = Database.convertLead(tempList, false);

        }
    }
}else{}}

 

ABC.ax1000ABC.ax1000

Hi,

 

Can you post the final code for lead coversion to contact.

kanavkhuranakanavkhurana
Hi guys,

I am doing a bulk lead conversion on Lead and I notice the 'after insert' trigger for Opportunity running (which is as expected). However, the vague thing is that the trigger is running for 1 record at a time ( I am converting ~500 leads), hence I am hitting the 'TOO MANY SOQL QUERIES' exception. Has anybody noticed this? Thanks! :)
pradyprady
You are bound to hit the limits if you are converting over 100 records and if there are other workflows and triggers on Lead/contact/Account then you might hit the limit much earlier than 100 records.
You can also try out appexchange solution https://appexchange.salesforce.com/listingDetail?listingId=a0N3000000B5LuWEAV
MrBrianMrBrian
This was immensely helpful, thank you all! Just wanted to share my version, in which the lead gets converted not by a web domain, but simply to a related account (lookup) that is specified on the lead.
 
trigger AutoConvert on Lead (after update) {
    list<Lead> LeadsToConvert = new list<Lead>();

    for(Lead myLead: Trigger.new){
        if(!myLead.isConverted && mylead.Autoconvert_lead__c == true)
            LeadsToConvert.add(myLead);
    }
   
    list<Database.LeadConvert> leadConverts = new list<Database.LeadConvert>();
    for(Lead myLead : LeadsToConvert){
    
        Database.LeadConvert lc = new database.LeadConvert();
        lc.setLeadId(myLead.Id);
        lc.convertedStatus = 'Qualified';
        lc.setDoNotCreateOpportunity(true);
        if(mylead.Related_Account__c != null){
            lc.setAccountId(mylead.Related_Account__c);
            }
        leadConverts.add(lc);
    }

    if(!leadConverts.isEmpty()){
        for(Integer i = 0; i <= leadConverts.size()/100 ; i++){
            list<Database.LeadConvert> tempList = new list<Database.LeadConvert>();
            Integer startIndex = i*100;
            Integer endIndex = ((startIndex+100) < leadConverts.size()) ? startIndex+100: leadConverts.size();
            for(Integer j=startIndex;j<endIndex;j++){
                tempList.add(leadConverts[j]);
            }
            Database.LeadConvertResult[] lcrList = Database.convertLead(tempList, false);

        }
    }
}

Simple test class as well that gets a pass (note it doesn't test for DML updates though).
@istest
public class testAutoConvert {
static testMethod void AutoConvert(){
    
     Account a = new Account(name='Test Company Name', addtional_required_fields = 'go here');
     insert a;
     
     Lead l = new Lead(lastName = 'last', Company = 'Test Company Name', LeadSource = 'Other', Related_Account__c = a.id);
     insert l;
     
     l.Autoconvert_lead__c = true;
     update l;
     
}
}

 
Chance @ GranthamChance @ Grantham
This whole thread was of use to me. I wanted to add in my version as well. I included a bit of dedupe logic as well - specifically on the email. This also allows my data team to decide whether to create an opp or not depending on the Convert_No_Opp__c or Convert_with_Opp__c field.

Any feedback is appreciated. This class should be able to handle bulk updates, too.
 
public class leadConvert {
    public void convert(Lead[] givenLeads){
        System.debug('leadConvert.convert class running...................................................');
        
        List<Lead> noOppList = new List<Lead>();
        List<Lead> withOppList = new List<Lead>();
        Set<string> emailSet = new Set<string>();
        //set<string> phoneSet = new Set<string>();
        Set<string> accountSet = new Set<string>();
        
        //Loop through and grab information to query on
        for(lead l: givenLeads){
            if(!l.IsConverted){
                if(l.Convert_No_Opp__c){
                    noOppList.add(l);
                    if(l.Email!=null && l.Email!=''){emailSet.add(l.Email);}
                    //if(l.Phone!=null && l.Phone!=''){phoneSet.add(l.Phone);}
                    accountSet.add(l.Company);
                }
                else if(l.Convert_with_Opp__c){
                    withOppList.add(l);
                    if(l.Email!=null && l.Email!=''){emailSet.add(l.Email);}
                    //if(l.Phone!=null && l.Phone!=''){phoneSet.add(l.Phone);}
                    accountSet.add(l.Company);
                }
            }
        }
        
        System.debug('noOppList = ' + noOppList);
        System.debug('withOppList = ' + withOppList);
        
        //Query contacts to see if a contact exists that should be used as the target for conversion
        //We also have to get the account of that contact for the conversion
        List<Contact> emailMatchList = [SELECT id, lastname, email, accountid FROM Contact WHERE email IN :emailSet];
        //List<Contact> phoneMatchList = [SELECT id, lastname FROM Contact WHERE phone IN :phoneSet];        
        Map<string,id> emailMatchMap = new Map<string,id>();
        Map<id,id> contactAccountMap = new Map<id,id>();
        for(Contact c: emailMatchList){
            emailMatchMap.put(c.email, c.Id);
            contactAccountMap.put(c.id,c.AccountId);
        }
        
        //Search for accounts that match company
        List<Account> accountMatchList = [SELECT id, name FROM Account WHERE name IN :accountSet];
        Map<string,id> accountMatchMap = new Map<string,id>();
        for(Account a: accountMatchList){
            accountMatchMap.put(a.name, a.id);
        }
        
        //List used to convert
        List<Database.LeadConvert> leadConvertList = new List<Database.LeadConvert>();
        
        //Add to convert list without an opportunity
        for(lead lno: noOppList){
            Database.LeadConvert lc = new database.LeadConvert();
            lc.setLeadId(lno.Id);
            lc.convertedstatus = 'Closed - Converted';
            lc.setDoNotCreateOpportunity(true); //the only difference betweeen the two for loops
            if(lno.Email!=null && lno.Email!=''){
                if(emailMatchMap.containsKey(lno.Email) && emailMatchMap.get(lno.Email)!=null){
                    id contactID = emailMatchMap.get(lno.Email);
                    if(contactAccountMap.containsKey(contactID) && contactAccountMap.get(contactID)!=null){
                        lc.setAccountId(contactAccountMap.get(contactID));
                        lc.setContactId(contactID);
                    }
                }
            }
            if(lc.accountid == null){
                if(accountMatchMap.containsKey(lno.Company) && accountMatchMap.get(lno.Company)!=null){
                    lc.setAccountId(accountMatchMap.get(lno.Company));
                }
            }
            leadConvertList.add(lc);
        }
        
        //Add to convert list with an opportunity
        for(lead lyes: withOppList){
            Database.LeadConvert lc = new database.LeadConvert();
            lc.setLeadId(lyes.Id);
            lc.convertedstatus = 'Closed - Converted';
            lc.setDoNotCreateOpportunity(false); //the only difference betweeen the two for loops
            if(lyes.Email!=null && lyes.Email!=''){
                if(emailMatchMap.containsKey(lyes.Email) && emailMatchMap.get(lyes.Email)!=null){
                    id contactID = emailMatchMap.get(lyes.Email);
                    if(contactAccountMap.containsKey(contactID) && contactAccountMap.get(contactID)!=null){
                        lc.setAccountId(contactAccountMap.get(contactID));
                        lc.setContactId(contactID);
                    }
                }
            }
            if(lc.accountid == null){
                if(accountMatchMap.containsKey(lyes.Company) && accountMatchMap.get(lyes.Company)!=null){
                    lc.setAccountId(accountMatchMap.get(lyes.Company));
                }
            }
            leadConvertList.add(lc);
        }
        
        system.debug('leadConvertList = ' + leadConvertList);
        
        //update the leads
        //LeadConvert can only handle 100 records at a time
        if(!leadConvertList.isEmpty()){
            for(Integer i = 0; i <= leadConvertList.size()/100 ; i++){
                list<Database.LeadConvert> tempList = new list<Database.LeadConvert>();
                Integer startIndex = i*100;
                Integer endIndex = ((startIndex+100) < leadConvertList.size()) ? startIndex+100: leadConvertList.size();
                for(Integer j=startIndex;j<endIndex;j++){
                    tempList.add(leadConvertList[j]);
                }
                Database.LeadConvertResult[] lcrList = Database.convertLead(tempList, false);
                for(Database.LeadConvertResult lcr : lcrList)
                    System.assert(lcr.isSuccess());
            }
        }
    }
}

And here is the test class that is 90% coverage, but could easily be 100% coverage if needed.
@isTest
public class leadConvertTest {
    @isTest static void testConvert(){
        Test.startTest();
        Account a = new Account(name='leadConvertTestAccount');
        insert a;
        
        Contact c = new Contact(lastname = 'last', email='lno@leadConvertTest.com');
        insert c;
        
        lead lno = new Lead(lastName = 'lno last', Company = 'leadConvertTestAccount', LeadSource = 'Other', email='lno@leadConvertTest.com');
        insert lno;
        lead lno2 = new Lead(lastName = 'lno2 last', Company = 'leadConvertTestAccount', LeadSource = 'Other', email='lno2@leadConvertTest.com');
        insert lno2;
        
        lead lyes = new Lead(lastName = 'lyes last', Company = 'leadConvertTestAccount', LeadSource = 'Other', email='lyes@leadConvertTest.com');
        insert lyes;
        lead lyes2 = new Lead(lastName = 'lyes2 last', Company = 'leadConvertTestAccount', LeadSource = 'Other', email='lyes2@leadConvertTest.com');
        insert lyes2;
        
        List<Lead> leadList = [SELECT id, email, isconverted FROM Lead WHERE email LIKE '%leadConvertTest.com'];        
        System.debug('leadList = ' + leadList);
        
        for(Lead l: leadList){
            if(l.email == 'lno@leadConvertTest.com'){l.Convert_No_Opp__c = true;}
            if(l.email == 'lno2@leadConvertTest.com'){l.Convert_with_Opp__c = true;}
            if(l.email == 'lyes@leadConvertTest.com'){l.Convert_with_Opp__c = true;}
            if(l.email == 'lyes2@leadConvertTest.com'){l.Convert_No_Opp__c = true;}
        }
        update leadList;
        
        Test.stopTest();
        List<Account> acctCheck = [SELECT id, name FROM Account WHERE name = 'leadConvertTestAccount'];
        id acctCheckId = acctCheck[0].id;
        List<Contact> contactCheck = [SELECT id, lastname FROM Contact WHERE AccountId = :acctCheckId];
        List<Opportunity> oppCheck = [SELECT id FROM Opportunity WHERE AccountId = :acctCheckId];
        
        System.assertEquals(4, contactCheck.size(), 'There should be 4 contacts on the account, since one lead was converted into a contact');
        System.assertEquals(2, oppCheck.size(), 'There should be 2 Opps on the account');
    }
}

 
Chris SweetChris Sweet
Hi all, I'm curious if anyone has attempted to convert more than 100 leads using the convertLead call recently. We were messing around with some old code and set our for loop to 200 instead of 100, and to our surprise, this didn't hit an error. I'm curious if the docs are outdated or if we're just misunderstanding how that limit is determined.
VPM 1VPM 1
Im facing similar kind of issue where the Lead conversion doesnt work for bulk leads. Any help will be greatly appreciated
rishabh rathor 28rishabh rathor 28
Would like to recommend app called Bulk Object Field Creator, it helps to convert 500+ Lead records in just 10-15 minutes. In BOFC admins can filter leads using any of the below mentioned formats:
1) Use Salesforce Standard Report
2) Import Lead IDs using XLS file
3) Show All Leads which are not converted
https://appexchange.salesforce.com/appxListingDetail?listingId=a0N30000000qDqqEAE
Dima Ivanov 17Dima Ivanov 17
You can use below appexchange tool for mass and auto lead conversion:
https://appexchange.salesforce.com/appxListingDetail?listingId=a0N3A00000G0nBzUAJ
Mike Bucci 6Mike Bucci 6
There is an app on the AppExchange that enables you to automate lead conversion.  https://appexchange.salesforce.com/appxListingDetail?listingId=a0N4V00000DZQtdUAH&tab=e