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:
Create another object / custom setting with fields
- Master Value
- Dependent Field API Name
- Dependant Field Value
Populate as appropriate
- 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:
- 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:
- 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
- I'd probably alter/overload to accept
- Create a
Field Set
to track master picklists you care about. - 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]);
}
}