6

It would seem that it is not possible to support picklists enabled and picklists not enabled from the same Apex Code if we are writing to the State or Country. Consider the following:

    oAccount[0].BillingStreet = '1060 W Addison St';
    oAccount[0].BillingCity = 'Chicago';
    oAccount[0].BillingPostalCode = '60613-4566';

    oAccount[0].BillingState = 'IL';
    oAccount[0].BillingCountry = 'US';

    update oAccount;

This code works perfectly if picklists are not enabled. When picklists are enabled, the fields BillingState and BillingCountry become read only and two new fields are added BillingStateCode and BillingCountryCode which are meant to contain the ISO 2 char code.

Because the State and Country fields are changed to read only, the above code fails if picklists are enabled with a runtime exception on the update. In order to support picklists, we can make the following modificaiton:

    oAccount[0].BillingStreet = '1060 W Addison St';
    oAccount[0].BillingCity = 'Chicago';
    oAccount[0].BillingPostalCode = '60613-4566';

    oAccount[0].BillingStateCode = 'IL';
    oAccount[0].BillingCountryCode = 'US';

    update oAccount;

This code works fine on orgs with picklists enabled, but will not compile or run on orgs where they are not, because the fields BillingStateCode and BillingCountryCode do not exist.

The problem is how to support both from the same code? It would appear that it cannot be done. We can certainly detect whether or not picklists are enabled as follows:

private boolean PickListsEnabled()
{
     // if Account Billing Address is enabled, assume all addresses are enabled
     Map<String, Schema.SObjectField> M = Schema.SObjectType.Account.fields.getMap();
     return M.containsKey('BillingStateCode');
}

and then try something like:

    oAccount[0].BillingStreet = '1060 W Addison St';
    oAccount[0].BillingCity = 'Chicago';
    oAccount[0].BillingPostalCode = '60613-4566';

    if(PickListsEnabled())
    {
        oAccount[0].BillingStateCode = 'IL';
        oAccount[0].BillingCountryCode = 'US';
    }
    else
    {
        oAccount[0].BillingState = 'IL';
        oAccount[0].BillingCountry = 'US';
    }

    update oAccount;

But on orgs that do not have picklists enabled, this still will not compile or run due to the mention of the BillingStateCode and BillingCountryCode fields - even if they will not be accessed in the case that picklists are enabled. If we precompile our managed package on an org with picklists enabled, it will not install on orgs where they are not enabled.

So is there no way to support both from the same codebase? Will we be forced to maintain 2 different versions of our managed package? Any suggestions are appreciated.

Suggest SalesForce consider implementing this in just the opposite way. Leave the BillingCountry and BillingState fields as they are, and the new fields are called BillingCountryFull and BillingStateFull and are read only. In this scenario, for example, United States would be the value of BillingCountryFull when BillingCountry has the value "US" written to it.

4

Thanks for the post Jeremy. It put us on the track to do resolve our problem. We still maintain that SF would better serve its end users by implementing as we suggested in the initial post, but it is what it is . Posting here for the benefit of others who may have run into this kind of problem.

The only problem with get/put is that in order to use get, you must have queried the original field before calling it, else you will get a runtime SOQL error: "SObject row was retrieved via SOQL without querying the requested field: "

Curiously, the put will work regardless.

The way around this is to use Dynamic SOQL Query, something like:

public class ChangeAccountBillingAddress
{
public List<Account> oAccount; 
public Id apCurPage {get; set;}
private String stateFieldName = 'BillingState';
private String countryFieldName = 'BillingCountry';

public ChangeAccountBillingAddress(ApexPages.StandardController controller) 
{
    apCurPage = ApexPages.currentPage().getParameters().get('id');

    if(PickListsEnabled())
    {
        stateFieldName = 'BillingStateCode';
        countryFieldName = 'BillingCountryCode';
    }
    String acctQuery = String.escapeSingleQuotes('select Account.BillingStreet, Account.BillingCity, Account.BillingPostalCode,' +
       stateFieldName + ',' + countryFieldName + ' from Account where Id =:apCurPage');

    oAccount = Database.query(acctQuery);
}


public PageReference ChangeBillingAddress()
{
    String stateVal = oAccount[0].get(stateFieldName);
    if(stateVal != 'IL')
    {
         oAccount[0].BillingStreet = '1060 W Addison St';
         oAccount[0].BillingCity = 'Chicago';
         oAccount[0].put(stateFieldName,'IL');
         oAccount[0].BillingPostalCode = '60613-4566';
         oAccount[0].put(countryFieldName,'US');
         update oAccount;
    }

    PageReference pageRef = new PageReference('/' + apCurPage);         

    pageRef.setRedirect(true);         
    return pageRef;               
}

private boolean PickListsEnabled()
{
     // if Account Billing Address is enabled, assume all addresses are enabled
     //return Account.BillingCountry.getDescribe().getType() == Schema.DisplayType.Picklist;
     Map<String, Schema.SObjectField> M = Schema.SObjectType.Account.fields.getMap();
     return M.containsKey('BillingStateCode');
}

}

The above code will compile and more importantly run correctly on orgs with picklistst enabled or picklists not enabled.

3

There may be a better way to do this, but the simplest change I would make to the code you posted is this. It uses get/put format for populating these fields, and so should not be subject to compile or run-time errors (as long as these lines are never run).

if(PickListsEnabled())
{
    oAccount[0].put('BillingStateCode', 'IL');
    oAccount[0].put('BillingCountryCode', 'US');
}
3

When picklists are enabled, the fields BillingState and BillingCountry become read only.

No they don't. You can still write to them, as long as you use valid integration values as defined by the org. (That's why they're called "integration values" - they're intended to be used by integrations like Apex.)

You can get the integration values from Address.Settings via MD API. (I'm not sure there's a way to get the list in Apex, other than doing something hacky like creating a record for each unique BillingCountryCode, saving, and recording the value of the BillingCountry field.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.