04 August 2009

CRM Filtered Lookup Multi

I had some posts last year about the CRM Filtered Lookup, these technique are broadly used in the CRM community.

The mysterious CRM Lookup (I)
The mysterious CRM Lookup (II)
The mysterious CRM Lookup (III)

A few days ago, I saw a post on the Microsoft Dynamics CRM Chinese Forum about how to add filter to LookupMulti.aspx ?
I think it's a very common requirements, so I'd like to give my idea.
When I start with this customization, my bottom line was: Not change any files/databases. However this customization should be marked as a "unsupported customization" (call CRM/JS function directly).


OK, the question was:
A customized entity: ShippingMark (new_shippingmark), it has N:1 relationship with Account; it also has N:N relationship with Quote.
And as we known by default, Quote has N:1 relationship with Account(via customerid)

So the relationship is simple: Account -< (customerid)Quote >< ShippingMark(new_accountid) >- Account

What the user wants was classic: Open a Quote record, then go to Add Existing ShippingMark, then in the LookupMulti page, only return the ShippingMark which has the same Account(new_account) with Quote's(customerid).

There are two parts of the code: server side Plugin.Execute event and client side CRM.Onload event. What the client side code does is: create a custom lookup window, and pass the customerid as a parameter, so the lookup URL looks like: …&id=…, then the server side plugin will replace the FilterXml query string based on the parameter.

I give the code prototype for this specific requirement, you need to modify it for re-use. This technique should work for both LookupSingle.aspx and LookupMulti.aspx.


1. Plug-Ins
Register the Execute message on the Pre Stage/Synchronous/Server/Parent Pipeline.



/*
* Microsoft Dynamics CRM Lookup Filter
* Plug-Ins: Execute message on the Pre Stage/Synchronous/Server/Parent Pipeline.
* Jim Wang @ Aug 2009, http://jianwang.blogspot.com, http://mscrm.cn
*
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Web;
using Microsoft.Crm.Sdk;

namespace CRMExecuteEvent
{
public class CRMExecuteEvent : IPlugin
{
string lookupId;

public void Execute(IPluginExecutionContext context)
{

lookupId = HttpContext.Current.Request.QueryString["id"] == null ? null : HttpContext.Current.Request.QueryString["id"].ToString();

if (lookupId == null) return;

try
{
if (context.InputParameters.Contains("FetchXml"))
{
string beforeXml = (String)context.InputParameters["FetchXml"];

if (beforeXml.Contains("<entity name=\"new_shippingmark\">") && beforeXml.Contains("xml-platform"))
{
//Customise the FetchXml query string
string afterXml =
"<fetch version='1.0' page='1' count='100' output-format='xml-platform' mapping='logical'> " +
"<entity name='new_shippingmark'> " +
"<attribute name='new_shippingmarkid' /> " +
"<attribute name='new_name' /> " +
"<attribute name='createdon' /> " +
"<order attribute='new_name' /> " +
"<link-entity name='quote' to='new_accountid' from='customerid'> " +
"<filter type='and'> " +
"<condition attribute = 'customerid' operator='eq' value='" + lookupId + "'/> " +
"</filter> " +
"</link-entity> " +
"<filter type='and'> " +
"<condition attribute='statecode' operator='eq' value='0' /> " +
"<condition attribute='new_name' operator='like' value='%' /> " +
"</filter> " +
"</entity> " +
"</fetch>";

//Replace the FetchXml query string
context.InputParameters["FetchXml"] = beforeXml.Replace(beforeXml, afterXml);

}
}
}

catch (System.Web.Services.Protocols.SoapException ex)
{
throw new InvalidPluginExecutionException("An error occurred in the CRM plug-in.", ex);
}
}

}
}




2. Quote.OnLoad()


var relId = "new_new_shippingmark_quote";
var lookupId = crmForm.all.customerid;

var lookupEntityTypeCode;
var navId = document.getElementById("nav" + relId);
if (navId != null)
{
var la = navId.onclick.toString();
la = la.substring(la.indexOf("loadArea"), la.indexOf(";"));

navId.onclick = function()
{
eval(la);

var areaId = document.getElementById("area" + relId + "Frame");
if(areaId != null)
{
areaId.onreadystatechange = function()
{
if (areaId.readyState == "complete")
{
var frame = frames[window.event.srcElement.id];
var li = frame.document.getElementsByTagName("li");

for (var i = 0; i < li.length; i++)
{
var action = li[i].getAttribute("action");
if(action != null && action.indexOf(relId) > 1)
{
lookupEntityTypeCode = action.substring(action.indexOf("\(")+1, action.indexOf(","));
li[i].onclick = CustomLookup;
break;
}
}
}
}
}
}
}

function CustomLookup()
{
var lookupSrc = "/" + ORG_UNIQUE_NAME + "/_controls/lookup/lookupmulti.aspx?class=&objecttypes=" + lookupEntityTypeCode + "&browse=0";
if(lookupId != null && lookupId.DataValue != null && lookupId.DataValue[0] != null)
{
lookupSrc = lookupSrc + "&id=" + lookupId.DataValue[0].id;
}

var lookupItems = window.showModalDialog(lookupSrc, null);
if (lookupItems) // This is the CRM internal JS funciton on \_static\_grid\action.js
{
if ( lookupItems.items.length > 0 )
{
AssociateObjects( crmFormSubmit.crmFormSubmitObjectType.value, crmFormSubmit.crmFormSubmitId.value, lookupEntityTypeCode, lookupItems, true, null, relId);
}
}
}

17 comments:

a33ik said...

Wow! Great work, Jim! This question was asked many times!

Pano said...

Hi Jim,

Thank you for this code, it works well!

The only modification i recommend for anyone wanting to preserve the filter criteria that can be typed into the Multilookup form is that you need strip out the "new_name" filter condition from the PreXML, and add it into your AfterXML as well.


Now it all works just fine for me, except:

If you log into the system via AD, all users can work, but whenever anyone logs into the system via IFD, including the deployment manager, i get an error to the tune of
"Invalid User Authorisation"
"The user authorization passed to the platform is not valid".

I registered the plugin as CALLING USER as per your instruction.

After playing around with the execution context, i found if i register the plugin to execute in a particualar users context, only that user can log into crm and not see the error above, and in that case, the multilookup works as planned.

What could be the problem?

Kind regards
Pano Kappos

Jim Wang said...

Hi Pano,

I should make it clear that this plugin(code prototype) only works for On-Premise deployment. If you want to use it in a OnPremise/IFD deployment, you need to change the code to use the CrmImpersonator() class.

Thank you for testing it.

Cheers,
Jim

Pano said...

Hi Jim,

Thank you so much for the reply, that makes sense.

I will post an example here when i get it working.

Regards,
Pano

harbyherbman said...

For Everquest 2 Cheats, Everquest 2 Dupes, EQ2 Plat cheats, Everquest 2 Bots, Everquest 2 Guides, and Walkthroughs click here

酒店上班請找艾葳 said...

艾葳酒店經紀公司提供專業的酒店經紀, 酒店上班小姐,八大行業,酒店兼職,傳播妹,或者想要打工兼差打工,兼差,八大行業,酒店兼職,想去酒店上班, 日式酒店,制服酒店,ktv酒店,禮服店,整天穿得水水漂漂的,還是想去制服店日領上班小姐,水水們如果想要擁有打工工作、晚上兼差工作兼差打工假日兼職兼職工作酒店兼差兼差打工兼差日領工作晚上兼差工作酒店工作酒店上班酒店打工兼職兼差兼差工作酒店上班等,想了解酒店相關工作特種行業內容,想兼職工作日領假日兼職兼差打工、或晚班兼職想擁有鋼琴酒吧又有保障的工作嗎???又可以現領請找專業又有保障的艾葳酒店經紀公司!

艾葳酒店經紀是合法的公司工作環境高雅時尚,無業績壓力,無脫秀無喝酒壓力,高層次會員制客源,工作輕鬆,可日領現領
一般的酒店經紀只會在水水們第一次上班和領薪水時出現而已,對水水們的上班安全一點保障都沒有!艾葳酒店經紀公司的水水們上班時全程媽咪作陪,不需擔心!只提供最優質的酒店上班,酒店上班,酒店打工環境、上班條件給水水們。心動嗎!? 趕快來填寫你的酒店上班履歷表

水水們妳有缺現領、有兼職缺錢便服店的煩腦嗎?想到日本留學缺錢嗎?妳是傳播妹??想要擁有高時薪又輕鬆的賺錢,酒店和,假日打工,假日兼職賺錢的機會嗎??想實現夢想卻又缺錢沒錢嗎!??
艾葳酒店台北酒店經紀招兵買馬!!徵專業的酒店打工,想要去酒店的水水,想要短期日領,酒店日領,禮服酒店,制服店,酒店經紀,ktv酒店,便服店,酒店工作,禮服店,酒店小姐,酒店經紀人,
等相關服務 幫您快速的實現您的夢想~!!

jane_yu00 said...

Hello Jim,

I would like to make this work for single lookup field. The problem I have is because the single lookup field usual is on the same page of form, and I could not find a way to get the event that call the singlelookup.aspx, like "action" you had for multilookup.aspx.

Your help are appreciated,

Jameel Ur Rahman said...

Hi Jim,

I would like to ask some thing about reporting.
we have crm4.0 deployed and reporting is also working, data connector is there.
we schedule reports and than ssrs subscribe these so reports successfully delivered to mentioned email id in subscription.

my question is that when this report come in inbox containing the link but when we click on it we canot view this report or
any sub report. because the url comes is local i mean dead servers report server. srs running on crm application server on port 8080. how to modify this link to show actual ifd url so user (non-crm users) can view these reports out of crm.

jameelrk08@gmail.com

aspguy said...

Dear Jim ,
I created a new .aspx file, named lookupsingle.aspx , and placed only one DropDown control on it. I also modified the javascript code to avoid java script errors. I replaced the oroginal LookupSingle.aspx with this file and what happens is that it is opened but when I change the selected item in the DropDown control, it disappears !!
This also happens if I use a TreeView control and write something in its NodeExpanded event handler.
Do you have any idea why it happens?

Roger said...

Hi!

Be very careful when using this.

The context filtering of Reporting Services reports are also modified when using this... The entities specified to rewrite the lookup multi filter get their report context filters rewritten as well.

(If the plugin is written as the prototype suggests.)

Nicole said...

I just attended this event.

WIN HP mini Netbooks (5 Netbooks) and 100 Dynamics CRM online subscription (free for 6 months).

The Dynamic Business Week is the event where you can experience the Social Customer Relationship Management (SCRM) community in action. This event offers you a unique opportunity to network with your peers, industry experts and Microsoft Dynamics CRM team members. Share ideas and knowledge— all in one place.

Visit Dynamic Business Week by following this link. http://bit.ly/dsBBc4 .

This event lets you interact with industry experts in the CRM and social CRM space and also with other attendees that are interested in SCRM .

This is a virtual event with social media interaction built from ground up, the Virtual events platform used is http://social27.com and it is based on Windows Azure platform.

http://www.microsoft.com/windowsazure/

CRM Parker said...

CRM .The Easy CRM Software for Outlook. Prophet simplifies contact management software, contact manager, small business Sales CRM Software. Prophet is the easiest CRM software because it is built INTO Outlook vs simply synching with Outlook.

Maria_Rilke said...

The advantage of a Channel management software offers increased in productivity by providing a set of targeted efficiency tools that aim to automate many time consuming tasks inside a business process specially in channel management part.

markpittsnh said...

I want to use your technique with a lookupsingle.aspx and a field on the form. Do you have any tips on how to override lookup event?

Currently, I have

var field = crmForm.all.productid;
if (field)
field.attachEvent('onclick', CustomLookup);

The problem I am facing is that after my custom code is executed, it launches the standard lookupsingle.aspx. How do I avoid this second call to lookupsingle.aspx?

markpittsnh said...

I figured it out.


crmForm.all.productid.onclick = function ()
{

var url = "/CRMDEV3/_controls/lookup/lookupsingle.aspx?class=ProductWithPriceLevel&objecttypes=1024&browse=0&ShowNewButton=1&ShowPropButton=1";

var lookupItem = window.showModalDialog(url, null,'dialogWidth:600px;dialogHeight:600px;resizable:yes');
var objs = lookupItem.items;
var iLength = objs.length;

if (iLength > 0)
{
var lookupData = new Array()
var lookupItem = new Object()
lookupData[0] = objs[0];

crmForm.all.productid.DataValue = lookupData;
}
}

Cheers!

Premroop Surendran said...

Hi Jim,

How can I implement the similar functionality in crm 2011. I want to filter the lookup when clicked on addexisting button, that has 1 to many relationship.

leonerdscrew said...

Filter nodes in TreeView in .NET