Showing posts with label Leads. Show all posts
Showing posts with label Leads. Show all posts

Monday, April 15, 2013

Lions, Tigers, and Lead Conversion, Oh My!

For something as simple as clicking a button on a Lead to flip its status from "Open" to "Qualified", there sure are a lot of operations and complexity happening behind the scenes.  Converting a lead takes a lot of forethought.  For instance it must abide by Business processes associated with its Record Type. Fields must be mapped correctly for them to be carried over to the related Opportunity, Account, and Contact records.

What if you want to step outside the box with Lead conversion?  What if you want to implement your own lead button with a custom Visualforce UI?  What if you want to implement mass lead conversion through an integration or middleware?  The latter is where lead conversion gets really tricky, and that's the topic of this blog post.

I'm working on a rather large scale integration, which synchronizes both standard and custom objects from a source Salesforce org to another target Salesforce org.  The integration itself is a bit too complex to go with a standard Salesforce to Salesforce implementation.  One of the objects is Leads, and hence Converted Leads as well.

After digging into Lead conversion and how to integrate converted leads, I felt like Frodo getting caught by the spider



A few things I needed to consider were:

  • How do I convert leads using an external id?
  • How do I talk back to the source record, after a successful conversion, and prevent that record from being synchronized again?
  • Can this be accomplished with a successful web service call or two?
After toying around with a simple web service, it was quickly apparent that a single call really wouldn't suffice.  The leads in the integration are basically under a single account, and once you try and convert leads under a single account using logic similar to below, you get a "Duplicate Id in list exception".  Here's my post on Stackexchange asking for help on this issue: http://salesforce.stackexchange.com/questions/10520/best-approach-for-integrating-converted-leadswith-external-ids/10531?noredirect=1#10531

 List leadConversions = new List();
        for(Lead l : leadsToConvert){
            Database.LeadConvert lc = new Database.LeadConvert();
            if(l.Contact__r != null){
                lc.setContactId(l.Contact__r.Id);
                lc.setAccountId(l.Contact__r.AccountId);
            }
            lc.setLeadId(l.Id);
            lc.setDoNotCreateOpportunity(true);
            lc.setConvertedStatus(status);
            leadConversions.add(lc);              
            leadExternalIds.add(l.External_Id__c);
        }
     
        List lcr = new List();
        Integer i=0;
        for(Database.LeadConvertResult r : Database.convertLead(leadConversions,false)){
            lcr.add(
                new LeadConversionResult(
                    r.isSuccess(),
                    r.getErrors().isEmpty() ? null : r.getErrors().get(0).getMessage(),
                    leadExternalIds.get(i++)
                )
            );
        }

After realizing this wouldn't work, it was time for a completely different approach.  Breaking out SoapUI (http://www.soapui.org/), a handy plugin for testing web service calls, and your best friend for testing out the Salesforce.com Enterprise and Partner WSDLs,  it was apparent that the api convertLead operation behaves differently than the apex version.  The convertLead operation does allow multiple leads under the same account!  Continuing the LOTR theme, the convertLead operation was my Samwise Gamgee to my integration Shelob (http://en.wikipedia.org/wiki/Shelob)

Taking the above into consideration I came up with the following solution:

  1. When the source lead is converted, the external id is loaded into a custom object.  This allows the control successes and failures in the integration since leads cannot be updated after they are converted.
  2. The integration queries the converted object queue above and passes the external ids to a webservice on the target org to retrieve their Salesforce Ids.  Since the external ids are Unique, and Case sensitive, it's a straight 1-1 mapping.
  3. The ids are transformed into a convertLead request, which is a operation available on the partner wsdl.
  4. Any successes are passed back to a third webservice call, which deletes successful conversions from the custom object "queue".  Errors are left in the queue for re-processing
The flow of the operation looks something like this:


And there you have it, a solution for integrating converted leads.



Thursday, March 3, 2011

Leads, Campaigns, and Workflow

I have a requirement for a client that wishes to create a Web 2 Lead form and assign it to a campaign.
Once that is done, a workflow shall fire off a series of emails depending on what sort of campaign the lead is tied to.  This sounds easy enough to do with Salesforce's out of the box Web2Lead functionality and workflow, but there were some other tweaks we needed, so utilizing our own solution is the only way to go.

- The form -
Utilizing a jQuery modal, the user clicks a link and populates a small web to lead form.  When the user hits submit, sever side validation happens in a VF controller, so we can control the UI (messages and labels if a field is required for instance). 

- Allowing Portal users to use the web 2 lead form -
Once the form is submitted, it stores the lead in a custom lead object.  The custom lead object is needed, since leads cannot be created (owned) by Portal users.  When a portal user creates the mirrored lead object, it fires off a trigger calling an @future method to insert the lead and campaign member.

- Tricky workflows with Leads and Campaigns -
Here's the tricky part of the workflow.  When the lead is inserted, it doesn't have access to its related Campaign member and hence related Campaign (the Campaign member is a junction object to connect Campaigns to leads or contacts).  Our workflow needed to look at the Campaign Name field on the Campaign.  The lead doesn't have this when it is inserted, nor if the Campaign member is updated, does it update the lead.  Therefore, modifying the campaign member or campaign doesn't fire the workflow rule until the lead is updated next.  This is true even if the workflow rule is set to 'Created or did not previously meet the criteria'.

The solution is to create the workflow rule on the Campaign member and not the Lead.  When the Campaign Member junction is created, it should have access to both its Campaign and its Lead objects.  Thus, when the member is created, we can create a rule like 'Campaign Name equals My Name' and fire off a workflow when the Lead is assigned to the particular campaign.

Hope this helps others.