Thursday, January 28, 2016

Selecting compound data not supported in Bulk Query

There isn't much out there on this error.  Before doing any data update, I like to export the target object's full data set as a precaution.  My tool of choice is workbench but sometimes it can be a little baby and complain like "Selecting compound data not supported in Bulk Query". 

Turns out that the following fields are going to make your bulk api export fail:


  • Address fields like:
    • Billing Address
    • Shipping Address
  • Latitude and Longitude fields like:
    • BillingLatitude
    • BillingLongitude


These are your most common "compound" fields.  Once you remove these fields from your export, you should be ok.

Source: https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/compound_fields.htm

Tuesday, April 7, 2015

Salesforce Gender (?!)

I recently came across an unusual eclipse IDE error: Cannot specify a gender for a gender neutral language

In the 7 years I've been doing Salesforce work, that error was a first.  Fortunately, I found some folks who have also run into this -

https://developer.salesforce.com/forums/ForumsMain?id=906F00000008uyaIAA

Turns out if you are toggling between a language like Spanish/French and English while building something in Salesforce, you might need to delete the <Gender> tag in the object metadata.


Friday, March 20, 2015

Visualforce Email Templates

Hello, dear readers.  I have been busy and haven't updated this blog in a while but I came across a couple unusual behaviors that are not well documented.  In fact, one of the behaviors led to my support rep getting a knowledge article published.  The topic for today is visualforce email templates and some things to think about if you are using them.  Trust me, none of this is documented and even though Salesforce says "working as designed", I think you may want to look at your solution carefully.

Imagine Joe Q. Salesperson lands a sweet client one Friday morning, creates an opportunity, generates a quote and sends that quote to his boss for approval.  Joe spends the weekend relaxing the afternoon away and the next day still hasn't gotten approval from his boss.  Joe calls the boss and finds out he never got the email alert that something required his approval.  Well, that's strange because Joe never saw an error and the quote was submitted for approval (he can see it in Salesforce). Furthermore, after his boss approved the email, the status in the email alert still said "Pending".  What was going on?  Joe calls IT and is severely irritated because he couldn't close the deal before the weekend and was seeing data in the email that didn't match what was in Salesforce.

So what is it with VF email templates?

Email Alerts with Visualforce Templates Do Not Always Send (and will not throw error)
Let that sink in for a minute - your trusty email alerts, even when the rule or approval process triggers the alert do not always send the email.  And when they don't send, there isn't an error generated.  Not even in the logs.

According to Salesforce, this is working as designed because Joe's administrator put a field on the VF email template that Joe did not have read-access to.  Support was kind enough to generate some documentation of this behavior for us, after the fact.  It should be noted that if your VF template uses a controller, your users will see an error if you don't give them access to the controller.  It seems to me that the expected behavior here is that field level security should be applied.  If the user has access to the field, display it, otherwise, hide it.  Unfortunately that is not the case.


Email Alerts with Visualforce Templates Do Not Always Get the Latest Field Values
If you have an approval process with an action that performs a field update and sends an email alert, the email will not have the field update value if the template is a VF template.  But, wait Salesforce's documentation says

  • Field updates occur before email alerts, tasks, and outbound messages

It is the first bullet in the Field Update considerations.  Except, it doesn't apply when it comes to VF templates.  Again, support says it is working as designed.  If you have a text or html template, the value from the field update is represented in the email, however, this is not the case with a VF template.

Wednesday, September 24, 2014

Data Dictionary Utility

Surely you've had the need to generate a spreadsheet with object and field details for someone to use for a data conversion or integration, right?

I had previously posted a solution involving a Cast Iron orchestration that could go object by object within your org and write to another custom object some details like field name, field data type, length, etc.

Here is another way, that I'll make as a unmanaged package in a future post.  In the interim, here is the recipe:

1. Create a custom object to hold the definitions.  In the anonymous apex below, my reporting object, ObjDef__c has fields like Object_Name__c, Field_Name__c, Data_Type__c, Length__c.

2. Execute the following anonymous apex for each of the objects you'd like to report on.  In my example, I have a custom object called 'Conference__c' that I want to define so that my business partner can create a data dictionary.

****************
//this is the object that you want to define
String type='Conference__c';

map<string, Schema.SObjectType> schemaMap = Schema.getGlobalDescribe();
Schema.SObjectType mySchema = schemaMap.get(type);
map<string, Schema.SObjectField> fieldMap = mySchema.getDescribe().fields.getMap();

//this is the object that you will report on
list<ObjDef__c> myDefs = new list<ObjDef__c>();

//loop through each of the fields on the object and create a record in your reporting obj
for (String fieldName: fieldMap.keySet()) 
{
    //this is the reporting obj
    ObjDef__c myDef = new ObjDef__c();
    myDef.Object_Name__c = type;
    myDef.Name = fieldName;
    myDef.Field_Name__c = string.valueOf(fieldMap.get(fieldName).getDescribe().getLabel());
    myDef.Data_Type__c = string.valueOf(fieldMap.get(fieldName).getDescribe().getType());
    myDef.Length__c = Integer.valueOf(fieldMap.get(fieldName).getDescribe().getLength());
    //add the rec to a list for insert
    myDefs.add(myDef);
}
//do the insert
insert myDefs;
**********************

3. Create a report on your custom object

Now, you may mess up at some point or maybe your schema has been updated.  No worries, just add a few lines to the anonymous apex to delete all of the rows in the reporting object that holds the invalid/outdated schema and re-run the script.

Wednesday, February 12, 2014

Hiding Tab Tools using JQuery

If you've logged into Salesforce a million times or more like me, you may have trained your eyes to ignore some of the subsections on the standard tabs.  For example, when you click on the Accounts tab, there is a Tools section with all kinds of goodies that you may not want your users to know they have the power to do.  I mean, does "Mass Delete Accounts" sound like a link you want someone to click on... ever?!



In most cases you can control access to these links by some profile permission or user permission.  For example, if you remove the "Modify All Data" setting on the profile, most of the links above go away. However, you may encounter a need to give someone a very broad permission like "Modify All Data" or "Transfer Records", but want to restrict their ability to see their access these tools.  One option to evaluate is using jquery to scrub out the links from the ui.  Borrowing some ideas from stackexchange, a little script like this to the home page (as an html component) has the ability to remove any link (or the entire section).  In my use case, I want to remove the ability to "Transfer Accounts" from this tab except by my power user:

******
<script type="text/javascript" src="/soap/ajax/27.0/connection.js"></script> 
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
$j = jQuery.noConflict();
$j(document).ready(function()
{  
var sid = getCookie('sid');  
var server = "https://" + window.location.host + "/services/Soap/u/27.0";  
sforce.connection.init(sid, server);
var currentUser = sforce.connection.getUserInfo();   
if(currentUser.profileId != 'SOMEPOWERUSER')  
{  
var url = location.href;
var tabUrl = "/001/o";   
if(url.indexOf(tabUrl) !== -1)  
{  
$j('.toolsContentRight a').each(function() 
{  
if ($j(this).text() == 'Transfer Accounts')
{  
$j(this).text('');  
}  
 
});   
}     
}});
</script>

*******

Mind you, that this does not remove their permission to do these things.  It just makes it less obvious by removing it from the tab.

Preventing Your Form from Submitting Twice (or more!)

If you have a visualforce page that does any kind of update or insert and you're using a custom method on your controller, be mindful of your aggressive clickers!  They'll get you in trouble by submitting your form more than once, wreaking havoc on all of your hard work.

I just dealt with this and was surprised that I was not able to use the apex:actionstatus and facet combination that I typically use to disable buttons that initiate queries.  Turns out that the important detail is that the actionstatus tag requires us to rerender a component.  When this rerendering occurs, you potentially get another form submission.  It may have to do with the redirect my page was doing after the save.  The good folks over at the stack exchange saved my bacon once again.  Their approach to disable your button is the following:

Button click calls an actionfunction js.  The actionfunction does some javascript to disable the buttons using jquery, then calls the actionfunction component, which describes the controller method to run and what to do when the method completes.

I changed up the accepted stack exchange solution in two ways:

1. The button, while gray and disabled, still behaved like a button (it was still clickable).  I ended up using jquery to remove the btn class (which makes it clickable) and also restore the btn class, after any page messages/errors are displayed:

function buttonsEnabled(enabled) {
        // to disable the button
        if (enabled === false) {
            var $b = jQuery('.btn');
            $b.removeClass('btn');
            $b.addClass('btnDisabled');
            $b.toggleClass('btnDisabled', true).attr('disabled', 'disabled');
        } else {
            var $b = jQuery('.btnDisabled');
            $b.toggleClass('btnDisabled', false).attr('disabled', null);
            $b.removeClass('btnDisabled');
            $b.addClass('btn');
            
        } 
    }

2. If you have some validations on your page, you'll want to be sure you're re-rendering the messages tag in your action function.  In this example, my pagemessages has the id "msgs".

<apex:form id="form">
<apex:actionFunction name="doSomeWorkActionFunction" 
        action="{!mySave}" 
        oncomplete="buttonsEnabled(true);"
        rerender="msgs">
</apex:actionFunction>
<apex:pagemessages escape="false" id="msgs"/>
<apex:pageblock mode="Detail" id="pageblock" >
    <apex:pageblockButtons >
        <apex:commandButton id="mysavebutton" 
                  onclick="return doSomeWork();" value="Save"  />
        <apex:commandButton action="{!myCancel}" value="Cancel" immediate="true" />
    </apex:pageblockButtons>

Wednesday, January 22, 2014

Lost Data?

I have a client who recently discovered that they had an inbound integration overwriting some important opportunity data in Salesforce.  Unfortunately, the field that was being overwritten was not a field that had tracking turned on as they had reached their limit of 20 fields.

Salesforce has put together a nice checklist of things you can do to try to recover lost or deleted data.  For my client, the suggestions weren't options to consider because the data updates were incremental over a long period of time.

However, we didn't give up, and were able to recover some of the data from workflow emails!  The attribute that they thought they had lost was part of the Won email that was sent out and because the administrator was copied on these emails, we just had to figure out a way to extract the data we wanted to recover.  Turns out if you can get your email into Outlook, Access has a way to import email messages.  Once the emails are in Access, a few little queries can fetch the data you might need.  For us, we just needed the link to the opportunity and a value from the email template.  Once the query fetched the data we wanted, a little scrubbing in Excel cleansed the rows for import.

TL;DR: you might be able to recover lost data from your workflow emails!

Tuesday, January 7, 2014

Salesforce1 list views and Flexipages

Issue: List Views are not displayed by default in Salesforce1 tabs

Part of the Salesforce1 mobile app is a design decision that makes visible in the mobile app only the list views that a user had previously run in the Salesforce.com application.  We were caught by surprise with this "feature" when we rolled out a new mobile app on Salesforce1.  Our mobile users didn't see any of the list views we had created for them in the main application, because they are primarily mobile users, and it was only through trial-and-error did we discover what was going on.  We were able to corroborate our experience when we saw this idea.

Some of the possible workarounds we explored:

Pinned List Views:
While we knew that this would be self-correcting over time, we thought about some alternatives that were mentioned in that thread.  One suggestion was to use "pinned list views", or PLVs, which I had not heard of before.  PLVs were introduced in the Winter 14 release as part of the Service Cloud console. We're not Service Cloud users so it was not a viable workaround for us.

Flexipages:
The other idea, that was suggested was to implement "Flexipages".  Flexipages were introduced in the Salesforce1 developer guide, chapter 15, as "a middle ground between page layouts and visualforce pages".  After working with these, I'm not sure I agree.  I'm not even sure I'd say that this feature isn't a beta as the procedure for developing and deploying feels half-baked.  Here are some of the key takeaways:

1. There is neither a gui section in the Setup menu for flexipages nor a way to create a flexipage using the developer console.  I did my "dev" with Notepad.  I took the example xml and changed it for my custom object in my text editor and saved it as .xml.

2. My flexipage just need two things: an "All" items list view and a "Recent" items list view.  The docs are unclear how you achieve this but basically you reference your application's list views by name.  For example, if you have a list view in Salesforce.com with a Name "All_Records", your flexipage.xml should have something like this:

<componentInstanceProperties>
<name>filterName</name>
<value>All_Records</value>

</componentInstanceProperties>

3. According to the docs, the flexipages are deployable from the force.com ide.  I upgraded my ide and did not see any reference to flexipages.  Could be an ide update issue but for the sake of time I ended up using workbench to deploy my package.  Just zip up your package and include an updated package.xml like what is described in the api documentation:

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <fullName>Travel, Inc.</fullName>
    <types>
        <members>TravelIncFlexiPage</members>
        <name>CustomTab</name>
    </types>
    <types>
        <members>TravelIncFlexiPage</members>
        <name>FlexiPage</name>
    </types>
    <types>
        <members>TravelIncQuickActions</members>
        <name>QuickAction</name>
    </types>
    <version>29.0</version>
</Package>

4. It took a few tries for me to deploy the package in workbench but when it finally went through, the last few steps to expose the page in the Salesforce1 app were straightforward.  Just add a flexipage tab and then add the tab to your mobile navigation as described in the developer guide.

5. Any updates to your flexipage require you to reload your flexipage xml.  Ugh.  Hopefully you remember where you backed it up and be sure to buy your admin a beer as they're going to hate deploying this.




Monday, December 16, 2013

Apex Test Methods - SeeAllData

One topic that I want to touch on is the SeeAllData attribute.  I thought to include it in my other post about test methods but decided to give this one it's own space.  First, let's get a definition on it:

***
Starting with Apex code saved using Salesforce.com API version 24.0 and later, test methods don’t have access by default to pre-existing data in the organization, such as standard objects, custom objects, and custom settings data, and can only access data that they create. However, objects that are used to manage your organization or metadata objects can still be accessed in your tests such as:
User
Profile
Organization
AsyncApexJob
CronTrigger
RecordType
ApexClass
ApexTrigger
ApexComponent
ApexPage
Whenever possible, you should create test data for each test. You can disable this restriction by annotating your test class or test method with the IsTest(SeeAllData=true) annotation.
Test code saved using Salesforce.com API version 23.0 or earlier continues to have access to all data in the organization and its data access is unchanged.

***

I've seen and heard many people claim that setting this attribute to true was a bad practice.  Go ahead and google it.  You'll find plenty of well-intentioned people telling you that you should never (or very rarely) set it to true and warn you like they were saving you from some kind of doom.

I say that there are plenty of reasons to use it and removing this from your tool set, puts you at a disadvantage.  Let's look at a couple examples of where I see some utility:

  • Existing org data
    • Volume
    • Quality
  • Custom settings
Setting SeeAllData to false (or accepting the current default), limits your test methods from existing org data. So for example, if you were to query for an existing account and set a field value in your test method, you'd end up with a query exception.  If you were the developer of a managed package, you'd probably be smart enough to recognize that this scenario and that your account doesn't exist in your customer's org, so you'd need to create a new account in your test methods.  In this case, SeeAllData = false, makes sense because you cannot assume that your test data exists in someone else's org.

If you were the developer of your company's internal Salesforce instance, your perspective and use cases are likely to be different. Imagine you've created a vf page that aggregates some account data.  For a page that does a lot of soql queries, you may be concerned about performance or governor limits.  If your org had millions of accounts, you could make your test methods try to simulate your account data volume, but it might be smarter to make use your existing data in your full sandbox also.  Running apex tests for both new and existing data, you'll be more confident in how your application behaves in production.  

Data quality is another place where setting SeeAllData = false may leave you vulnerable.  Let's say your org's history was something like this:
  1. In January, your users started using opportunities
  2. Later in March, they created a validation rule to make a field required on opportunities
  3. In June, you were asked to come in and add a trigger to activities to automatically update the related opportunity
If you were using SeeAllData = false, your test methods would create a new account and opportunity, create a related Task, and successfully assert that the Opportunity was updated by your new trigger.  Your test opportunity would have been created with all of the required data and your trigger would pass your unit tests.  However, the trigger would fail in production, perhaps unexpectedly, because you didn't know that there were January opportunities that had not been updated after the validation rule was introduced.  If you were using SeeAllData = true, you may have caught this data inconsistency and been prepared to handle the exception cleanly.

I think custom settings are another place where I'd look to use the SeeAllData.  In much of my apex code, I try to make the code flexible through a custom setting.  So, for example, if I am integrating via an http callout, I'd try to put the endpoint url in a custom setting.  Using SeeAllData = false, I could succesfully deploy to production without ever setting up the custom setting because my test methods create the custom setting. However, using SeeAllData = true, my test methods would fail because the custom setting had not been created in production.  This allows me to be sure that my dependency is in place before bringing users back into the system.  

So, there you have it.  SeeAllData = true can be your friend.  It's like bacon - plenty of people will tell you to avoid it, if you want to live.  But I say a little bit in your life can be delicious.
  

Deep Thoughts on Apex Test Methods

You're good enough.
You're smart enough.
You can write a good apex test method.

I just completed a major rewrite of all test methods for a client and while it was painful at times, I think it puts them in a position to extract some value from what was previously just a production deployment hurdle. Trust me, I'm not yet a full test-driven-development convert but I do believe that you can help your business automate some testing, and maybe even save some cash, if you take the time to think about your testing and apply it to your test methods.

You get better at the things you do over and over and writing good test methods is certainly something you can expect to have plenty of opportunity to practice.  There are some great resources out there to be sure you are repeating good habits. I'd start with Dan Appleman's Advanced Apex book as he has some great ideas for test class writing. Some other articles that I think are helpful and instructive are:

http://jessealtman.com/2013/09/proper-unit-test-structure-in-apex
http://wiki.developerforce.com/page/How_to_Write_Good_Unit_Tests

With this recent rewrite effort, some of the good practices I've incorporated are:

  • Moving test methods from functional classes into separate Test Classes
    • This allows you to decouple your tests from your functional classes, which excludes your tests' ability to reach private methods.  This will give you some flexibility with refactoring your code without being tied down by your test methods.
  • Asserting results
    • While you may achieve the 75% goal of code coverage, your tests will have little/no value if you are not actually checking expected versus actual results.  As a managed package developer, you'll also get flagged during the security review if you are not asserting your results. 
  • Centralizing and standardizing helper utilities
    • Like other apex you write, you should try to encapsulate where you can and use helpers to minimize the effort in testing various permutations of data against your code.
  • Testing negative scenarios
    • This is another area where you can get some value out of unit testing.  While it may take 80% of your effort to identify and code these, it's going to yield lots of value in improving your code and confidence in your code handling atypical scenarios.
  • Testing as an end user
    • Unfortunately as developers, we are system admins and almost everything we test works as expected because we don't have to deal with sharing or role hierarchy or object/field visibility. However, in the real world, our users are almost never system admins, so testing as a system admin makes no sense.

This is certainly not the complete list of best practices, but it's a good start.

As with other things in Salesforce, there is some room for improvement with the execution of unit testing in the application. In particular, I'm still frustrated by what Jeff Douglas calls the "black art".  Like Jeff, I've come across some peculiar behaviors that can be maddening.  For example, if you are testing a trigger and need to create a user and then test the dml, there's a pretty good chance you're going to get a mixed DML exception.  What is maddening, however, is that the error will not be caught in the force.com ide.  Oh, and you can deploy to production with this too!  The only thing keeping me sane was knowing that someone else noticed this too:



And aside from the nonsense of trying to estimate your code coverage in any of the tools out there, it would be nice if the test classes had the code coverage estimation like the functional classes for those of us separating them:


And don't get me started on the new test execution screens.  Aside from queuing your tests, they provide no value!

I think there is plenty to like about putting some effort into doing proper unit testing in Salesforce.  I'm sure if you build in the time to your sprints/plans, it will pay dividends in the long term.  Just be sure you approach this with a good sense of humor.


Wednesday, November 20, 2013

Traffic Nightmare @ Dreamforce

This is incredible..last year, the SF Giants were in the playoff hunt so downtown was already jamming. I can't even imagine trying to drive anywhere with over 140k registered attendees this year:

Monday, November 18, 2013

Drink the Kool Aid, It's DreamForce Season Again!

Salesforce1 appears to be the big announcement this year.  I just watched this video that Salesforce published and while the music and voice over are great, I don't understand a thing about what is being announced.  If you can make sense of this video, please share in the comments.


Wednesday, November 13, 2013

A Bug!

It's not often that I come across a real bug with Salesforce's apex or visualforce platform.  Most often there are limitations or shortcomings that you have to workaround.  Recently, I had to make an urgent change to a trigger and it's associated test class.  However, when I attempted to comment out a line in my test class, I got the following error in the editor when I tried to save:

java.lang.reflect.InvocationTargetException

When I attempted to make the same change in the developer console, I got another error:

 An unexpected error has occurred. 421011484-16071 (1420197083) for deploymentId=1drJ00000002FDxIAM If this persists, please contact customer support.


Fortunately, I was able to still deploy my code without the test class change but I opened a case anyway and after waiting a few days for a reply, was told that it was a known issue.  The instructions from developer support were:
  • Please clear Test results and try to save the code:
    • From Setup, click Develop | Apex Test Execution |View Test History | Clear test results. 

However, even before I did these actions, I tried to update the test class again and surprise!  no errors. So, something fishy is going on... Support wants to close the case but I'm inquiring for additional details.  Will keep you posted.


**Update Nov 13**

Salesforce has responded and indicated that it was a bug but has been fixed.  Details here: https://success.salesforce.com/issues_view?id=a1p30000000T17j

Friday, November 8, 2013

Workflow and User Permissions

Q: Do workflow rules run as the user or do they run as the system?  For example, if you had a sales team associate update an opportunity and there was a workflow that fired on any opportunity edit, would the workflow update a field that the user did not have profile (or permission set) permission to update?

Q: If the workflow action reassigned ownership to another user, would it execute the ownership change despite the user's system permission of Transfer Records as false?

Q: If the workflow action changed the record type to a value, would it change the record type if the user's profile did not have access to the specific record type value?

***

My initial reaction was that workflow would run as the logged in user and would obey the user's profile and permission sets.  However upon testing, what I found was that workflow runs as the system and does not honor the user's profile or permission.  So, for the 3 questions above:

  1. Workflows run as the system and would update a field that the user did not have profile/permission set access to update
  2. Workflows will execute ownership changes on behalf of users who do not have permission to directly change the ownership
  3. Workflows will change record types in spite of profile-specified record type access.



Wednesday, October 30, 2013

JQuery Tablesorter, Meet PageBlockTable

I am definitely late to the jquery party.  In the last year or so, I've been able to harness jquery to make pages more usable and functional and just finished a poc for another beautiful jquery solution for a recurring visualforce requirement: sortable tables.

A colleague and I were doing a peer review on a visualforce page he had built.  The page had a sortable pageblocktable, which he had enabled with a custom compare function he had built in his controller.  Now, I've seen all kinds of ways of doing sorting in the controller and have done some suboptimal server-side sorting, but I never really thought about trying to keep it client-side.  I figured there'd be a way with javascript but I just didn't have it in me to try to code it up.  So, I poked around the google and sure enough, there's a jquery plugin already built to do it.  And, sure enough, another Salesforce developer, shared her solution using the tablesorter plugin years ago.

So, a few years late to the party :)

Anyway, I wanted to share my variation since I was able to get the tablesorter to work w/ the standard apex component pageblocktable.  Again, with jquery, the solution is basically the following:

1. Import your library as a static resource
2. Reference your resource in your vf page
3. Bind your jquery function and your component

So, applying the parts to my poc, I have a page that looks like this:

*******

<apex:page standardController="Opportunity" tabStyle="Opportunity" extensions="myext" id="thepage">
<apex:includeScript value="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"/>
<apex:includeScript value="https://ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"/>
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/ui-lightness/jquery-ui.css" type="text/css" media="all" />
<apex:includeScript value="{!URLFOR($Resource.tablesorter, 'jquery.tablesorter.min.js')}"/>

<script type="text/javascript">
    $j = jQuery.noConflict();    
    $j(document).ready(function () {
    $j("[id$=theaddrs]").tablesorter();

    });

  //some other unrelated js

</script>

<!-- some other visualforce stuff then the heart of the proof of concept: -->

<apex:pageBlock id="theaddrsblock">

                    <apex:pageBlockTable value="{!Addrs}" var="a" id="theaddrs" styleClass="tablesorter" headerClass="header">
                    <apex:column>
                            <apex:facet name="header">
                                <apex:outputText styleClass="header" value="{!$ObjectType.Address__c.Fields.Street__c.Label}" />
                            </apex:facet>
                            <apex:outputText value="{!a.Street__c}" />

                        </apex:column>

<!-- the other columns, closing tags, and that's it -->


******

There is nothing to share about the controller because the sorting is being done w/out a callback to Salesforce.

The sections highlighted in yellow show how little is needed to modify your standard pageblocktable into a sortable table.  Not much, right?

I suggest looking at the documentation or online discussions about optional parameters that can be specified in the tablesorter library but if you just have a few fields in a table that you need to sort, the tool will do a great job of figuring out the data magically.  It's really awesome.

Now, this is only a proof of concept and so there is at least one issue resolve:  the icons to indicate sort direction are displaying on top of the column labels.  Should be able to modify the css to offset the icons.  Worst case, we just remove the styleClass attribute on the outputtext of the column facet.  Anyway, if you don't have to send it back to the controller for some logic or because the dataset is too large, just use the plugin to sort!


Thursday, October 24, 2013

Custom Button to Run Your Entry Criteria before Approval Submission

A sorely lacking feature with Salesforce approvals is the entry criteria rejection message.  If your record does not meet the entry criteria for a given approval process, you get a very generic

Unable to Submit for Approval
This record does not meet the entry criteria or initial submitters of any active approval processes. Please contact your administrator for assistance. 


with no indication of what is missing!  Users hate this.  I mean, HATE this.  They did not configure the approval process and so they've got no idea why they can't submit for an approval.  If the organization is large enough, the sales ops and/or IT help desk gets involved.  This to me is total lunacy and I see it everywhere.  Imagine the productivity you could restore if you provided your users with some meaningful information.  There is an idea you can vote up, if you agree.

There are some options to work around this limitation in the product.  One that I've been playing with is the idea of moving the entry criteria rules into one or many validation rules that only run when a field is set.  For example, you could have a Validated__c flag and create "entry criteria" validation rules to only allow it be set if your entry criteria are met. The flag could then be used by the approval process entry criteria and moves the logic back to the object where it can be surfaced.  Depending on your business, you could use workflows/triggers to uncheck the flag, should something change prior to approval submission.

It was pretty easy to move the entry criteria into validation rules but where I struggled was getting a button to set the field for me and still display the validation rule errors on the standard layout.  I was thinking that a "Validate" button would be more intuitive than setting the flag manually, so here's what I tried:

  1. Button click -> javascript to set field and save 
    • validation rule errors only captured in js but raised through an alert box
  2. Button click -> apex class to set field and save
    • validation rule errors only captured in js, again, raised in alert box
  3. Button click -> url params to set field and auto-save(?!)
    • almost there!

With options 1 and 2 the javascript is only able to surface the errors with an alert.  If your rules are simple, this is probably viable and acceptable for your users.  However, if you have 10 fields that are required for an approval process, your users are probably not going to write down each of the fields they need, then dismiss the alert, then fix the fields and submit.

With option 3, I was looking around to see if there were any non-visualforce options when I came across this article.  The idea was simple: use a custom button to invoke the edit mode for the record and prepopulate the Validate__c flag.  Additionally,  if you add Save=1 to your url, Salesforce could automatically save the record!  It only took a few minutes to configure and everything worked beautifully except the auto-save.  Apparently, Salesforce has disabled the Save parameter, so for now, our users have to click the Validate button, then Save.  Not bad.

The bottom line is that there are options to improve the usability of the entry criteria in your approval process.  You do not have to live with the generic error and you can certainly improve the productivity of your team by moving some of the logic into validation rules.



Friday, October 18, 2013

Draggable and Resizable Modal Popup

One common visualforce solution that I've built for several clients is a modal popup.  At it's core, it is nothing but a hidden outputpanel that is dynamically rendered.  With some styling, you can display the panel "above" your current page, with the current page grayed out.  There are hundreds of blog posts out there covering how to do this: here's one, another, yet another.

These blog posts are incredibly helpful and have inspired me to pass it forward and share the incremental bits that I can.  One thing that I've wanted to do that I just got working in my sandbox was to make these modal popups draggable and/or resizable.  As I've come to learn, jquery makes this ridiculously easy.

If you look at the source code for the draggable example on jquery's site, you'll see that it's 3 parts:
  1. The references to the jquery library
  2. The jquery function
  3. The div that you want to make draggable
Applying this to Salesforce, there are the same 3 parts:

1. A reference to the jquery libraries.  You should probably use static resources, but if you're just testing it out, something like this needs to be on your page:

<apex:includeScript value="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"/>
<apex:includeScript value="https://ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"/>
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/ui-lightness/jquery-ui.css" type="text/css" media="all" />

2. Add the jquery script and don't forget the noConflict() requirement.  In my example, I have a div called "pop" that I want to make draggable and resizable.

<script type="text/javascript">
    $j = jQuery.noConflict();
    $j(function() {
    $j( "[id$='pop']" ).draggable().resizable();
});
</script>

3. The div/outputpanel becomes something like this:

<apex:outputPanel id="popupBackground" styleClass="popupBackground" layout="block" rendered="{!displayPopUp}"/>
        <apex:outputPanel id="custPopup"  layout="block" rendered="{!displayPopUp}" >
        <div id="pop" Class="custPopup">
        <!-- your form/pageblock/fields/tables go here-->


Part 3 depends on your styling but it should be pretty easy to apply to your modal popup.  If all goes well you should be able to move your panel around and resize it.  Enjoy!

Tuesday, October 15, 2013

Salesforce formula with CONTAINS

Recently, I learned an interesting detail about the CONTAINS method that can be used in formula fields.  According to the inline formula help, CONTAINS is defined as follows:

CONTAINS(text, compare_text)
Checks if text contains specified characters, and returns TRUE if it does. Otherwise, returns FALSE

Let's say you needed to check for multiple values and for each, set your formula to some other value. The obvious path would be to nest your CONTAINS in a case statement, right?  Something like this, maybe:

CASE(My_Field__c
  CONTAINS(My_Field__c, 'Some Value'), 'New Value'
  CONTAINS(My_Field__c, 'Some Other Value'), 'New Value', etc...)

WRONG!  Turns out, you can't use CONTAINS with CASE, as confirmed here.  Ugh.  So, plan B, might be to use CONTAINS with a nested IF, right?  Yes, it works, but the problem you may run into is that if you are checking for many values, you may hit the maximum size for the formula field, which at the time of writing, is 3900 characters.  

So, when I was researching this, I came across this obscure knowledge article.  What caught my eye was the following:

Example 2:
a. CONTAINS("CA:NV:FL:NY",BillingState)
Will return TRUE if BillingState is CA,NV,V,L,FL:NY or any exact match of "CA:NV:FL:NY".
NOTE: when using contains with the multiple operator (:) contains then becomes equals.

The colon operator allows you to inspect many values, without the overhead of nesting IF-statements. This seems to be very well suited for checking the standard BillingState field, where values will be relatively uniform.  The key difference is the highlighted note indicating the change of function when using the operator.  In the provided example, if you had C, A, N, V, L, F, or Y, it would return true.  But, if you had California, or even CALIFORNIA, it would return false.  By contrast, if you had used a nested-if, you could have introduced some additional flexibility in finding California, CA, Cali, NoCal, SoCal, etc, sacrificing some of your character limit.  So, the takeaway for me is this: if your data is pretty uniform and structured, use the : operator, otherwise use the nested-if. 

Wednesday, October 9, 2013

Multiple Addresses - A Larger Question

One interesting aspect of Salesforce is the address concept.  Since Salesforce is a software (er, no-software) company, they don't really ship anything, right?  Everything is delivered via the cloud.  This perspective seems to have influenced how they model addresses.  Out-of-the-box, addresses are merely attributes of an account and contact in Salesforce.  So, for businesses who actually ship things, like widgets, to other businesses, how does this out-of-box model work?  Say, you are a widget maker and you have big customers, with many locations that consume your widgets.  How should you capture where you're selling your product and where it is sent?

Imagine you have a customer who's organization looks something like this:

  • Joe's Plumbing Worldwide
    • Joe's Plumbing Canada
    • Joe's Plumbing America
      • Joe's Plumbing New England
        • Joe's Plumbing Boston
        • Joe's Plumbing Hartford
      • Joe's Plumbing Chicago
      • Joe's Plumbing Los Angeles
    • Joe's Plumbing Europe
      • Joe's Plumbing France
      • Joe's Plumbing England
If you were selling widgets to Joe's Plumbing, what is important for your business to capture?  Does it only matter that you are selling to "Joe's Plumbing Worldwide"?  Do you have regional team's that support Joe's Plumbing in language?  Do your team's "own" these accounts and selling into them? When you report on sales and service, do you want to measure the selling and servicing at the regional level?  Is your customer data provided or enriched by any 3rd party services?

The account concept is central to Salesforce crm and so the decisions you make around how you model your customers, is significant.  Many appexchange products and services, like address verification, assume that you use the out-of-the-box address fields.  

There are several options to support multiple addresses and each of the options I've listed below can have some variation but the important take-away is that your approach has some implications to think through.

Option 1: Use the native Account hierarchy
Option 2: Create a custom object to hold Addresses
Option 3: Denormalize shipping addresses onto Opportunities/Orders
Option 4: Add additional shipping address fields onto your Account object

For example, if you go with option 1, do your shipping records mean anything?  Should your teams own these records?  Do you need to restrict the ability to create opportunities to just the parent account? Do you want to restrict the ability to create contacts or tasks to just the parent account?  If not, is your reporting ready for a hierarchy of data to roll up?

If you like option 2, do you need to report on shipping information?  Does your address data need to be verified or enhanced by a 3rd party and if so, does that 3rd party support your custom address object?

As is usually the case, there are many ways to solve the problem.  It's a matter of figuring out what is the best fit for your business and thinking through the implications of that decision.


Thursday, October 3, 2013

Salesforce Quotes


There is no doubt that the Salesforce Quote functionality is useful.  But could this object be any more specialized?  We've run into so many issues with customizations.  Here's a list of some issues or limitations I've recently come across:

1. You cannot override the standard Quote view with a custom visualforce page.  The option to override View is not available.
  • I suspect that because the object is so specialized (oppty sync, PDF creation, etc) that this is not likely to change.  If you need custom quote functionality, you may need to build your own from the object up.

2. The standard Discount field is not available as a merge field in Email templates.
  • As a workaround, you can create a formula field that references the out-of-box field and then use the formula field in your email template.

3. You cannot roll up list price from the line item.
  • Apparently, list price is a reference to the PricebookEntry object and so it is not a field that can be summarized.  To proceed, you need to create another currency field and populate it with a workflow on the QLI based on your business requirements.

4. You can only control edit access to Sales Price from a Profile-level parameter.
  • It's not so bad to have to use this parameter, since you can also put it in a permission set, but it's pretty inconsistent from the way the rest of the profile/page layouts behave.

5. If your record is locked as the outcome an approval process, you will not be able to save your pdf to the quote using the Create PDF button.
  • Speaking of inconsistent... on all other objects that are locked, you can almost always add an attachment.  To get around this, I changed our approvals to unlock the record, then used validation rules to keep the QLI from being changed.