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!