function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Derhyk Doggett -Derhyk Doggett - 

Updating Picklist Through Metadata API Removes Previous Picklist Values

I've used the picklist example from this link as a starting point:
https://github.com/financialforcedev/apex-mdapi/blob/master/apex-mdapi/src/classes/MetadataServiceExamples.cls

I've run execute anonymous passing in the parameters. The picklist gets updated with the new value, but marks the previous values as inactive.
Any ideas?

Snippet of code running the update:

webService static void updatePicklistField(String picklistToUpdate, String picklistLabel, String newPicklistValue)
    {

		MetadataService.MetadataPort service = createService();
        MetadataService.CustomField customField = new MetadataService.CustomField();
        customField.fullName = picklistToUpdate; //This is the picklist field. This must in the format Object.Field (append __c for custom object/field)
        customField.label = picklistLabel; //This field is needed in update
        customField.type_x = 'Picklist';
        metadataservice.Picklist pt = new metadataservice.Picklist();
        pt.sorted= false;
        metadataservice.PicklistValue newPlValue = new metadataservice.PicklistValue();
        newPlValue.fullName  = newPicklistValue; //This is the new picklist value
        newPlValue.default_x =false ;
        pt.picklistValues = new list<metadataservice.PicklistValue>{newPlValue};
        customField.picklist = pt ;
        List<MetadataService.SaveResult> results =
            service.updateMetadata(
                new MetadataService.Metadata[] { customField });
        handleSaveResults(results[0]);
    }

Run Execute Anonymous below:

MetadataHandler.updatePicklistField('Account.Access_Type__c','Access Type','Metadata');

Results in the following. note the now one active value and newly marked inactive values:
User-added image
 

Best Answer chosen by Derhyk Doggett -
Derhyk Doggett -Derhyk Doggett -

Okay figured this out. If anyone figures this out without having to include the whole active list of values, please comment. The solution below will leave a recent system timestamp on all active values when new ones are inserted.
In a nutshell, use describe to get the Object, then Object Field Map, use that map to get the field to update, describe that field, get the Picklist values. If the picklist values are active, include them in the list to update.

 

webService static void updatePicklistField(String obj, String picklistToUpdate, String picklistLabel, String newPicklistValue)
    {

		//Get the details of the current picklist as we need to include the existing values
		SobjectType objType    = Schema.getGlobalDescribe().get(obj);
		Map<String,Schema.SObjectField> objFields = objType.getDescribe().fields.getMap();
		SObjectField fieldName = objFields.get(picklistToUpdate);
		Schema.DescribeFieldResult fieldResult = fieldName.getDescribe();
		fieldResult = fieldResult.getSObjectField().getDescribe();
		List<Schema.PicklistEntry> ple = fieldResult.getPicklistValues();

		MetadataService.MetadataPort service = createService();
        MetadataService.CustomField customField = new MetadataService.CustomField();
        customField.fullName = obj + '.' + picklistToUpdate; //This must in the format Object.Field
        customField.label = picklistLabel; //This field is needed in update
        customField.type_x = 'Picklist';
        metadataservice.Picklist pt = new metadataservice.Picklist();
        pt.sorted= false;
        metadataservice.PicklistValue newPlValue = new metadataservice.PicklistValue();
        newPlValue.fullName  = newPicklistValue; //This is the new picklist value
        newPlValue.default_x =false ;

		pt.picklistValues = new list<metadataservice.PicklistValue>();
		pt.picklistValues.add(newPlValue);

		//Add Existing values back in
		for (Schema.PicklistEntry pl : ple){
			if (pl.isActive()){
				metadataservice.PicklistValue existingPLValue = new metadataservice.PicklistValue();
				existingPLValue.fullName  = pl.getValue();
				existingPLValue.default_x = pl.isDefaultValue();
				pt.picklistValues.add(existingPLValue);
			}
		}

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

--Derhyk

All Answers

Derhyk Doggett -Derhyk Doggett -

Okay figured this out. If anyone figures this out without having to include the whole active list of values, please comment. The solution below will leave a recent system timestamp on all active values when new ones are inserted.
In a nutshell, use describe to get the Object, then Object Field Map, use that map to get the field to update, describe that field, get the Picklist values. If the picklist values are active, include them in the list to update.

 

webService static void updatePicklistField(String obj, String picklistToUpdate, String picklistLabel, String newPicklistValue)
    {

		//Get the details of the current picklist as we need to include the existing values
		SobjectType objType    = Schema.getGlobalDescribe().get(obj);
		Map<String,Schema.SObjectField> objFields = objType.getDescribe().fields.getMap();
		SObjectField fieldName = objFields.get(picklistToUpdate);
		Schema.DescribeFieldResult fieldResult = fieldName.getDescribe();
		fieldResult = fieldResult.getSObjectField().getDescribe();
		List<Schema.PicklistEntry> ple = fieldResult.getPicklistValues();

		MetadataService.MetadataPort service = createService();
        MetadataService.CustomField customField = new MetadataService.CustomField();
        customField.fullName = obj + '.' + picklistToUpdate; //This must in the format Object.Field
        customField.label = picklistLabel; //This field is needed in update
        customField.type_x = 'Picklist';
        metadataservice.Picklist pt = new metadataservice.Picklist();
        pt.sorted= false;
        metadataservice.PicklistValue newPlValue = new metadataservice.PicklistValue();
        newPlValue.fullName  = newPicklistValue; //This is the new picklist value
        newPlValue.default_x =false ;

		pt.picklistValues = new list<metadataservice.PicklistValue>();
		pt.picklistValues.add(newPlValue);

		//Add Existing values back in
		for (Schema.PicklistEntry pl : ple){
			if (pl.isActive()){
				metadataservice.PicklistValue existingPLValue = new metadataservice.PicklistValue();
				existingPLValue.fullName  = pl.getValue();
				existingPLValue.default_x = pl.isDefaultValue();
				pt.picklistValues.add(existingPLValue);
			}
		}

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

--Derhyk
This was selected as the best answer
Kevin MurrayKevin Murray
Hey Derhyk, I'm running into the exact same issue and using the same solution as you did, but want to add my support to keeping this thread alive in case anyone has come up with a better option.  The only other options I could find were using the Tooling API or another SOAP API call to retrieve the existing Custom Values of the Global Value Set, but at least in your approach you save a callout.