Update Dependent Picklist when created by API

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


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]);
    }
}