The "ConvertLead" Lead Conversion Process of Salesforce

Tuesday, March 8, 2011

I've been debugging some custom code that is operating around the Lead Conversion process of Salesforce. This is a big feature of the standard Salesforce platform - the ability to take the data from a fresh Lead and effortlessly transfer it into a new Contact, Account, and/or Opportunity.

We have some custom fields on the standard objects, and the value of this field on the Lead standard object was not properly keeping its value through the lead conversion process into a Contact. There is very little documentation on this process (see the convertLead() method and the ConvertLead Operation) so I was forced to sprinkle debug statements around my code and check the debug logs. I'll summarize the details of the ConvertLead process and its order of operations that may be of use to another Apex developer.

When the ConvertLead process is called, either by pressing the "Convert" standard button on the Lead page or by calling the convertLead Apex method in code, the same steps are followed on Salesforce's side. This is how my echo-location view of how it plays out:

Clicking the "Convert" button does this in this order:

  1. Insert new Account or update existing Account
  2. Insert the new Contact
  3. Insert new Opportunity (optional)
  4. Update references
    1. All records with fields that pointed to the old Lead are updated to point to the new Contact
    2. If an Opportunity was created, all references are updated to point to the new Opportunity instead of the new Contact
  5. Update old Lead
    1. The isConverted and other conversion-related fields are updated to 'true'

Now, remember that triggers and validation operations occur before and after each of these DML operations. This order of operations was our biggest question mark when confronting our bug, but we now understand it better, if only by a little. By looking at these notes, because all references will point to the optional Opportunity if it is created, it seems that Salesforce deems the Opportunity to be the more important of the two newly created sObjects (Opportunity and Contact). Keep this little detail in mind as you debug your code.

(Just a guess, but I would guess that these all occur under the same database transaction, and if one part fails, the transaction rolls back and an error is displayed to the user.)

Querying at the System Level From Custom Visualforce page.

Friday, March 4, 2011

I'll detail a problem I ran into last week. I'm sure I'll be missing some details, but I'll do my best.

We have a custom Salesforce application in-house that we use for tracking projects and the tasks involved in accomplishing those projects. When a custom Task is assigned to me, I do the work specified in the Task description and mark it as resolved. With the wonder of Chatter on this custom object, I am able to give status updates regarding the task to everyone following the task. But sometimes, the person I want to notify is not following this task when I know they should be.

The solution to this problem was to create an "Add Followers" button on the Task. When clicked, a Visualforce page would pop up that would have a listbox of users on either side of two buttons. One button would push a user from the All Users list on the left into the Followers list on the right and the other button would do the opposite.

Simple, right? And it was simple. It only took me a few hours to add the button and create the Visualforce page and its controller. This button worked great in the sandbox, so we pushed it to production and it also worked there. But soon, I started to receive IMs wondering why the button displayed a Not Authorized message for them. Oh no! My beautiful solution wasn't working!

Now why, you're wondering, were they getting this Not Authorized message? Well, I figured that it was because we were using my ChatterHelper class to directly modify a user's Chatter objects, which belong to the system level. As an administrator in the sandbox, this is no problem, but as a standard user in production, this permission is denied. This confused me, as I had been using my ChatterHelper class with no problems when called from triggers, so what was different when called from a Visualforce page.

After some investigation into the Salesforce security model, I learned that a trigger is executed with system-level permissions, but a Visualforce page is executed with the permissions of the current user. This makes sense, no issues here. But what am I supposed to do to allow my TestHelper class system-level permissions to make modifications to the Chatter objects?

Here's the answer:

I went to Develop > Apex Class and found the controller for my AddChatterFollowers page. There is a handy button there called "Security". Click it and select the profiles to allow. After making this change, I tried again as a standard user and found no problems.

Until next time, friends, here's to lackluster testing!