About one and half years ago, I blogged an approach to implement filtering functionality for lookup fields on CRM form, using George Doubinski's invention. Today, I am going to take this approach one step further, and show you how to apply the same technique to achieve our purpose for a CRM associated view that represents a many-to-many relationship. The CRM form and its associated view could be something similar to the following screenshot.
In the above scrren, I made up a custom entity called Project, which has a N:N relationship with Account entity. My requirement is to only show the account records that have an industry code of "Service Retail" (Again, this is what I came up, your story could be entirely different).
Since I have previously documented how to load CRM associated view in an iframe, I am not going to repeat the same code here.
The following is the procedure that you may follow to implement the aforementioned N:N filtering lookup, which is fairly similar to the filtering functionality for lookup fields on CRM form:
- Create a subfolder called CustomLookup under your CRMWeb's ISV folder.
- Copy <crmweb folder>\_controls\lookup\lookupsingle.aspx page to your ISV\CustomLookup folder which you have just created, and renamed the file to lookupmulti.aspx.
- Add the following script tag to <crmweb folder>\ISV\CustomLookup\lookupmulti.aspx (Please thank George for his code, this is basically a copy from his snippet with one extra line that I added to enable multi-select).
<%-- ********************************** BEGIN: Custom Changes ********************************** This page is a copy of <crmweb folder>\_controls\lookup\lookupsingle.aspx file with the following custom change. --%> <script runat="server"> protected override void OnLoad( EventArgs e ) { base.OnLoad(e); crmGrid.PreRender += new EventHandler( crmgrid_PreRender ); } void crmgrid_PreRender( object sender , EventArgs e ) { // As we don't want to break any other lookups, ensure that we use workaround only if // search parameter set to fetch xml. if (crmGrid.Parameters["search"] != null && crmGrid.Parameters["search"].StartsWith("<fetch")) { crmGrid.Parameters.Add("fetchxml", crmGrid.Parameters["search"]); // searchvalue needs to be removed as it's typically set to a wildcard '*' crmGrid.Parameters.Remove("searchvalue"); // Allow multi-select crmGrid.MaximumSelectableItems = -1; // Icing on a cake - ensure that user cannot create new new record from the lookup window this._showNewButton = false; } } </script> <%-- ********************************** END: Custom Changes ********************************** --%>
- Use the script in my previous blog post to load the associated view in iframe. The script should be added to your CRM form's onload event.
- Then you are ready to add filtering criteria to your many-to-many associated view lookup button. In my case, my requirement is to only show the account records that have an industry code of "Service Retail" (its picklist integer value is 25) as I have just mentioned above.
getFilteredUrlForAddExistingAccountButton = function(lookupUrl) { var iframe = crmForm.all.IFRAME_Accounts; // Change IFRAME_Accounts to your iframe's ID var crmGrid = iframe.contentWindow.document.all['crmGrid']; // If it's not N:N lookup dialog, we skip it. if (lookupUrl.toLowerCase().match(/\/_controls\/lookup\/lookupmulti.aspx/i) == null) return lookupUrl; // If the lookup window is not concerned with the entity that we are interested in, we skip as well if (GetQueryString(lookupUrl, 'objecttypes') !== crmGrid.GetParameter('otc')) return lookupUrl; lookupUrl = lookupUrl.replace(/\/_controls\/lookup\/lookupmulti.aspx/i, '/ISV/CustomLookup/lookupmulti.aspx'); var filterXml = '<fetch mapping="logical">' + '<entity name="account">' + '<filter>' + '<condition attribute="industrycode" operator="eq" value="25" />' + // Industry: Service Retail '</filter>' + '</entity>' + '</fetch>'; // Ensure that search box is not available in the lookup dialog lookupUrl = SetQueryString(lookupUrl, 'browse', 1); lookupUrl = SetQueryString(lookupUrl, 'ShowNewButton', 0); lookupUrl = SetQueryString(lookupUrl, 'search', filterXml); return lookupUrl; };
- Add the following immediate JavaScript function to your form's onload event, following the above script
(function replaceCrmLookups() { window.oldOpenStdDlg = window.oldOpenStdDlg || window.openStdDlg; window.openStdDlg = function() { arguments[0] = getFilteredUrlForAddExistingAccountButton(arguments[0]); return oldOpenStdDlg.apply(this, arguments); }; })();
- Save your form and publish your entity, you are good to go now.
In my case, after I have everything published, my "Add Existing Account" now prompts me the following screen. As you can tell, I intentionally changed the default many-to-may lookup dialog to the single lookup dialog, which is usually considered to be much more user friendly. I believe we have just shot two birds with one stone, isn't that something beautiful?
Note that you should change the iframe ID in the first line of getFilteredUrlForAddExistingAccountButton() function, also ensure that you have constructed filterXml string correctly in the same function.
I hope this helps.