9

I have queries pulling all of the information correctly but what I can't seem to find was a way to add new options to a picklist using APEX.

I currently have

Package__c.Products__c.add(new Schema.PicklistEntry(products[j].Name));

Package__c is my custom object, and Products__c is the picklist. products[j] is pulling a list of products pulled from our products page.

And I've tried adding it as a select option, or even casting the variable type.

And this information has to go to the actual picklist within a custom object, and not within a visual force page or anything like that.

4
  • 2
    That is an administrative capability that I would strongly recommend against exposing to your end users. Can you go into more detail about your use case?
    – Adrian Larson
    Commented Feb 9, 2015 at 20:44
  • Well currently I need a dynamic picklist that pulls in the entire listing of our products to a custom object to build packages with deal based incentives. That way we can select a few products and save it to a package, and then check these packages against the products in an opportunity. Commented Feb 10, 2015 at 15:01
  • Are you sure you need a picklist? Why not use a lookup to Product then?
    – Adrian Larson
    Commented Feb 10, 2015 at 15:05
  • Are there multi-lookups? The person using this needs to be able to select multiple items. Commented Feb 10, 2015 at 15:35

4 Answers 4

10

The way to do this is using the Apex MetaData API wrapper as provided by esteemed SFSE colleague Andrew Fawcett here

  1. Running user needs to have Customize Application privilege so you really want this to be an admin type user
  2. Read the examples in the GIT package carefully - there is one for picklists. You need to read the CustomField from the MetaData API first then update, otherwise you'll smash all the existing picklist entries.

The relevant example in the package's MetadataServiceExamples.cls file is here:

public static void updatePicklist()
{
    MetadataService.MetadataPort service = createService();             

    // Read Custom Field
    MetadataService.CustomField customField = 
        (MetadataService.CustomField) service.readMetadata('CustomField', 
            new String[] { 'Lead.picklist__c' }).getRecords()[0];       

    // Add pick list values
    metadataservice.PicklistValue two = new metadataservice.PicklistValue();
    two.fullName= 'second';
    two.default_x=false;
    metadataservice.PicklistValue three = new metadataservice.PicklistValue();
    three.fullName= 'third';
    three.default_x=false;
    customField.picklist.picklistValues.add(two);
    customField.picklist.picklistValues.add(three);     

    // Update Custom Field
    handleSaveResults(
        service.updateMetadata(
            new MetadataService.Metadata[] { customField })[0]);        
}
5
  • 1
    This is very useful but the associated knowledge needed is outside of my scope. Thank you though! Commented Feb 10, 2015 at 17:43
  • Okay so I looked into it deeper and decided to have a go at it. The example shows creating a new picklist and updating the created picklist, but what about updating an existing picklist? Commented Feb 12, 2015 at 16:23
  • @CodyBeaner - I added the code sample from the examples class
    – cropredy
    Commented Feb 12, 2015 at 17:04
  • I've used the code to completely make a new field and it doesn't even show. Is there a comprehensive tutorial for this? Commented Feb 13, 2015 at 15:51
  • @CodyBeaner - Hmm -- the Apex wrapper to the metadata API is basically a wrapper on how a REST or SOAP client would use the API. As such, the SFDC doc developer.salesforce.com/docs/atlas.en-us.api_meta.meta/… was the most helpful to me along with the aformentioned examples class. The doc explains the datastructure
    – cropredy
    Commented Feb 13, 2015 at 20:08
4

@cropredy answer is correct. But there are some changes in MetadataService class. So code given in above answer will not work. Below code will work with latest version of mdapi GitHub repo.

MetadataService.MetadataPort service = createService(); 

MetadataService.CustomField picklistField =(List<MetadataService.CustomField>) service.readMetadata('CustomField', 'Lead.picklist__c').getRecords();
        
metadataservice.CustomValue two = new metadataservice.CustomValue();
two.fullName= 'second';
two.default_x=false;  

picklistField.valueSet.valueSetDefinition.value.add(two);

MetadataService.SaveResult result = service.updateMetadata( new MetadataService.Metadata[] { picklistField })[0];

In place of PicklistValue we have to use ValueSet. Also we need to make little change in MetadataService Class. We have to replace ValueSet virtual class in Metadatservice with

public class ValueSet {
        public String controllingField;
        public Boolean restricted;
        public MetadataService.ValueSetValuesDefinition valueSetDefinition;
        public String valueSetName;
        public MetadataService.ValueSettings[] valueSettings;
        private String[] controllingField_type_info = new String[]{'controllingField',SOAP_M_URI,null,'0','1','false'};
        private String[] restricted_type_info = new String[]{'restricted',SOAP_M_URI,null,'0','1','false'};
        private String[] valueSetDefinition_type_info = new String[]{'valueSetDefinition',SOAP_M_URI,null,'0','1','false'};
        private String[] valueSetName_type_info = new String[]{'valueSetName',SOAP_M_URI,null,'0','1','false'};
        private String[] valueSettings_type_info = new String[]{'valueSettings',SOAP_M_URI,null,'0','-1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'controllingField','restricted','valueSetDefinition','valueSetName','valueSettings'};
    }

Also Check URL in your metadataPort.endpoint_x. It should be

public String endpoint_x = URL.getSalesforceBaseUrl().toExternalForm() + '/services/Soap/m/42.0';
1
  • I couldn't get this to work. readMetadata expected (String,, String[]) not (String,String), the CustomField.valueSet doesnt' exist even though I see it in the code and updateMetadata expects MetadataService.Metadata and this code has the list of the CustomField Commented Oct 16, 2022 at 0:08
2

The above given solutions works perfectly using Apex Metadata Service (https://andyinthecloud.com/2013/10/27/introduction-to-calling-the-metadata-api-from-apex/); but it requires to import all code for even a small requirement. After, release of Tooling API, I find it lifesaver for such implementation without importing and managing too much code. We just need to make a callout using Tooling API.

Full detail article is here: https://sfdcian.com/update-picklist-value-and-add-it-in-record-type-using-apex-salesforce/

PATCH
/services/data/v41.0/tooling/sobjects/CustomField
Body: <as given at below URL>

Here is JSON Body that has to be sent as request body in this callout: https://github.com/ayub-ansari/Create-Picklist-Field-Using-Apex/blob/master/FirstHTTPRequestToUpdatePicklistField

0

Based on your comments, what you really want is a Junction object to Create a Many-To-Many Relationship between Product and your Package Deal object. I might call it something like Package Deal Line Item.

5
  • This seems like it might be exactly what I need, thanks! Commented Feb 10, 2015 at 16:26
  • Haha it was tough to figure out what you really needed. Gotta ask the right questions! :)
    – Adrian Larson
    Commented Feb 10, 2015 at 16:28
  • So for some reason I cannot add a Master relationship to my products? Ideas? Commented Feb 10, 2015 at 17:31
  • You can't make Product master? Well, a junction can still enable many-to-many with a lookup instead. The pairing is not as rigid, but I think it will suit your needs.
    – Adrian Larson
    Commented Feb 10, 2015 at 17:32
  • This does't really seem to be what I'm looking for... Is there a way to put the product layout in my custom object the same way it's in opportunity? Commented Feb 10, 2015 at 17:42

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .