14

I'm building a Custom List Button in a Dev Environment, and I'd like it to work with no changes when deployed to Production even though all IDs will change.

The list button (which will be displayed in a related list) allows users to create a new related object, but specifies a specific Record Type, and pre-fills the new object with data from the current record.

It looks like this:

{!URLFOR( $Action.Contact.NewContact , null, [CF00NJ0000000QIX7=Contact.Id, RecordType="012J00000004NGB"])}

The problem is that this RecordType ID will change with deployment. I'll always know it's name, though. Is there any way to use that?

Update: Also, won't the custom field ID change also (CF00NJ0000000QIX7 above) -- what do I do about that?

  • Benj, 2 things: (1) yes, the custom field ID is different in every org (e.g. Prod, Test Sandbox, Dev Sandbox), and use of this 'Id' is NOT supported, but it should remain constant in a single org, so as long as you figure out what it is in every org you use, you could store it in a custom setting, as @Forceguru suggests. (2) It is always best practice to use RecordType.DeveloperName when possible, not the Id. – zachelrath Sep 28 '12 at 17:10
  • Zach, that's helpful. Can I refer to RecordType.DeveloperName in custom links URLs like the above? – Benj Sep 28 '12 at 17:14
13

You can use custom setting for this. Keep the record type Id in custom setting and use that in your button code, it will look something like this "{!$Setup.CustomSettingAPI_c.RecordTypeId_c}"

| improve this answer | |
  • 1
    Hm. This would require a configuration change on deployment, but it can be documented easily. Thanks. – Benj Sep 25 '12 at 4:23
  • Good solution Forceguru. The $RecordType global variable is not available in Custom button context, but the $Setting.<SettingName> variable is, so this would work great for your solution, and requires no Apex code. – zachelrath Sep 25 '12 at 12:24
  • There are many other options, but this is the shortest and sweetest. – Forceguru Sep 28 '12 at 22:50
5

As an aide once you deploy to Production and refresh your sandboxes from Prod, record type ids and for that matter all ids (metadata) will be in sync (I.e. same ) across environments

| improve this answer | |
  • Do you know where SF has that documented (link)? I agree with you, but just wonder where SF has it documented. – Peter Knolle Sep 29 '12 at 0:59
  • Just know from practical experience. Don't think I've seen it in documentation. – techtrekker Sep 29 '12 at 1:05
4

Great question!

For the sake of anyone who's doing this in Visualforce (which, Benj, I recognize you're not), here's what I would do: get a reference to the record type Id in Apex using a method similar to this: (note that I am returning a Map keyed by RecordType DeveloperName, NOT Name, because Name can always be changed/overridden in Production org, whereas DeveloperName does not change. This is an especially important best practice when developing managed packages.

// Returns a map of active, user-available RecordType IDs for a given SObjectType,
// keyed by each RecordType's unique, unchanging DeveloperName 
global static Map<String, Id> GetRecordTypeIdsByDeveloperName(
    Schema.SObjectType token
) {
    // Build a map of RecordTypeIds keyed by DeveloperName
    Map<String, Id> mapRecordTypes = new Map<String, Id>();

    // Get the Describe Result
    Schema.DescribeSObjectResult obj = token.getDescribe();

    // Obtain ALL Active Record Types for the given SObjectType token
    // (We will filter out the Record Types that are unavailable
    // to the Running User using Schema information)
    String soql = 
        'SELECT Id, Name, DeveloperName '
        + 'FROM RecordType '
        + 'WHERE SObjectType = \'' + String.escapeSingleQuotes(obj.getName()) + '\' '
        + 'AND IsActive = TRUE';
    List<SObject> results;
    try {
        results = Database.query(soql);
    } catch (Exception ex) {
        results = new List<SObject>();
    }

    // Obtain the RecordTypeInfos for this SObjectType token
    Map<Id,Schema.RecordTypeInfo> recordTypeInfos = obj.getRecordTypeInfosByID();

    // Loop through all of the Record Types we found,
    //      and weed out those that are unavailable to the Running User
    for (SObject rt : results) {  
        if (recordTypeInfos.get(rt.Id).isAvailable()) {
            // This RecordType IS available to the running user,
            //      so add it to our map of RecordTypeIds by DeveloperName
            mapRecordTypes.put(String.valueOf(rt.get('DeveloperName')),rt.Id);
        }
    }

    return mapRecordTypes;
}

TO use this to accomplish your need, you could do something like the following:

// CONTROLLER
public class MyController() {

    public transient Id myRecordType {
         public get {
              if (myRecordType == null) {
                 myRecordType = GetRecordTypeIdsByDeveloperName(
                     DesiredObject__c.SObjectType
                 ).get('Desired_Record_Type_Developer_Name');
              } 
              return myRecordType;  
         } private set;
    }
}


 // Visualforce Page with Link to create a new record of this type
 <apex:page>
       <apex:commandLink value='{!URLFOR( $Action.Contact.NewContact , null, [CF00NJ0000000QIX7=Contact.Id, RecordType=myRecordType])}'/> 
 </apex:page>
| improve this answer | |
2

Yes. You can query the RecordType object in your controller and then reference that property in your VF page.

Here's an example using the Account object:

Id recTypeId = [
     Select Id 
     From RecordType 
     Where SObjectType ='Account' 
       And Name = 'Your Record Type Name' Limit 1
][0].Id;
| improve this answer | |
  • This isn't actually a VF page - it's a custom list button (on a related list). But useful nonetheless! – Benj Sep 25 '12 at 4:22
  • Gah! Sorry about that. – Peter Knolle Sep 25 '12 at 10:46
  • 3
    This is a good way to go, however you should also check to make sure the RecordType is Active and available to the running user. Also, it is best practice to query against the DeveloperName / API Name of a RecordType, as this will never change, whereas the Name of a RecordType can be overridden or translated in target/Production orgs. – zachelrath Sep 25 '12 at 12:04
2

You can also do this with an on-click java script and to call an apex web-service to return the recordtype ID. Use the returned record type ID string to create your new contact.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.