How to update picklist value using metadata api/apex? - Answers - Salesforce Trailblazer Community
Trailblazer Community
Ask Search:
Jane GJane G 

How to update picklist value using metadata api/apex?

I have a picklist field - Status__c. Its values are 'Open', 'Closed', 'In progress'. I want to update the values using metadata api. I'm using the metadataServices.cls but can't update the picklist value. Having error.

For e,g update 'Closed' to Close.

Can someone please help. Thank you.

 
Best Answer chosen by Jane G
Om PrakashOm Prakash
Hi Jane,
Please try this code I have used valueSet to update the picklist value.
(Modify your API name and old/new value and give it a test from the developer console for one value "Closed".)
String picklistapiname = 'Lead.Status__c';
MetadataService.MetadataPort service = new MetadataService.MetadataPort();   
service.SessionHeader = new MetadataService.SessionHeader_element();
service.SessionHeader.sessionId = UserInfo.getSessionId();

MetadataService.CustomField customField = (MetadataService.CustomField) service.readMetadata('CustomField', new String[] { picklistapiname}).getRecords()[0];

// For each on: customField > Get valueset > Get valueSetDefinition > get values
for(MetadataService.CustomValue objCustomValue : customField.valueSet.valueSetDefinition.value){
     if(objCustomValue.fullName == 'Closed'){ // API Name
          objCustomValue.fullName = 'Close'; // New API Value
          objCustomValue.label = 'Close'; // New Label value
     }
}
// Update 
List<MetadataService.SaveResult> lstResults = service.updateMetadata( new MetadataService.Metadata[] { customField });

for (MetadataService.SaveResult objResult : lstResults) {
    if (objResult.success) {
        System.debug('Successfully updated');
    }
    else {                       
        if(objResult.errors.size() > 0){
          System.debug('eror : ' + objResult.errors[0].message);
        }
    }
}

Let's discuss if you any query!

All Answers

Jane GJane G
Note: I have been able to add a value to a picklist but unable to update an existing picklist value.
Tommaso BolisTommaso Bolis
Which error message are you getting?

May I request you to please check the below link for similar kind of requirement from the forum community.
https://developer.salesforce.com/forums/?id=906F00000008vubIAA
https://developer.salesforce.com/forums/?id=906F0000000kKcvIAE
https://developer.salesforce.com/forums/?id=906F000000092wDIAQ
I hope it will be helpful.
Meghna VijayMeghna Vijay
Hi Jane,

You can refer to this link for further help.
https://developer.salesforce.com/forums/?id=9060G000000IBJDQA4

Thanks
Om PrakashOm Prakash
Hi Jane,
Please have a look at this discussion.
https://developer.salesforce.com/forums/?id=9060G000000XbVHQA0

If still not found the resolution please sare some more details what is an exact issue while updating.
Jane GJane G
i have tried this:

MetadataService.MetadataPort service = new MetadataService.MetadataPort();
service.SessionHeader = new MetadataService.SessionHeader_element();
service.SessionHeader.sessionId = UserInfo.getSessionId();

//update a picklist value
MetadataService.CustomField customField = new MetadataService.CustomField();
customField.fullName = picklistapiname;
customField.label ='test123';
customField.type_x = 'Picklist';

MetadataService.Picklist pt = new MetadataService.Picklist();
pt.sorted= true;
pt.picklistValues = new List<MetadataService.PicklistValue>();
MetadataService.PicklistValue pv = new MetadataService.PicklistValue();
pv.fullName = picklistapiname;
pv.default_x = false;
pt.picklistValues.add(pv);
customField.picklist = pt; //getting error here as it is not found in the MetadataService class
MetadataService.UpdateMetadata ut = new MetadataService.UpdateMetadata(); //getting invalid type 
ut.currentName= picklistapiname;
ut.metadata= customField;
service.updateMetadata(new List<MetadataService.Metadata>{ ut});
Om PrakashOm Prakash
Have you tried this:
(Without creating instance of MetadataService.UpdateMetadata)
List<MetadataService.SaveResult> results = service.updateMetadata( new MetadataService.Metadata[] { customField });

 
Jane GJane G
i have tried :
MetadataService.MetadataPort service = new MetadataService.MetadataPort();   
        service.SessionHeader = new MetadataService.SessionHeader_element();
        service.SessionHeader.sessionId = UserInfo.getSessionId();
        
        //update a picklist value
        MetadataService.CustomField customField = new MetadataService.CustomField();
        customField.fullName = picklistapiname;
        customField.label ='test123';
        customField.type_x = 'Picklist';

        MetadataService.Picklist pt = new MetadataService.Picklist();
        pt.sorted= true;
        pt.picklistValues = new List<MetadataService.PicklistValue>();

        MetadataService.PicklistValue pv = new MetadataService.PicklistValue();
        pv.fullName = picklistapiname;
        pv.default_x = false;
        pt.picklistValues.add(pv);
        customField.picklist = pt;

        List<MetadataService.SaveResult> results = service.updateMetadata( new MetadataService.Metadata[] { customField });

i have added picklist in custom field. I've run this code and not getting the errors now but my picklist value is not being updated. 

I've read somewhere that - "Picklist in CustomField has been deprecated use valueSet instead". But i cannot figure out how to use valueSet.

Thank you.
Om PrakashOm Prakash
Thanks for the info Jane.
Let me try it out.
If succeed I would update it asap today.
 
Om PrakashOm Prakash
Hi Jane,
Please try this code I have used valueSet to update the picklist value.
(Modify your API name and old/new value and give it a test from the developer console for one value "Closed".)
String picklistapiname = 'Lead.Status__c';
MetadataService.MetadataPort service = new MetadataService.MetadataPort();   
service.SessionHeader = new MetadataService.SessionHeader_element();
service.SessionHeader.sessionId = UserInfo.getSessionId();

MetadataService.CustomField customField = (MetadataService.CustomField) service.readMetadata('CustomField', new String[] { picklistapiname}).getRecords()[0];

// For each on: customField > Get valueset > Get valueSetDefinition > get values
for(MetadataService.CustomValue objCustomValue : customField.valueSet.valueSetDefinition.value){
     if(objCustomValue.fullName == 'Closed'){ // API Name
          objCustomValue.fullName = 'Close'; // New API Value
          objCustomValue.label = 'Close'; // New Label value
     }
}
// Update 
List<MetadataService.SaveResult> lstResults = service.updateMetadata( new MetadataService.Metadata[] { customField });

for (MetadataService.SaveResult objResult : lstResults) {
    if (objResult.success) {
        System.debug('Successfully updated');
    }
    else {                       
        if(objResult.errors.size() > 0){
          System.debug('eror : ' + objResult.errors[0].message);
        }
    }
}

Let's discuss if you any query!
This was selected as the best answer
Jane GJane G
Hello Om Prakash,

Thank you so much. I am able to update the picklist value.
Om PrakashOm Prakash
Awesome!
Thanks for confirming.
 
Jane GJane G
@Om Prakash

Hello,

I have doing it on a dependent picklist. I've updated the value and setting its controlling field value. But it is not being updated on salesforce and i'm not sure if am using the correct classes.
 
MetadataService.MetadataPort service = new MetadataService.MetadataPort();   
service.SessionHeader = new MetadataService.SessionHeader_element();
service.SessionHeader.sessionId = UserInfo.getSessionId();

        //Pull down current config of dependent picklist field
        MetadataService.CustomField reportCustomField = (MetadataService.CustomField) service.readMetadata('CustomField', new string[] { 'Account.State__c' }).getRecords()[0];

        System.debug('## controllingfield: ' + reportCustomField.ValueSet.controllingField);
        System.debug('## controllingfield. VALUES: ' + reportCustomField.ValueSet.valueSetDefinition.value);
        System.debug('## VALUESettings: ' + reportCustomField.ValueSet.ValueSettings);

String picklstValApiname = 'test';

        //update the dependent picklist value
        for(MetadataService.CustomValue objCustomValue : reportCustomField.valueSet.valueSetDefinition.value){
            if(objCustomValue.fullName == picklstValApiname ){ // API Name
                objCustomValue.fullName = 'paris'; // New API Value
                objCustomValue.label = 'paris'; // New Label value
            }
        }

        //update the controllingFieldValue
        for(MetadataService.ValueSettings valuesetting :  reportCustomField.ValueSet.ValueSettings){
            if(valuesetting.valueName == picklstValApiname){
                System.debug('##controlling: '+valuesetting.controllingFieldValue);
                valuesetting.controllingFieldValue = new List<String>{'France'};
            }
        }
        
        for(MetadataService.ValueSettings valuesetting :  reportCustomField.ValueSet.ValueSettings){
            System.debug('## pos: # ' + valuesetting.controllingFieldValue + ' ## ' + valuesetting.valueName);
//here it is being set
        }

        List<MetadataService.SaveResult> lstResults = service.updateMetadata( new MetadataService.Metadata[] { reportCustomField });
I think the controlling field is not being set to the customField. I can't figure out which variable to use. And what am i doing wrong.

Thank you in advance.
Om PrakashOm Prakash

Hello Jane,
A minor small addition in the code of //update the controllingFieldValue.

valuesetting.valueName = 'paris'; // Name of new value. same value which is going to update:
 
//update the controllingFieldValue
        for(MetadataService.ValueSettings valuesetting :  reportCustomField.ValueSet.ValueSettings){
            if(valuesetting.valueName == picklstValApiname){
                System.debug('##controlling: '+valuesetting.controllingFieldValue);
                valuesetting.valueName = 'paris'; // Name of new value which asigned above. Hard code for now
                valuesetting.controllingFieldValue = new List<String>{'France'};
            }
        }


 
Jane GJane G
I have a question, why does the valuesetting.controllingFieldValue takes a list of string and not a string?

Also, it has been updated successfully. 

But i can't see it on the screen (page layout) as the object has record type. I will have to specify which record type to assign the picklist value to, right? the record type attribute is found in the picklist class though. Can i still do it using valueSet? 

Thank you.
Om PrakashOm Prakash
1. Controlling values for a dependent picklist value can be multiple, right?
From controlling field either value "A" get selected or value "B" get selected, paris  value should be available in the dependent field.
In this case, we need to pass multiple values, that's why List<String>, not String.

When we manually include/exclude then we also have multiple selection options.

User-added image


2. I suggest, please give it a try assigning record type attribute, and let's discuss what is the finding.

 
Jane GJane G

@Om

1. thank you its clear now

2. i've tried it. I have created a picklist value for the dependent picklist. I have retrieved all the picklist and its values for a specific record type. I want to add the newly created picklist value to the list of picklist values retrieved for that record type, but the picklist value is of type CustomValue and the picklist values of type MetadataService.PicklistValue. I'm stuck.

MetadataService.MetadataPort service = new MetadataService.MetadataPort();   
        service.SessionHeader = new MetadataService.SessionHeader_element();
        service.SessionHeader.sessionId = UserInfo.getSessionId();

        MetadataService.CustomField customField = (MetadataService.CustomField) service.readMetadata('CustomField', new String[] { dependentpklstapiname }).getRecords()[0];

        //build the picklist value
        MetadataService.CustomValue value = new MetadataService.CustomValue();
        value.fullName = 'fruit';
        value.default_x = false ;
        value.isActive = true;

        customField.valueSet.valueSetDefinition.value.add(value);

        //read recordtype
        MetadataService.RecordType recordType = (MetadataService.RecordType) service.readMetadata('RecordType', new String[] { rectypename }).getRecords()[0];
        System.debug('### recordType: ' + recordType.label);

        //get picklist and its values for the specified record type
        for ( MetadataService.RecordTypePicklistValue rpk : recordType.picklistValues ) {
            if(rpk.picklist == 'test__c'){
                System.debug('picklistvals: '+rpk.values);
// i want to add the value (fruit) here in the  rpk.values list
            }
        }

        List<MetadataService.SaveResult> lstResults = service.updateMetadata( new MetadataService.Metadata[] { customField });
Om PrakashOm Prakash
Hi Jane,
It's really new learning for me also with this discussion as exploring these.

Please try below code to assign the new value to record type. (fruit value is already created/existing, in below code only recordtype is getting updated).

Changes in your recent code are highlighted for easy tracking.
 
MetadataService.MetadataPort service = new MetadataService.MetadataPort();   
        service.SessionHeader = new MetadataService.SessionHeader_element();
        service.SessionHeader.sessionId = UserInfo.getSessionId();

        MetadataService.CustomField customField = (MetadataService.CustomField) service.readMetadata('CustomField', new String[] { dependentpklstapiname }).getRecords()[0];

        //build the picklist value
       /* Commented
        MetadataService.CustomValue value = new MetadataService.CustomValue();
        value.fullName = 'fruit'; 
        value.default_x = false ;
        value.isActive = true;

        customField.valueSet.valueSetDefinition.value.add(value);
        */

        MetadataService.PickListValue objPickListValue = new MetadataService.PickListValue();
        objPickListValue.fullName = 'fruit'; // Existing value
        objPickListValue.default_x = false;

        //read recordtype
        MetadataService.RecordType recordType = (MetadataService.RecordType) service.readMetadata('RecordType', new String[] { rectypename }).getRecords()[0];
        System.debug('### recordType: ' + recordType.label);

        //get picklist and its values for the specified record type
        for ( MetadataService.RecordTypePicklistValue rpk : recordType.picklistValues ) {
            if(rpk.picklist == 'test__c'){
                System.debug('picklistvals: '+rpk.values);
                rpk.values.add(objPickListValue);
            }
        }

        List<MetadataService.SaveResult> lstResults = service.updateMetadata( new MetadataService.Metadata[] { recordType });


 
Om PrakashOm Prakash
And after combining above both codes to insert new picklist value and assign it to a specific record type, the code would like below:
String pickListAPI = 'Students__c.Status__c';
String fieldAPI = 'Status__c'; // Only field name
String recordTypeName = 'Students__c.Current_Student';

MetadataService.MetadataPort service = new MetadataService.MetadataPort();   
service.SessionHeader = new MetadataService.SessionHeader_element();
service.SessionHeader.sessionId = UserInfo.getSessionId();

MetadataService.CustomField customField = (MetadataService.CustomField) service.readMetadata('CustomField', new String[] { pickListAPI }).getRecords()[0];

//build the picklist value for create new value
MetadataService.CustomValue value = new MetadataService.CustomValue();
value.fullName = 'New Value';
value.default_x = false ;
value.isActive = true;

customField.valueSet.valueSetDefinition.value.add(value);

// Update the custom Field
List<MetadataService.SaveResult> lstResults = service.updateMetadata( new MetadataService.Metadata[] { customField });

// instance of PickListValue for record type
MetadataService.PickListValue objPickListValue = new MetadataService.PickListValue();
objPickListValue.fullName = value.fullName;
objPickListValue.default_x = value.default_x;

//read recordtype
MetadataService.RecordType recordType = (MetadataService.RecordType) service.readMetadata('RecordType', new String[] { recordTypeName }).getRecords()[0];

//get picklist and it's values for the specified record type
for ( MetadataService.RecordTypePicklistValue rpk : recordType.picklistValues ) {
    if(rpk.picklist == fieldAPI){
        rpk.values.add(objPickListValue);
    }
}
// update record type
lstResults = service.updateMetadata( new MetadataService.Metadata[] { recordType });

 
Jane GJane G

Hello Om Prakash,


Thank you so much for your help. It worked successfully. 

Om PrakashOm Prakash
You're welcome!
Sandeep KumarSandeep Kumar
Hey Om Praksh,

I am trying to update the picklist API names, however getting below issue. Can you help me?

"At least one value is required to create this picklist."

Here is my CODE:
 Map<String,String> labelToValueMap = new Map<String,String>();
        
        Schema.DescribeFieldResult fieldResult = User.Countrycode.getDescribe();
        List<Schema.PicklistEntry> ple = fieldResult.getPicklistValues();
        for( Schema.PicklistEntry f : ple){
            labelToValueMap.put(f.getLabel(),f.getValue());
        }
        
        String picklistapiname = 'CustomObject.CountryCode';
        MetadataService.MetadataPort service = new MetadataService.MetadataPort();   
        service.SessionHeader = new MetadataService.SessionHeader_element();
        service.SessionHeader.sessionId = UserInfo.getSessionId();
        System.debug('service'+service);
        MetadataService.CustomField customField = (MetadataService.CustomField) service.readMetadata('CustomField', new String[]{picklistapiname}).getRecords()[0];
        for(MetadataService.CustomValue objCustomValue : customField.valueSet.valueSetDefinition.value){
            objCustomValue.fullName = labelToValueMap.get(objCustomValue.label);                 
        }
        
        // Update 
        List<MetadataService.SaveResult> lstResults = service.updateMetadata(new MetadataService.Metadata[] {customField});
        
        for (MetadataService.SaveResult objResult : lstResults) {
            if (objResult.success) {
                System.debug('Successfully updated');
            }
            else {                       
                if(objResult.errors.size() > 0){
                    System.debug('eror : ' + objResult.errors[0].message);
                }
            }
        }
Mohammed AyyubMohammed Ayyub
The above gievn solutions works 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/00N540000071QJG
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
Suyash Kawna ☁️Suyash Kawna ☁️
Hi Om Prakash,

I am getting below error while  running your code.

Line: 11364, Column: 1
System.CalloutException: Web service callout failed: WebService returned a SOAP Fault: '' is not valid for type xsd:boolean, should be '0', '1', 'true' or 'false' faultcode=soapenv:Client faultactor=

Could you please help.
Om PrakashOm Prakash
Hi Suyash,
Have you assigned the value for default_x?

For example:

objPickListValue.default_x = false ;
anil bolisettyanil bolisetty
Om prakash,,
 I am using your class but i am getting MetadataService.MetadataPort invalid ,do i need to any other class ?