Mimic multi-type lookup in CRM 2011

Certain CRM entities have lookup fields that allow more than one type (regardingobjectid on all activity types, customerid on case, etc). Unfortunately, we are not able to create custom fields allowing multiple types but we can mimic the user interface on the form using the following approach. To highlight the approach, I am going to create a custom Credit Note entity (new_creditnote) with a Customer field that can point to an Account or a Contact.

Step 1: Create N:1 relationships to each of the possible entity types.
Mimic custom multi-type lookup - Step 1

Step 2: Add all fields to the form. Give them all the same label.
Mimic custom multi-type lookup - Step 2

Step 3: Add code to the on load event of the form to override the available lookup types of the first of the multi-type controls.

var control = document.getElementById("new_accountid"); // Get the first control
control.setAttribute("lookuptypes", "1,2"); // Set the available lookup types
control.setAttribute("lookuptypenames", "account:1:Account,contact:2:Contact"); // Set the available lookup type names

Step 4: Add an on change event to each of the controls

Xrm.Page.data.entity.attributes.get("new_accountid").addOnChange(customer_onchange);
Xrm.Page.data.entity.attributes.get("new_contactid").addOnChange(customer_onchange);
function customer_onchange() {
  if (Xrm.Page.data.entity.attributes.get("new_accountid").getValue() != null){
    switch (Xrm.Page.data.entity.attributes.get("new_accountid").getValue()[0].type){
      case "1": // Account
        // Already the correct type
        break;
      case "2": // Contact
        Xrm.Page.data.entity.attributes.get("new_contactid").setValue(Xrm.Page.data.entity.attributes.get("new_accountid").getValue()); // Set the value of the new_contactid field to the value of the new_accountid field
        Xrm.Page.data.entity.attributes.get("new_accountid").setValue(null); // Set the value of the new_accountid field to null
        break;
    }
  }

  Xrm.Page.ui.controls.get("new_accountid").setVisible(Xrm.Page.data.entity.attributes.get("new_accountid").getValue() != null || Xrm.Page.data.entity.attributes.get("new_contactid").getValue() == null); // Set the visibility of the first control to visible if the selected customer is a contact or if all the other lookups are null
  Xrm.Page.ui.controls.get("new_contactid").setVisible(Xrm.Page.data.entity.attributes.get("new_contactid").getValue() != null); // Set the visibility of the other control to visible if the lookup's value is not null
}

Step 5: Add code to the onload event of the form to call the customer_onchange function to set the visibility of the customer controls.

customer_onchange();

And there you have it – multiple lookups that to the user, behave like a single multi-type lookup.

Example form showing a contact selected through the account lookup control.

Example form showing a contact selected through the account lookup control.

Unfortunately, this approach does not handle displaying only one field in lists. This could probably be achieved with a sneaky PostRetrieveMultipe plugin but I would not advise it.

Example list showing 2 credit notes. One has an account customer and the other has a contact customer.

Example list showing 2 credit notes. One has an account customer and the other has a contact customer.

The approach explained here is the bare minimum to get this kind of interface.

Where I have used this approach, I actually place each lookup into its own section and then set the visiblity of the section, not the individual control. This is because hiding a control actually leaves the space that the control was in on the form (like the difference between setting the display vs visibility CSS properties). Setting the visiblity of the section makes the control really appear to be one control.

The approach is not limited to 2 entity types as in the example above and can be expanded to cater for any number of different entity types.

See below for the full code listing:

function onload() {
  var control = document.getElementById("new_accountid"); // Get the first control
  control.setAttribute("lookuptypes", "1,2"); // Set the available lookup types
  control.setAttribute("lookuptypenames", "account:1:Account,contact:2:Contact"); // Set the available lookup type names

  customer_onchange();

  Xrm.Page.data.entity.attributes.get("new_accountid").addOnChange(customer_onchange);
  Xrm.Page.data.entity.attributes.get("new_contactid").addOnChange(customer_onchange);
}

function customer_onchange() {
  if (Xrm.Page.data.entity.attributes.get("new_accountid").getValue() != null){
    switch (Xrm.Page.data.entity.attributes.get("new_accountid").getValue()[0].type){
      case "1": // Account
        // Already the correct type
        break;
      case "2": // Contact
        Xrm.Page.data.entity.attributes.get("new_contactid").setValue(Xrm.Page.data.entity.attributes.get("new_accountid").getValue()); // Set the value of the new_contactid field to the value of the new_accountid field
        Xrm.Page.data.entity.attributes.get("new_accountid").setValue(null); // Set the value of the new_accountid field to null
        break;
    }
  }

  Xrm.Page.ui.controls.get("new_accountid").setVisible(Xrm.Page.data.entity.attributes.get("new_accountid").getValue() != null || Xrm.Page.data.entity.attributes.get("new_contactid").getValue() == null); // Set the visibility of the first control to visible if the selected customer is a contact or if all the other lookups are null
  Xrm.Page.ui.controls.get("new_contactid").setVisible(Xrm.Page.data.entity.attributes.get("new_contactid").getValue() != null); // Set the visibility of the other control to visible if the lookup's value is not null
}