Author Archives: eric.kintzer@cropredy.com

Conga Composer – Excel – Array Formulas – aargh

We’re a steady user of Conga Composer to generate Excel reports for our users where we can’t get SFDC to format a report the way the users want. This week, an interesting problem arose.

Business problem
Display a count of distinct values in a set of rows matching some condition; some rows may have null values

Background
Without getting into too much detail, we needed to count unique accounts for various conditions in the Conga-generated, merged Excel template. I couldn’t coerce the well-known ‘Power of One’ solution to work for us in the actual SFDC report that serves as the Conga source data so the decision was made to do the counting in Excel using new cell ranges below the generated Conga output.

How do you count distinct values in Excel?
If you go to the Microsoft Excel Help, you get this page.

For example, one method is to use this excel formula: =SUM(IF(FREQUENCY(MATCH(B2:B10,B2:B10,0),MATCH(B2:B10,B2:B10,0))>0,1))

Now, if you coded up this formula in the Conga template, you couldn’t directly use the cell value B10 because Conga will generate a variable number of rows based on your source data. So, you would code this in Excel as:
=SUM(IF(FREQUENCY(MATCH(B2:B2,B2:B2,0),MATCH(B2:B2,B2:B2,0))>0,1)) and rely on Conga+Excel to expand the outer range of each cell range expression to be B13 or B24 or B65 or whatever, depending on how many rows of generated data (where column B represents Excel value to be counted and rows 2…n are what Conga inserts into the merged template. I’m presuming you understand Conga merge templates.

Unfortunately, this does not work as the formula =SUM(IF(FREQUENCY(MATCH(B2:B2,B2:B2,0),MATCH(B2:B2,B2:B2,0))>0,1)) has to be entered into Excel as an array formula and Conga won’t expand/Excel array formulas by adjusting the outer range and B2:B2 expression

So, now the problem is how to count distinct values without using array formulas.

The solution that worked for us (with limitations)
Some Google searching yielded this count distinct formula that does not rely on Excel array formulas:
=SUMPRODUCT(1/COUNTIF(List1, List1))

Geez, how does this work? I’m used to the second argument of COUNTIF being some expression that is tested against the elements of the first argument but here we are comparing the list to itself. The MSFT documentation of COUNTIF doesn’t have such an example.

Well, I turned to the handy Excel formula expression evaluator in my Windows Excel 2010. Take a look at the picture below (click to see full size so you can read it):
Count distinct formula evaluation

As you can see from the picture and successive evaluations, the COUNTIF(list1,list1) looks at each element in the list and sees how many times it appears in itself, returning an array of the same size as List1. Then the reciprocal is taken of each resulting array element.

So, if a value X appears twice in the range, the resulting array will be {2,..,2,…} and thus the reciprocal array is {.5,..,.5,…}. If also a value Y appears in the range three times, the resulting array will be {2,3,3,…2,…}. Positions are of course arbitrary as there is no assumption made about sorting. And the reciprocal array would be {.5,.333,.333,…5,…}

Finally, SUMPRODUCT, because it has only one array, doesn’t multiply anything but does add up the values in the reciprocal array. Adding .5 twice is 1; Adding .33 three times is 1. Summing 1 and 1 is 2 – two unique elements

As you can see from the picture, the result ‘4’ is the number of unique Account Names. pretty cool and no array formula required.

But what happens if you have nulls in the range of Account Names?
The expression will return #DIV/0. Sigh.

The solution is to recognize that if the comparison array in COUNTIF is guaranteed to have a non-null value, then the counts will work. Look at this picture:

Count distinct with null formula evaluation

Note the concatenation operator ‘&’. This appends a zero length string to the second list in the COUNTIF. Thus your final result will include a count of distinct Account names plus 1 for the null cells. If you are guaranteed there will be null cells (and we were due to Conga subtotal lines inserted), you just subtract one from the final result of the =SUMPRODUCT(…) formula.

Adventures in URL Hacking ‘Send an Email’ on Cases

URL hacking the SFDC Send an Email page is well-known. See for example:

Adventure starts
In one of my orgs, we were using On Demand Email-to-Case. If you’ve used this, you know that SFDC creates a thread_id value based on the orgId and case Id wrapped in special keywords (e.g. ref:_00DK0AaiLo._500K05CUEs:ref) . Any subsequent email sent from the Case will include this thread_id in the body and subject line so that if there is an email reply to your On Demand email-to-case email address, the reply is attached to the originating case (and doesn’t create a new case).

I had a custom VF page associated to the Case object and had added a custom button to send an email. Here was the URL hack (line breaks added for clarity):

<apex:commandButton 
  action="{!URLFOR($Action.Activity.SendEmail,csW.cs.id,
  [p2_lkid=csW.cs.contactId,
  rtype=LPAD('3',3,'00'),
  p3_lkid=csW.cs.id,
  retURL=$CurrentPage.url])}"
 value="Send an Email"/>
  • csW.cs.id refers to a controller variable csW of type CaseWrapper that had a member variable cs of type Case.
  • Per the URL Hack tips, p3_lkid refers to the ID of the target object used for merge fields – here, a Case object. p2_lkid references the recipient of the email – here, a Contact record id.

Adventure becomes a mystery
Given the following sequence:

  1. Customer sends email to Foo-support@ourdomain.com
  2. Customer receives auto-reply email containing the thread_id
  3. From the Case, Support team replies to the original email message (from: foo-support@ourdomain.com)
  4. Customer replies to message in step 3; this reply is properly attached to the Case.
  5. From the Case, Support team sends, using the custom button, a new email messsage to the customer (from: foo-support@ourdomain.com).
  6. Customer replies to message in step 5. This reply creates a brand new case!

It was as if emails generated from the ‘Email Message’ reply link had the proper thread_id but emails generated from the ‘Send and Email’ url hack did not – yet both resolved to the proper SFDC page with all fields filled in correctly.

Adventure Ends
This took me a while to figure out what with the flurry of emails to inspect and that the SFDC page that sends the emails looked just fine.

Here was the solution:

In my URL hack for ‘send an email’, when merge fields are resolved by SFDC, I got this:

https://cs9.salesforce.com/_ui/core/email/author/EmailAuthor
?p2_lkid=003K000000jbawQIAQ
&rtype=003
&p3_lkid=500K0000005CUEsIAO
&retURL=...

Note that &p3_lkid is an 18 character ID of the Case. When the email message was sent, SFDC used this 18 character ID and generated a threadId of ref:_00DK0AaiLo._500K05CUEsIAO:ref.

Contrast this thread_id with the thread_id generated by SFDC when the Case was originally created: ref:_00DK0AaiLo._500K05CUEs:ref, That is:

ref:_00DK0AaiLo._500K05CUEsIAO:ref
ref:_00DK0AaiLo._500K05CUEs:ref

Even though SFDC normally treats 18 character IDs the same as 15 character IDs, if the value of the URL hack field p3_lkid=csW.cs.id is an 18 character ID, the Send an Email SFDC code (not mine) creates a thread_id that is 3 characters longer than the thread_id normally generated when the Case was created via On Demand Email-to-Case

Obviously, the problem is that the ‘Send an Email’ code expected the value of &p3_lkid=500K0000005CUEsIAO to be a 15 character ID, not an 18 character Id, that is: &p3_lkid=500K0000005CUEs.
In other words, the SFDC Send an Email code is not independent of p3_lkId length (15 versus 18). Compounding the mystery was that Email To Case parses thread_ids in such a way as to ignore the 15 vs 18 character ID origin.

Thus, the solution was to change the URL Hack to the following (by substringing the 18 character Case Id to a 15 character value):

<apex:commandButton 
  action="{!URLFOR($Action.Activity.SendEmail,csW.cs.id,
  [p2_lkid=csW.cs.contactId,
  rtype=LPAD('3',3,'00'),
  p3_lkid=LEFT(csW.cs.id,15),
  retURL=$CurrentPage.url])}"
 value="Send an Email"/>

Soft alerts

In several projects I have done, the users don’t want to be bothered with required fields or validation errors on Accounts or Opportunities until they get closer to quotation or order fulfillment. Yet marketing and management reports frequently want ‘categorization’ or ‘taxonomic’ fields filled in (but can live with them blank).

To resolve this in a proactive way and still use SFDC out-of-box page layouts – no VF custom controller or controller extension , I use soft alerts on Account and Opportunity that warn the user that data is not complete and that it should be completed –but it is not urgent that it is done so right this minute.

Here is an example Foo__c object with a soft alert when the Name field contains a ‘colorful’ word
Foo_fubar

This is done with a formula field that leverages the IMAGE() function as shown here:

/* Form of an alert is a set of concatenated strings as follows:

IF(<condition 1>,
IMAGE("/img/samples/flag_yellow|red|green.gif" , 'Yellow|Red|Green' ,12,12) & ' alert text with leading space' & BR() ,
NULL
) &
next condition as above for second alert */


IF (CONTAINS(Name, 'fubar') ,
IMAGE("/img/samples/flag_red.gif" , 'Red' ,12,12) & ' Name contains a colorful slang word' & BR(),
NULL)
&
IF (CONTAINS(Name, 'sylvan') ,
IMAGE("/img/samples/flag_green.gif" , 'Green' ,12,12) & ' Name contains a peaceful word' & BR(),
NULL)

The solution has some limitations as the total size of the formula must be under 5000 characters but for a dozen or so possible alerts, you can avoid using a VF controller. Note the second alert that appears if the Foo__c.name field has a nice word as shown here:
foo_sylvan

What if there are no alerts – how to suppress the custom field on the standard page layout?
If there are no alerts, why waste the valuable line to show nothing? Unfortunately, SFDC standard page layouts don’t permit conditional rendering of fields. Now, you need to escape to Visualforce but the goal is to preserve the advantages of your page layout using apex:detail.

We’ll start with a simple VF page (and we assume that inlineEdit is enabled for your users as it is so convenient):

<apex:page standardcontroller="Foo__c" >
	<apex:pageMessages id="pageMsgs" showDetail="true"/>
	<apex:detail inlineEdit="true" relatedList="true" showChatter="false" subject="{!Foo__c.id}" />	

</apex:page>

In order to suppress the Alerts field, we’ll need to use jQuery to locate the DOM elements and remove them when the value is null. So, here is the expanded page:

<apex:page standardcontroller="Foo__c" >

<apex:includescript value="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js" />

<script>
		$j = jQuery.noConflict();		//	reassign $ to $j to avoid conflict with VF
		var	fooViewJQApp	= {};       //	define the application
		$j(function() {					// 	jQuery shorthand for $(document).ready(function() ..  Wait until page loads 

			(function(app) {			//	Define anon function
				//	--------------------------
				//	variable definitions
				//	--------------------------
				
				//	--------------------------
				//	init:	do initializations
				//	--------------------------
				app.init	= function() {
					app.bindings();		// establish bindings
					app.hideNullAlerts();  // hide the alerts field on page load if null
				};				
				//	-----------------------------------
				//	bindings:  events to event handlers
				//	-----------------------------------
				app.bindings = function() {
					// set up binding for events
                };	// end all bindings				
				
				//	------------------------------------
				//	hideNullAlerts:	Alerts formula field takes up a row and should be suppressed if null (Caption + field)
				//	------------------------------------
				app.hideNullAlerts = function() {
					$j('.labelCol').each(function() {
						if ($j(this).text() == 'Alerts') {		// found the <td> for the caption
							var $alertVal = $j(this).next();	// get adjacent td for the inlineEdit value
							if ($alertVal.text().length == 0) {	// formula field if no value has length 0
								$j(this).parent().remove();			// locate the parent of the alerts label (a tr) remove the <tr> and everything inside it
								return false;						// breaks out of each loop	as we found the alerts field
							}	
						}
					});
				};

				app.init();			// Anon function invokes its own init function
			}) (fooViewJQApp);		// Execute anon function passing in an object representing our application
		});			
	</script>


	<apex:pageMessages id="pageMsgs" showDetail="true"/>
	<apex:detail inlineEdit="true" relatedList="true" showChatter="false" subject="{!Foo__c.id}"  
                     oncomplete="fooViewJQApp.hideNullAlerts();"/>	

</apex:page>

Now, this deserves some explanation:

  1. The jQuery pattern I borrowed from the kittyDressUp pattern in “jQuery Mobile Web Development Essentials” Chapter 7. It provides a nice place to hang all your functions and provide some order and consistency to using jQuery and javascript on your VF page. When you inspect a VF page in Eclipse, it is jarring to see VF markup and javascript interspersed and it can make the page harder to read as there are two different technologies at play working on a common DOM model. I didn’t need to set up any bindings but I left in the placeholder for other future jQuery uses.
  2. When the page loads, the jQuery app’s hideNullAlerts() function is invoked. When the page partial refreshes on an inlineEdit save, the apex:detail oncomplete event is invoked by VF and that too, calls hideNullAlerts().
  3. Using standard page layouts, there is no obvious id field to look for to find the DOM element corresponding to the ‘Alerts’ caption and alerts content field. You could use the SFDC-generated ID but I hate hardcoding ids in anything. Firebug or its equivalent is your friend here to understand the SFDC VF DOM structure for a standard page layout (see next figure). The $j('.labelCol') locates all elements on the page which are captions. Then, each() is used to go through every caption looking for the Alerts caption. Upon locating it (a td tag), the function then looks for the adjacent next sibling (also a td) and tests to see if the result of text() is of length 0. Note that jQuery text() returns the combined contents of the selected element and all descendents. If so, then the alert is null. So, go up one level to the tr and remove it. Since there is only one alert field to remove, stop the each() loop by returning false.

  4. Here is an excerpt of the HTML generated by VF for a standard pagelayout that has inlineEdit enabled. As always, when messing about with the SFDC-generated HTML, you are subject to changes in future releases.

    <table class="detailList" cellspacing="0" cellpadding="0" border="0">
    <tbody>
    <tr>
      <td class="labelCol">Alerts</td>
      <td id="00NK0000001OD2cj_id0_j_id28_ilecell" class="data2Col inlineEditLock" onmouseover="if (window.sfdcPage && window.sfdcPage.hasRun) sfdcPage.mouseOverField(event, this);" onmouseout="if (window.sfdcPage && window.sfdcPage.hasRun) sfdcPage.mouseOutField(event, this);" onfocus="if (window.sfdcPage && window.sfdcPage.hasRun) sfdcPage.mouseOverField(event, this);" ondblclick="if (window.sfdcPage && window.sfdcPage.hasRun) sfdcPage.dblClickField(event, this);" onclick="if (window.sfdcPage && window.sfdcPage.hasRun) sfdcPage.clickField(event, this);" onblur="if (window.sfdcPage && window.sfdcPage.hasRun) sfdcPage.mouseOutField(event, this);" colspan="3">
        <div id="00NK0000001OD2cj_id0_j_id28_ileinner">
         <img width="12" border="0" height="12" alt="Green" src="/img/samples/flag_green.gif">
    Name contains a peaceful word
        <br>
        </div>
      </td>
    </tr>
    <tr>
    <tr>
    <tr>
    </tbody>
    </table>
    

Visualforce – jQuery pattern – enforce only single checkbox in a list

Over the last few months, I’ve dabbled in adding some client-side improvements to Visualforce pages. But, I’ve resisted doing much with Javascript and jQuery in order to keep the development models limited to just Visualforce, Apex, and Force.com. There was something about a blend of Javascript and VF markup on a page that I always found jarring and hard to read and understand – too much syntactic dissonance

But, jQuery and jQuery Mobile are highly recommended for developing Salesforce1 apps. After reading jQuery Mobile Web Development Essentials, Second Edition by Camden and Matthews, I discovered a nice organizing pattern for jQuery that was a good place to add in all of a page’s jQuery event handlers (see chapter 8). So, I decided to apply it to a commonly-occurring problem – display a list with checkboxes next to each row, but allow only one box to be checked before submission to the server. This avoids writing controller code to enforce a single selection (as radiobuttons can’t easily be applied to table rows).

So, here it is where the table displays a list of Quotes on an Opportunity and only one can be specified for the action

<apex:page standardController="Opportunity" extensions="OppoControllerExtension">

	<apex:includescript value="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js" />
	<script>
		$j = jQuery.noConflict();		//	reassign $ to $j to avoid conflict with VF
		$j(function() {				// 	jQuery shorthand for $(document).ready(function() ..  Wait until page loads 
			//	define the application
			var	OppoToSvcContract	= {};
			
			(function(app) {		//	Define anon function
				//	--------------------------
				//	variable definitions
				//	--------------------------
		
				//	--------------------------
				//	init:	do initializations
				//	--------------------------
				app.init	= function() {
					app.bindings();		//	establish bindings	
				};
		
				//	-----------------------------------
				//	bindings:  events to event handlers
				//	-----------------------------------
				app.bindings = function() {
					// set up binding for onclick checkbox: Unchecks other checkboxes
					$j('input[id*="qCheckbox"]').click(function(e) {		// attach anonymous fn as event handler to click event for all DOM checkbox whose id attr contains qCheckbox, $j(this) available
						var clickedElmId	= $j(this).attr('id');			// DOM elm that was clicked
						$j('input[id*="qCheckbox"]').each(function() {		// Go thru all checkboxes
							if ($j(this).attr('id') != clickedElmId) {		// if id diff than clicked elm id, uncheck	
								$j(this).removeProp('checked');
							}
						});
					});													// end binding
				};														// end all bindings
				
				app.init();				// Anon function invokes its own init function
			}) (OppoToSvcContract);		// Execute anon function passing in an object representing our application
				
				
		});	
	</script>





	<apex:sectionHeader title="Create a Service Contract from this Opportunity's Quotes:" subTitle="{!Opportunity.name}"/>
	
	<apex:form id="theForm">
		<apex:pageMessages/> 
		<apex:pageBlock >
			<apex:pageBlockButtons location="top">
		 		<apex:commandbutton value="Create Service Contract from Oppo+Quote" 
						action="{!createSvcContract}"/>
		 		<apex:commandbutton value="Cancel" immediate="true" action="{!cancel}"/>      
			</apex:pageBlockButtons>
			
			
		</apex:pageBlock>
		
		<apex:pageBlock title="Quotes (to use as source for Service Contract line items)">
	
			<apex:pageBlockTable id="quotesTbl" value="{!availQuotes}" var="aq" style="width:90%; border-color: rgb(248,248,248);" headerClass="quoteTblHdr">
                <apex:column headerValue="Select One" style="width: 5%;">
                	<apex:inputCheckbox id="qCheckbox" value="{!aq.isSelected}"/>  <!--  jQuery event handler takes care of only one box checkable -->
                </apex:column>   
                <apex:column headerValue="Name" style="width: 10%">
					<apex:outputLink value="{!URLFOR($Action.Quote__c.View,aq.qw.q.id)}">{!aq.qw.q.name} {!aq.forecastMsg}</apex:outputLink> 
				</apex:column>    
			</apex:pageBlockTable>
         </apex:pageBlock>		
	</apex:form>
</apex:page>

Aligning labels with selectRadio within a pageBlockSection

Here was the issue:

I had a pageBlockSection with a mix of VF components bound to SObject fields and components bound to controller properties. In the latter case, I was using selectRadio.

The label on the selectRadio did not vertically align with the top radio button. You can see an aligned and misaligned example in the picture below.
Aligning label and selectRadio
Here is the VF markup for the aligned and misaligned example:
Solution: Use style="margin-top: -10px;".

<apex:pageBlockSection title="New Quote Wizard Parameters" columns="1" collapsible="false" id="quoteWizParams">
				
	<apex:selectRadio label="Type" value="{!quoteWiz.selectedQuoteWizType}" 
                          layout="pageDirection" id="quoteWizTypeRadios" 
                          style="margin-top: -10px;">
  		<apex:selectOptions value="{!quoteWiz.quoteWizTypeOptionList}"/>
        </apex:selectRadio>
				
	<apex:pageBlockSectionItem >
		<apex:outputText value="Service term (in months)"/>
		<apex:inputText value="{!quoteWiz.initServiceTermMos}"/>
	</apex:pageBlockSectionItem>
							
	<apex:selectRadio label="SnS Level" value="{!quoteWiz.selectedSnsLevel}" 
                          layout="pageDirection" id="snsLevelRadios">
	        <apex:selectOptions value="{!quoteWiz.snsLevelOptionList}"/>
	</apex:selectRadio>
</apex:pageBlockSection>

Side note: The use of the VF attribute label avoids the need for pageBlockSectionItem markup – SFDC automatically puts the label into the proper cell as if you were using apex:outputLabel.

Visualforce performance tuning – lessons learned – Part I

I was optimizing a complex VF page the other day and learned a couple of tips that I did not otherwise know beforehand

Lesson I – Use apex:variable to avoid repetitive getters that always return the same value
The page used apex:repeat to create a dataTable. Each dataTable had five rows. In the edge case I was tuning, there were sixty (60) repeats – thus sixty datatables.

<!--  snip -->
<apex:repeat value="{!tableList}" var="t">
  <apex:dataTable value="{!tableRows}" var="r">
     <apex:column headerValue="column00">
       <apex:inputText value="{!r.field00}" rendered="{!isEditable}"/>
       <apex:outputText value="{!r.field00}" rendered="{!NOT(isEditable)}"/>
     </apex:column> 
  </apex:dataTable>
</apex:repeat>
<!-- snip -->

Note that the decision to render the field in column00 as input or output is based on a controller variable isEditable. This controller variable was actually a getter and had some complex logic within:

public Boolean isEditable {
  get{
      if (isEditable == null)      
         // complex logic here to set isEditable - 
         // do only once as nothing the page can do will change the decision
      return isEditable;
  }
  private set;

Looking at the debug log, when the page is constructed, this controller getter was invoked 60 * 5 * 2 times = 600 invocations! Holy moly. Even though the getter only calculates its value once, it still gets invoked for every rendered= reference on the page.

The solution is to add an apex:variable outside of the repeat to cache the value for Visualforce and thus save on all those getter invocations. Here is the new page:

<!--  snip -->
<!-- Define an apex:variable to hold the controller's getter value. 
     Use naming convention of avXXX to clue developer
     where used that it is a reference to an apex:variable -->
<apex:variable value="{!isEditable}" var="avIsEditable"/> 
<apex:repeat value="{!tableList}" var="t">
  <apex:dataTable value="{!tableRows}" var="r">
     <apex:column headerValue="column00">
       <apex:inputText value="{!r.field00}" rendered="{!avIisEditable}"/>
       <apex:outputText value="{!r.field00}" rendered="{!NOT(avIsEditable)}"/>
     </apex:column> 
  </apex:dataTable>
</apex:repeat>
<!-- snip -->

Result was a couple of tenths of seconds shaved off the page render time

Lesson 2 — Multiple controller extensions and constructor time

I had a different page with several commandButtons. Some of the buttons invoked action methods in extension00, the others invoked action methods in extension01. There was no page action.

<apex:page standardController="Opportunity" extensions="OppoExtension00,OppoExtension01">
<apex:form>
  <apex:pageBlock>
    <apex:pageBlockButtons>
      <apex:commandButton value="Invoke 00" action="{!extension00Action}"/>
      <apex:commandButton value="Invoke 01" action="{!extension01Action}"/>
    </apex:pageBlockButtons>
  </apex:pageBlock>
</apex:form>
</apex:page>

When the page is initially rendered, both controllerExtensions’ constructors are invoked. If the constructors do heavy lifting in terms of initializations (such as SOQL calls), then all of that work must finish before the page can render. If the user is only going to click on one commandButton, then the constructor time for any other commandButton using a different extension is wasted. Now, I should have known that but I was misdirected by thinking that the constructor time only applied when the commandButton was clicked and the work had to be done.

The solution was to optimize the extension’s constructors. Two techniques were applied:

  • Move SOQL out of the constructors into getters
  • Using static variables to hold common data that would be the same for both constructors

Salesforce to Salesforce Using REST – Part II

A bit of a temporary setback here.
I naively thought that whatever you could do in the SFDC SOAP API you could do in the REST API. Specifically, that it would be possible for APEX code in the source product catalog org to do mass inserts, updates, or upserts in the target org. NOPE.

The REST API only allows Create-Update-Delete operations on single resources – that is, single records. You can query (Read) to your heart’s content over many records. Teach me to not fully read the documentation.

Alternatives

  1. I could have the source org use the SOAP API. I looked into this and it is kind of a heavy-weight solution wherein you generate a massive Apex class from the partner or Enterprise WSDL. I tried this and realized that the Enterprise WSDL was too large to import and thus would need to be trimmed before I could generate the APEX bindings to it. This would mean maintenance issues. The Partner WSDL isn’t strongly typed and thus is more trouble than it’s worth to work with.
  2. I could use the SFDC Bulk API from the source org. This has other issues notably the input has to be either XML or CSV so I’d need to do conversions and, more importantly, it is asynchronous making the Visualforce page flow control complex with polling. Yuck.
  3. I could write a custom REST service that would be deployed on all target orgs that would handle the upserts through its own logic. This looked like the best way forward as the source org code would be simple – create a payload in the POST body for the Product2, PricebookEntry, and Pricebook2 SObjects. The target org custom REST service would figure out what needed to be done, cross-referencing source org data with existing target org data matching on Product2.ProductCode.

In many ways, option 3 is best as it will make testing easier through separation of responsibilities and much, much simpler Test Mock classes on the source org’s Apex class.

Salesforce to Salesforce Using REST – Part I

Here was the business problem:

Migrate Product2-PricebookEntry-Pricebook2 data from a sandbox to staging sandbox and then onto production.

My previous brute force solution was to use the Excel Connector, querying rows from the Pricebook2, PricebookEntry, and Product2 tables using a connection to the sandbox, then successively inserting rows into the target org while replacing ID fields in the upload PricebookEntry dataset to match the just uploaded Pricebook2 and Product2 datasets. (PricebookEntry is a junction record between Pricebook2 and Product2). This involved Excel VLOOKUP functions and lots of care to make sure that I didn’t reorder rows by accident.  Doing this once wasn’t so bad but the business changes the product line regularly and I was finding this tedious.

Solution Outline

Why not build an admin VF page in my org that could read from my source dataset sandbox and insert rows into the target sandbox/prod org?  To make this happen, the Visualforce controller code would need to be able to do SOQL and DML against a different org.  The way to do this is by exploiting the SFDC REST API and Apex HTTP Callouts.

The SFDC REST API documentation is written assuming that the calling application is not a Salesforce org but there is no restriction that this be so.

Step 1 – AuthenticationOAUTH Authentication SFDC to SFDC

The relevant SFDC REST API authentication documentation is here.  I chose the username-password method as I’m the admin and would use this to get authentication on the target orgs using my own username and passwords on those orgs.  I also opted for OAuth 2.0 authentication as I wanted to try this rather than using sessionIds.

public with sharing class HttpRest {

	//	Class to handle HTTP Rest calls to other SFDC instances
	//
	//	[HTTP-00]	- Reached limit on callouts
	//	[HTTP-01]	- Unable to get OAuth 2.0 token from remote SFDC
	//	[HTTP-02]	- Error in SOQL REST query

	String	accessToken;					// OAuth 2.0 access token
	String	sfdcInstanceUrl;				// Endpoint URL for SFDC instance
						
	private HttpResponse send(String uri,String httpMethod) {
		return send(uri,httpMethod,null);
	}
		
	private HttpResponse send(String uri, String httpMethod, String body) {
		
		if (Limits.getCallouts() == Limits.getLimitCallouts())
			throw new MyException('[HTTP-00] Callout limit: ' + Limits.getCallouts() + ' reached. No more callouts permitted.');
		Http		h		= new Http();
		HttpRequest	hRqst	= new HttpRequest();
		hRqst.setEndpoint(uri);						// caller provides, this will be a REST resource
		hRqst.setMethod(httpMethod);				// caller provides
		hRqst.setTimeout(6000);	
		if (body != null) 
			hRqst.setBody(body);					// caller provides
		if (this.accessToken != null)				// REST requires using the token, once obtained for each request
			hRqst.setHeader('Authorization','Bearer ' + this.accessToken);
		return h.send(hRqst);					// make the callout
	}	
	//	----------------------------------------------------------------------
    //	authenticateByUserNamePassword		: Returns a map of <String,String> of the OAuth 2.0 access token; required before REST calls on SFDC instances can be made 
    //	----------------------------------------------------------------------
    public void authenticateByUserNamePassword(String consumerKey, String consumerSecret, String uName, String uPassword, Boolean isSandbox) {
    	// Reference documentation can be found in the REST API Guide, section: 'Understanding the Username-Password OAuth Authentication Flow'
    	// OAuth 2.0 token is obtained from endpoints:
    	//	PROD orgs	: https://login.salesforce.com/services/oauth2/token
    	//	SANDBOX orgs: https://test.salesforce.com/services/oauth2/token
    	
 		//	OAuth 2.0 access token contains these name/values:
 		//		access_token		: used in subsequent REST calls
 		//		instance_url		: to form the REST URI
 		//		id					: identifies end user
 		//		issued_at			: When signature was created
 		//		signature			: HMAC-SHA256 signature signed with private key - can be used to verify the instance_url	 

		String uri 			= 'https://' + (isSandbox ? 'test' : 'login') + '.salesforce.com/services/oauth2/token';
		String clientId 	= EncodingUtil.urlEncode(consumerKey,'UTF-8');
		String clientSecret = EncodingUtil.urlEncode(consumerSecret,'UTF-8');
		String username		= EncodingUtil.urlEncode(uName,'UTF-8');
		String password		= EncodingUtil.urlEncode(uPassword,'UTF-8');

		String body =	'grant_type=password&client_id=' + clientId + 
						'&client_secret=' + clientSecret +
						'&username=' + username + 
						'&password=' + password; 

		HttpResponse hRes = this.send(uri,'POST',body);
		if (hRes.getStatusCode() != 200) 
			throw new MyException('[HTTP-01] OAuth 2.0 access token request error. Verify username, password, consumer key, consumer secret, isSandbox?  StatusCode=' +
												 hRes.getStatusCode() + ' statusMsg=' + hRes.getStatus());
			
		
		System.debug(FlowControl.getLogLevel(),'response body =\n' + hRes.getBody());
		

		Map<String,String> res = (Map<String,String>) JSON.deserialize(hRes.getBody(),Map<String,String>.class);

		this.accessToken		= res.get('access_token');		// remember these for subsequent calls
		this.sfdcInstanceUrl 	= res.get('instance_url');
		
		
    }


    //	-----------------------------------------------------------------------
    //	doSoqlQuery: Executes a REST query on a remote SFDC and returns a list of SObjects
    //	-----------------------------------------------------------------------
    public List<SObject> doSoqlQuery(String query) {
    	List<Sobject>	res;		
		PageReference 	urlPg	= new PageReference(this.sfdcInstanceUrl + '/services/data/v29.0/query');
		urlPg.getParameters().put('q',query); 

		String uri 				= urlPg.getUrl();				// let APEX do the URL encoding of the parms as necessary
		HttpResponse hRes = this.send(uri,'GET');
		if (hRes.getStatusCode() != 200) 
			throw new MyException('[HTTP-02] Error in query ' + uri + ' StatusCode=' +
												 hRes.getStatusCode() + ' statusMsg=' + hRes.getStatus());
		
		// Response body comes back as:
		// {"totalSize":10,
		//	"done":true,
		//	"records":[
		//				{"attributes":{
		//					"type"	: "the sobject",
		//					"url"	: "/services/data/v29.0/sobjects/the sobject/the id"
		//				},
		//				"field0 in query"	: "value of field 0",
		//				"field1 in query"	: "value of field1",
		//				...},
		//				next record ...
		//				]
		//	}
		JSONParser jp = JSON.createParser(hRes.getBody());
		do{
			jp.nextToken();
		} while(jp.hasCurrentToken() && !'records'.equals(jp.getCurrentName()));
		jp.nextToken();  // token is 'records'
		res = (List<SObject>) jp.readValueAs(List<SObject>.class);		// Let caller cast to specific SObject
		return res;
    }
 
 
 	//	--------------------------------------------------------------
    // 	TEST METHODS - MOCKS
    //	---------------------------------------------------------------	
	public class authenticateByUserNamePasswordMock implements HttpCalloutMock {
    	
    	Boolean	isMockResponseSuccessful;
    	
    	public authenticateByUserNamePasswordMock(Boolean isMockResponseSuccessful) {
    		this.isMockResponseSuccessful	= isMockResponseSuccessful;
    	}
    	
    	public HttpResponse respond(HttpRequest rqst) {
    		HttpResponse hResp		= new HttpResponse();
    		if (this.isMockResponseSuccessful) {
    			hResp.setStatusCode(200);
    			hResp.setBody('{' +			// data copied from SFDC example
    							' "id":"https://login.salesforce.com/id/00Dx0000000BV7z/005x00000012Q9P", ' +
								' "issued_at":"1278448832702",' +
								' "instance_url": "https://na1.salesforce.com",' +
								' "signature":"0CmxinZir53Yex7nE0TD+zMpvIWYGb/bdJh6XfOH6EQ=",' +
								' "access_token": "00Dx0000000BV7z!AR8AQAxo9UfVkh8AlV0Gomt9Czx9LjHnSSpwBMmbRcgKFmxOtvxjTrKW19ye6PE3Ds1eQz3z8jr3W7_VbWmEu4Q8TVGSTHxs"' 
							+'}');			
    		}
    		else {
    			hResp.setStatusCode(400);
    			hResp.setStatus('Bad request');
    		}
    		return hResp;
    	}
    }
    
    public class doSoqlQueryMock implements HttpCalloutMock {
    	
    	Boolean	isMockResponseSuccessful;
    	
    	public doSoqlQueryMock(Boolean isMockResponseSuccessful) {
    		this.isMockResponseSuccessful	= isMockResponseSuccessful;
    	}
    	
    	public HttpResponse respond(HttpRequest rqst) {
    		HttpResponse hResp		= new HttpResponse();
    		if (this.isMockResponseSuccessful) {
    			hResp.setStatusCode(200);
    			hResp.setBody('{' +			// data copied from SFDC example
    							' "totalSize": 2,' 		+ 
								' "done" 	: true,' 	+
								' "records" : ['		+
								'              { 	"attributes" 	: {' +
								'										"type"			: "Account",' +
								'										"url"			: "/services/data/v29.0/sobjects/Account/00id"' +
								'									},' +
								'				"ID"	: "00id"' +
								'				},' +
								'              { 	"attributes" 	: {' +
								'										"type"			: "Account",' +
								'										"url"			: "/services/data/v29.0/sobjects/Account/01id"' +
								'									},' +
								'				"ID"	: "01id"' +
								'				}' +
								'			]' +
								'}');			
    		}
    		else {
    			hResp.setStatusCode(404);
    			hResp.setStatus('Not found');
    		}
    		return hResp;
    	}
    }
    
    
   	//	--------------------------------------------------------------
    // 	TEST METHODS - tests
    //	--------------------------------------------------------------- 
	@isTest
    private static void testAuthenticateByUserNamePassword() {
    	HttpRest hr = new HttpRest();
    	try {
    		Test.setMock(HttpCalloutMock.class, new authenticateByUserNamePasswordMock(true));	
    		hr.authenticateByUserNamePassword('somekey','somesecret','someusername@somedomain','somepw+token',false);
    		System.assertEquals('00Dx0000000BV7z!AR8AQAxo9UfVkh8AlV0Gomt9Czx9LjHnSSpwBMmbRcgKFmxOtvxjTrKW19ye6PE3Ds1eQz3z8jr3W7_VbWmEu4Q8TVGSTHxs',hr.accessToken);
    		System.assertEquals('https://na1.salesforce.com',hr.sfdcInstanceUrl);
    	}
    	catch (Exception e) {System.assert(false,'shouldnt. Exception:' + e.getMessage() + e.getStackTraceString());}
    	try {
    		Test.setMock(HttpCalloutMock.class, new authenticateByUserNamePasswordMock(false));
    		hr.authenticateByUserNamePassword('somekey','somesecret','someusername@somedomain','somepw+token',false);
    		System.assert(false,'shouldnt happen, mock should produce error');
    	}
    	catch (Exception e) {System.assert(e.getMessage().contains('[HTTP-01]'),e.getMessage());}
    }

	@isTest
    private static void testDoSoqlQuery() {
    	HttpRest hr = new HttpRest();
    	List<Sobject>	sobjResList;
    	try {
    		Test.setMock(HttpCalloutMock.class, new doSoqlQueryMock(true));	
    		sObjResList = hr.doSoqlQuery('select id from Account');
    		System.assertEquals(2,sObjResList.size());
    	}
    	catch (Exception e) {System.assert(false,'shouldnt. Exception:' + e.getMessage() + e.getStackTraceString());}
    	try {
    		Test.setMock(HttpCalloutMock.class, new doSoqlQueryMock(false));
    		sObjResList = hr.doSoqlQuery('select id from Account');
    		System.assert(false,'shouldnt happen, mock should produce error');
    	}
    	catch (Exception e) {System.assert(e.getMessage().contains('[HTTP-02]'),e.getMessage());}
    }


}

Implementation Notes

  1. You’ll need your own Exception class for this to compile. The example calls it MyException but I usually name these after the project or business unit wherein my code is deployed.
  2. Note that method authenticateByUserNamePassword takes five arguments. You will need to set up the consumerKey and consumerSecret on the target org using Setup | Develop | Remote Access. Starting in V29.0, this is now called a ‘Connected App’.  Your source org will need access to these values – perhaps through custom settings, directly-entered into your VF page, or for more security, as encrypted fields in an custom SObject.
  3. Your source org will need to Setup | Security Controls | Remote Site Settings for both the OAuth 2.0 authentication access token provider(s) as well as the target instance(s).
Connected App Setup in the target org

There are several OAuth scopes available to you. I chose ‘Access and manage your data (api)’ as that met my requirements.ConnectedAppSetup

Remote Site settings in the source org

In my source org, I set up four Remote Sites – two for the production and sandbox OAuth 2.0 access token providers and one each for the target PROD and target sandbox.
remoteSites

Good APEX Naming Conventions

To inaugurate the blog, I’ll start with something prosaic but one where I saved myself a lot of grief over time as I built up large APEX systems. Naming conventions are of course a matter of personal preference. I’ve seen a lot of posts asking for help on the SFDC Apex Forum and many times the poster’s naming conventions get in the way of those trying to assist. Professional programmers will already know all this stuff, so this might be more appropriate for Apex newbies and ‘toddlers’.

Class and method names

  • Follow the SFDC conventions. Your code is a mix of SFDC classes and methods so follow the idiom. Class names are initialcaps.
  • Method names are initial lowercase. Don’t use underscores. If the method returns a Boolean, make the method name an assertion statement
public class Foo{
  public Boolean isClosed() {
   // some condition that returns true if something is true, else false
  }
}

Consistency in abbreviations

I use a consistent shorthand for my APEX variables (and by extension Visualforce var= attributes).

  • Variables that reference Accounts start with ‘a’, Opportunities with ‘o’, Contracts with ‘c’, Quotes with ‘q’, QuoteItems with ‘qi’ and so on.
  • Where there is a first letter conflict, I stick to something clear and easily remembered such as ‘cs’ for Case or ‘attach’ for Attachments
  • Custom Objects would use a shorthand made up the first letters of each word (or maybe syllable) such as ‘pcr’ for Product_Config_Rule__c

Consistency in references to IDs

  • IDs are your friend as they can be used to reference maps, query for SObjects, and are globally unique.  Use your variable names to ‘type’ your Ids
  • Thus, aId is an Account Id, qiId is a quote Item Id
Account a;  // the account being worked on
Account aOld; // the prior version of the Account from Trigger.old
ID aId;  // an account Id

Consistency in collection variable names

  • You are constantly coding in the bulk idiom or handling result lists from queries. Because SFDC provides different instance methods for lists, sets, and maps and because each collection type serves a different purpose, it greatly improves code readability when the variable name makes it clear whether it is a list, set or map.  I always append my collection variable names with either ‘List’, ‘Set’, or ‘Map’. It also helps you code as you operate on the collection – as you type the variable name, you remind yourself of how it works (for example, maps aren’t in any sort order).
  • Since lists are often created to do DML or are the results of queries, add a common shorthand to the variable name as to purpose. For example ‘ins’ for a insert list, ‘upd’ for an update list, or ‘res’ for a query result list

Good names

List<Account> aInsList;  // a list of Accounts to insert
List<Opportunity] oUpdList;  // a list of Opportunities to update
List<Contract> cReslist = [select id from Contract where name like 'Foo%'];  // query result

Set<ID> aIdSet;  // a set of unique accountIds

Map<ID,Case> csIdToCaseMap; // a map of Case Ids to Case SObjects
Map<ID,List<Quote>> oIdToQuoteListMap;  // Map of Oppo ID to a list of Quotes

// Map of Case Id to Case with related lists fetched from a query
Map<ID,Case> csIdToCaseWRelListMap =
  new Map<ID,Case> ([select id, accountId, caseNumber, closedDate, contactId, subject, type,
		     (select id, commentBody, createdDate, createdById, isPublished
			     from CaseComments order by createdDate),
		     (select id, messageDate, subject
			     from EmailMessages order by MessageDate asc)
		    from Case where id IN :csIdSet]);

Avoid doing this…

List<Account> accts;  // when referenced in the code, is it a list, set or map?
List<Opportunity] updates;  // updates of what sObject?

Set<ID> ids;  // a set of Ids to what sObject?