4

We have a process where products are created in another system and then are pushed into Salesforce via the API. One of the fields that is coming over is a picklist that several other picklist fields are dependent on. The dependent fields are currently not being populated because the fields are not in the other system. When I go into the UI I can edit the record and the dependent values populate automatically and I can save the record and move on.

I want to be able to have the dependent fields be populated based on the picklist field that is coming over without going in through the UI and editing the record.

I know I could have the other system push into those picklist fields with a value. However, I don't want to do that because then I have to manage the dependencies in both systems. It's 100+ values in each picklist so it becomes cumbersome to do it in both places. The same if I were to use a workflow field update, that's another place to maintain the dependency.

Is there a easy way to update a record after it's inserted to mimic how the value is auto selected when you create/edit the fields in the UI without hardcoding the picklist values into the logic?

EDIT:

To highlight what I'm talking about in the dependent picklist setup you choose the values that you want to show up. In our case it's one to one in other cases you can choose more than one possible value.

enter image description here

When you are inserting records through the API and only populate the first value it does not automatically update the dependent values.

enter image description here

However, if you were in the record in the UI after the record is created you could click in and it populates the dependent value because there is only one choice.

enter image description here

  • Usually the dependency doesn't offer one clean option, but rather a narrower range of options. Don't you still have some choices to make in the UI? Maybe screenshots would be useful here. – Adrian Larson Sep 15 '16 at 14:51
  • I want to avoid going into the UI each time to update and have some sort of logic update the fields accordingly. – Dan Wooding Sep 15 '16 at 14:56
  • But aren't there still multiple options available? You still have to manually choose one, no? – Adrian Larson Sep 15 '16 at 14:57
  • Eh you can do it via Apex. Get lists of dependent picklist options in Apex – Adrian Larson Sep 15 '16 at 14:59
  • Still working on my 2¢...not sure if it will make sense to add. – Adrian Larson Sep 15 '16 at 18:23
6
+100

So, leveraging the tooling API would be a lot of work.

Simplest way:

Per Adrian this may be another easy way but you would have to refactor the JS to apex:

Get lists of dependent picklist options in Apex

Apex Version (Code below is from link, I take no credit):

http://titancronus.com/blog/2014/05/01/salesforce-acquiring-dependent-picklists-in-apex/

public class Bitset{
        public Map<String,Integer> AlphaNumCharCodes {get;set;}
        public Map<String, Integer> Base64CharCodes { get; set; }
        public Bitset(){
            LoadCharCodes();
        }
        //Method loads the char codes
        private void LoadCharCodes(){
            AlphaNumCharCodes = new Map<String,Integer>{
                'A'=>65,'B'=>66,'C'=>67,'D'=>68,'E'=>69,'F'=>70,'G'=>71,'H'=>72,'I'=>73,'J'=>74,
                'K'=>75,'L'=>76,'M'=>77,'N'=>78,'O'=>79,'P'=>80,'Q'=>81,'R'=>82,'S'=>83,'T'=>84,
                'U'=>85,'V'=> 86,'W'=>87,'X'=>88,'Y'=>89,'Z'=>90    
            };
            Base64CharCodes = new Map<String, Integer>();
            //lower case
            Set<String> pUpperCase = AlphaNumCharCodes.keySet();
            for(String pKey : pUpperCase){
                //the difference between upper case and lower case is 32
                AlphaNumCharCodes.put(pKey.toLowerCase(),AlphaNumCharCodes.get(pKey)+32);
                //Base 64 alpha starts from 0 (The ascii charcodes started from 65)
                Base64CharCodes.put(pKey,AlphaNumCharCodes.get(pKey) - 65);
                Base64CharCodes.put(pKey.toLowerCase(),AlphaNumCharCodes.get(pKey) - (65) + 26);
            }
            //numerics
            for (Integer i=0; i<=9; i++){
                AlphaNumCharCodes.put(string.valueOf(i),i+48);
                //base 64 numeric starts from 52
                Base64CharCodes.put(string.valueOf(i), i + 52);
            }
        }
        public Boolean testBit(String pValidFor,Integer n){
            //the list of bytes
            List<Integer> pBytes = new List<Integer>();
            //multiply by 6 since base 64 uses 6 bits
            Integer bytesBeingUsed = (pValidFor.length() * 6)/8;
            //will be used to hold the full decimal value
            Integer pFullValue = 0;
            //must be more than 1 byte
            if (bytesBeingUsed <= 1)
                return false;
            //calculate the target bit for comparison
            Integer bit = 7 - (Math.mod(n,8)); 
            //calculate the octet that has in the target bit
            Integer targetOctet = (bytesBeingUsed - 1) - (n >> bytesBeingUsed); 
            //the number of bits to shift by until we find the bit to compare for true or false
            Integer shiftBits = (targetOctet * 8) + bit;
            //get the base64bytes
            for(Integer i=0;i<pValidFor.length();i++){
                //get current character value
                pBytes.Add((Base64CharCodes.get((pValidFor.Substring(i, i+1)))));
            }
            //calculate the full decimal value
            for (Integer i = 0; i < pBytes.size(); i++)
            {
                Integer pShiftAmount = (pBytes.size()-(i+1))*6;//used to shift by a factor 6 bits to get the value
                pFullValue = pFullValue + (pBytes[i] << (pShiftAmount));
            }
            //& is to set the same set of bits for testing
            //shift to the bit which will dictate true or false
            Integer tBitVal = ((Integer)(Math.Pow(2, shiftBits)) & pFullValue) >> shiftBits;
            return  tBitVal == 1;
        }
    }




/*
     * @Summary: Entity to represent a json version of a picklist entry
     * so that the validFor property becomes exposed
    */
    public class TPicklistEntry{
        public string active {get;set;}
        public string defaultValue {get;set;}
        public string label {get;set;}
        public string value {get;set;}
        public string validFor {get;set;}
        public TPicklistEntry(){

        }
    }

Code to use above utility to get the dependant values

public static Map<String,List<String>> GetDependentOptions(String pObjName, String pControllingFieldName, String pDependentFieldName){
        Map<String,List<String>> objResults = new Map<String,List<String>>();
        //get the string to sobject global map
        Map<String,Schema.SObjectType> objGlobalMap = Schema.getGlobalDescribe();
        if (!objGlobalMap.containsKey(pObjName))
            return objResults;
        //get the type being dealt with
        Schema.SObjectType pType = objGlobalMap.get(pObjName);
        Map<String, Schema.SObjectField> objFieldMap = pType.getDescribe().fields.getMap();
        //verify field names
        if (!objFieldMap.containsKey(pControllingFieldName) || !objFieldMap.containsKey(pDependentFieldName))
            return objResults;     
        //get the control values   
        List<Schema.PicklistEntry> ctrl_ple = objFieldMap.get(pControllingFieldName).getDescribe().getPicklistValues();
        //get the dependent values
        List<Schema.PicklistEntry> dep_ple = objFieldMap.get(pDependentFieldName).getDescribe().getPicklistValues();
        //iterate through the values and get the ones valid for the controlling field name
        TStringUtils.Bitset objBitSet = new TStringUtils.Bitset();
        //set up the results
        for(Integer pControllingIndex=0; pControllingIndex<ctrl_ple.size(); pControllingIndex++){           
            //get the pointer to the entry
            Schema.PicklistEntry ctrl_entry = ctrl_ple[pControllingIndex];
            //get the label
            String pControllingLabel = ctrl_entry.getLabel();
            //create the entry with the label
            objResults.put(pControllingLabel,new List<String>());
        }
        //cater for null and empty
         objResults.put('',new List<String>());
         objResults.put(null,new List<String>());
        //check the dependent values
        for(Integer pDependentIndex=0; pDependentIndex<dep_ple.size(); pDependentIndex++){          
            //get the pointer to the dependent index
            Schema.PicklistEntry dep_entry = dep_ple[pDependentIndex];
            //get the valid for
            String pEntryStructure = JSON.serialize(dep_entry);                
            TStringUtils.TPicklistEntry objDepPLE = (TStringUtils.TPicklistEntry)JSON.deserialize(pEntryStructure, TStringUtils.TPicklistEntry.class);
            //if valid for is empty, skip
            if (objDepPLE.validFor==null || objDepPLE.validFor==''){
                continue;
            }
            //iterate through the controlling values
            for(Integer pControllingIndex=0; pControllingIndex<ctrl_ple.size(); pControllingIndex++){    
                if (objBitSet.testBit(objDepPLE.validFor,pControllingIndex)){                   
                    //get the label
                    String pControllingLabel = ctrl_ple[pControllingIndex].getLabel();
                    objResults.get(pControllingLabel).add(objDepPLE.label);
                }
            }
        } 
        return objResults;
    }

Second simple way:

  1. Create another object / custom setting with fields

    • Master Value
    • Dependent Field API Name
    • Dependant Field Value
  2. Populate as appropriate

  3. Create a trigger (or Process builder) to use this new object to find and set the values accordingly

Pros: Quick to implement

Cons: Maintenance - Have to remember to update this table when pick list values change. - NOT a best practice but given for those with minimal coding skills

Harder way:

  1. Use the tooling API and the describeLayout method to determine the pick list values based off of the master value.

Pros: None really given the above methods

Cons: Slow to implement and a lot of code required to start up. but there are examples you could possibly leverage

  • 1
    No need to port, just read further down in the answers. Salesforce: Acquiring Dependent Picklists in Apex – Adrian Larson Sep 15 '16 at 15:04
  • This is very detailed, thank you. Is it wrong to be leaning towards second way, I think of additional use cases where I can have a list of the values that business can analyze better than the UI. Plus approvals where they can create a new combination get it approved by a stakeholder and then the admin would make the change in the dependency? On top of the fact that it'll be quickish to deploy it feels like it's a better solution for my needs. – Dan Wooding Sep 15 '16 at 15:33
  • @DanWooding - Technically yes the second way could be considered "wrong". It is not scalable nor is it meant for an enterprise level application. But to just get it done and over with in a small org with a few people....Thats up to you. Time, cost, future failure, etc all need to be considered and only you can make that decision – Eric Sep 15 '16 at 15:39
  • Additionally, I am not a fan of dependent picklists. Many times they beg for the need for a child object to hold details of what you are doing with the pciklists. Especially if you want to capture additional information and do reporting on those values – Eric Sep 15 '16 at 15:40
  • Yeah, especially with the lack of API access with the dependency it makes it harder than it needs to be to manage. – Dan Wooding Sep 15 '16 at 16:00
3

I would suggest these steps:

  1. Create classes as described by Neel here: Salesforce: Acquiring Dependent Picklists in Apex
    • I'd probably alter/overload to accept Schema.SObjectField values
    • Also a good idea to incorporate the optimizations made by the author
  2. Create a Field Set to track master picklists you care about.
  3. For each master picklist field in your Field Set, look at each dependent picklist. If it contains only one option, set it.

Something like:

public static Map<SObjectField, Set<SObjectField>> getDependencies()
{
    Map<SObjectField, Set<SObjectField>> dependencies =
        new Map<SObjectField, Set<SObjectField>>();
    Map<String, SObjectField> fields = SObjectType.MyObject__c.fields.getMap();

    Fieldset masterPicklists = SObjectType.MyObject__c.Fieldsets.MasterPicklists;
    for (FieldSetMember member : masterPicklists.getFields)
        dependencies.put(fields.get(member.fieldPath()), new Set<SObjectField>());

    for (SObjectField field : fields.values())
    {
        SObjectField master = field.getDescribe().getController();
        if (dependencies.containsKey(master))
            dependencies.get(master).add(field);
    }
    return dependencies;
}
public static void fillInDependencies(List<MyObject__c> records)
{
    Map<SObjectField, Set<SObjectField>> dependencies = getDependencies();
    for (SObjectField master : dependencies.keySet())
        for (SObjectField dependent : dependencies.get(master))
            fillInDependencies(master, dependent, records);
}
static void fillInDependencies
    (SObjectField master, SObjectField dependent, List<MyObject__c> records)
{
    Map<String, List<String>> options = DependentPicklist.getOptions(master, dependent);
    for (SObject record : records)
    {
        List<String> dependentOptions = options.get((String)record.get(master));
        if (dependentOptions != null && dependentOptions.size() == 1)
            record.put(dependent, dependentOptions[0]);
    }
}
1

Base 64 chars includes the characters + and /, you need to include them om loadCharCodes function.

//Method loads the char codes
        private void loadCharCodes(){
            AlphaNumCharCodes = new Map<String,Integer>{
                'A'=>65,'B'=>66,'C'=>67,'D'=>68,'E'=>69,'F'=>70,'G'=>71,'H'=>72,'I'=>73,'J'=>74,
                'K'=>75,'L'=>76,'M'=>77,'N'=>78,'O'=>79,'P'=>80,'Q'=>81,'R'=>82,'S'=>83,'T'=>84,
                'U'=>85,'V'=> 86,'W'=>87,'X'=>88,'Y'=>89,'Z'=>90    
            };
            Base64CharCodes = new Map<String, Integer>();
            //lower case
            Set<String> pUpperCase = AlphaNumCharCodes.keySet();
            for(String pKey : pUpperCase){
                //the difference between upper case and lower case is 32
                AlphaNumCharCodes.put(pKey.toLowerCase(),AlphaNumCharCodes.get(pKey)+32);
                //Base 64 alpha starts from 0 (The ascii charcodes started from 65)
                Base64CharCodes.put(pKey,AlphaNumCharCodes.get(pKey) - 65);
                Base64CharCodes.put(pKey.toLowerCase(),AlphaNumCharCodes.get(pKey) - (65) + 26);
            }
            //numerics
            for (Integer i=0; i<=9; i++){
                AlphaNumCharCodes.put(string.valueOf(i),i+48);
                //base 64 numeric starts from 52
                Base64CharCodes.put(string.valueOf(i), i + 52);
            }
            //Symbols
            Base64CharCodes.put('+', 62);
            Base64CharCodes.put('/', 63);
        }

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.