The ability to apply custom filters to lookup fields has long been desirable. The Dynamics CRM 2013 SDK simplified the process by surfacing several methods and events (most notably addCustomFilter and addPreSearch) for lookup controls. There are many examples available elsewhere demonstrating how to use these in CRM 2013 and 2015, so I will not rehash that in this particular post. These new methods are wonderful, but many people are still using Dynamics CRM 2011 and are likely to do so for the foreseeable future. For this post, I'm going to focus on Dynamics CRM 2011 Custom Filtered Lookup display errors.
In CRM 2011, such filtering is implemented by writing custom JavaScript to change the FetchXML associated with the lookup control (via the addCustomView and setDefaultView methods). This technique has served Armanino Consulting and many other CRM 2011 developers very well over the past 4+ years. The following JavaScript demonstrates custom filtering.
If you are already familiar with this technique, feel free to skip beyond the JavaScript and the accompanying description.
function endCustomer_onChange() {
var filterDate = null;
if (Xrm.Page.ui.getFormType() == 1) {
filterDate = new Date();
}
else {
filterDate = Xrm.Page.getAttribute("createdon").getValue();
}
filterDate = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" +
date.getDate() + " " + date.getHours() + ":" + date.getMinutes();
var endCustomerId = Xrm.Page.getAttribute("endcustomerid").getValue();
var viewId = "{00000000-0000-0000-0000-000000000000}";
var entityName = "customentityname";
var viewDisplayName = "Available Records";
var fetchXml = "<?xml version='1.0'?>" +
"<fetch distinct='true' mapping='logical' output-format='xml-platform'" +
" version='1.0'><entity name='" + entityName + "'>" +
"<attribute name='fieldname1'/>" +
"<attribute name='fieldname2'/>" +
"<order descending='false' attribute='createdon'/>" +
"<filter type='and'>" +
"<condition attribute='endcustomerid' value='" + endCustomerId[0].id +
"' uitype='account' uiname='" + endCustomerId[0].name + "'operator='eq'/>" +
"<condition value='0' attribute='statecode' operator='eq'/>" +
"<filter type='and'>" +
"<condition value='" + filterDate + "' attribute='startdate'" +
" operator='on-or-before'/>" +
"<condition value='" + filterDate + "' attribute='enddate'" +
" operator='on-or-after'/>" +
"</filter>" +
"</filter>" +
"</entity></fetch>";
var layoutXml = "<grid name='resultset' object='1' jump='name' select='1' icon='1' preview='1'>" +
"<row name='name' id='customentityid'>" +
"<cell name='name' width='150' />" +
"<cell name='fieldname1' width='150' />" +
"<cell name='fieldname2' width='150' />" +
"</row>" +
"</grid>";
Xrm.Page.getControl("customentitylookup").addCustomView(viewId, entityName,
viewDisplayName, fetchXml, layoutXml, true);
Xrm.Page.getControl("customentitylookup").setDefaultView(viewId);
}
(NOTE: A FetchXML overview can found at MSDN, and the complete FetchXML schema is available here).
This is a fairly typical implementation: We want to filter the available records for a lookup field based upon the value of some other field that will be selected by the user (in this case, End Customer). This function serves as an onChange event for the End Customer field, so the FetchXML for the lookup field will change whenever the End Customer is changed. The highlighted sections form the filtering foundation. The End Customer's Name and Guid are inserted at the red sections. The current date (for new records) or CreatedOn date (for existing records) is inserted at the green sections to further filter the lookup result set function. The addCustomView() method attaches the custom FetchXML to the lookup control, and the setDefaultView() method activates it.
Recently one of our users attempted to display a custom filtered lookup, and encountered the infamous cryptic CRM error message:
This confused everyone involved – the user, the CRM sysadmin, and the developer – because the custom filtered lookup had been working perfectly for several months.
Preliminary investigation quickly revealed the REAL error: "The XML passed to the platform is not well-formed XML." A review of the JavaScript revealed nothing... it looked perfectly fine.
Years of software development experience have taught me that, when seemingly-stable code abruptly stops working, the first place to look for answers is the data. With additional script debugging, I was able to get the entire contents of the fetchXml variables and stick it into one of countless free online XML validators. The validator returned the same error message ("not well-formed XML"), but it included some additional details that CRM did not divulge:
Error: Entity name must immediately follow the '&' within entity reference.
Surely enough, the End Customer's name included an ampersand ("&"). This is a reserved character in XML, as are: Less Than ("<"); Greater Than (">"); and Percent ("%"). This explained quite neatly why the JavaScript and FetchXML had been working for so long... it was indeed the data that had caused the problem.Fortunately it took less time to implement the solution than it had to pin down the problem. JavaScript includes two string functions -- escape() and encodeURIComponent(), which encode special characters within strings (including that pesky ampersand). Only one small change was necessary:
"' uitype='account' uiname='" + endCustomerId[0].name + // OLD
"' uitype='account' uiname='" + encodeURIComponent(endCustomerId[0].name) + // NEW
Whenever customer/contact names are involved, we now use encodeURIComponent to protect ourselves from malformed XML errors. Just remember the saying "An ounce of prevention..."
Find more CRM troubleshooting and fixes like this one for the Dynamics CRM 2011 custom filtered lookup display error our Dynamics resources.