1. 大家都知道CRM 里面的Lookup 保存了相关实体的GUID,让我们深入的了解一下CRM Lookup。当我们在2个实体间建立关系的时候,CRM自动生成了一些attributes来保存相关实体的信息,虽然我们在CRM定制界面只能看到一个 attribute,也就是保存GUID的那个,其实CRM还创建了一些隐含的attributes来保存其他信息,来看一个例子:
crmForm.all.regardingobjectid.DataValue[ 0 ].name; // The text value of the lookup.
crmForm.all.regardingobjectid.DataValue[ 0 ].typename; // The entity type name.
这也就是为什么我们可以引用除了GUID的其他相关信息,这些隐含的attributes可以通过导出实体的customisation.xml 来看到。
也许有人注意到了,在CRM里,所有user-owned entity(比如 account, contact, case, email etc) 都有一个系统attribute叫做 ownerbusinessunit,这也是个Lookup,但却是系统的Lookup。这个attribute用来记录记录所有者(the owner)所在BusinessUnit的GUID。CRM论坛里一个常见的问题就是怎样得到当前用户的BusinessUnit,最常用的方法是通过AJAX来得到,见我以前的Blog有提。其实我们可以利用ownerbusinessunit来做,方法是导出customisation.xml 进行修改然后导入,这样才可以把ownerbusinessunit加到窗体上。注意这种方法只能得到GUID,不能得到BU's Name,原因上面已经说了,因为是系统的Lookup,CRM没有创建Name这个attribute。到此为止这种方法是比AJAX来的简单,但如果要得到BU's Name就不如食用AJAX了。
2. 我们来看看regardingobjectid这个特殊的Lookup,当用户选择Regarding的时候,也许那个下拉菜单会有很多选项:Account, Contact, Opportunity, Lead.... ,如果我只想显示Account 和Contact,并且把默认值设定为Contact怎么办呢? 在onLoad() 里面写如下语句:
crmForm.all.regardingobjectid.setAttribute( " lookuptypeIcons " , " /_imgs/ico_16_1.gif :/_imgs/ico_16_2.gif " ); // set the icons
crmForm.all.regardingobjectid.setAttribute( " defaulttype " , " 2 " ); // default to contact
除了使用setAttribute 方法,还可以使用CRM自己的方法:
crmForm.all.regardingobjectid.lookuptypeIcons = " /_imgs/ico_16_1.gif:/_imgs/ico_16_2.gif " ;
crmForm.all.regardingobjectid.defaulttype = " 2 " ;
3. 如果你注意一下Lookup的地址URL,你会发现CRM是这样调用的:
/lookupsingle.aspx?class=ActivityRegarding&objecttypes=1,2,3,4&browse=0&ShowNewButton=1&ShowPropButton=1&DefaultType=0
lookupsingle.aspx有一些参数可以被我们所用(ISV, IFRAME):
Objecttypes 实体代码, e.g. Objecttypes = "1, 2" //show account and contact
DefaultType 默认实体代码, e.g. DefaultType = "2" //default to contact
Browse 布尔值, 0 = 显示"查找" 栏; 1 = 浏览模式, 隐藏 "查找" 栏.
ShowNewButton 布尔值, 0 = 隐藏 "新建" 按钮; 1 = 显示 "新建" 按钮.
ShowPropButton 布尔值, 0 = 隐藏 "属性" 按钮; 1 = 显示 "属性" 按钮.
在IFRAME或者ISV里,如果我们不想显示"新建" 按钮,那么可以把URL调用写成:
/lookupsingle.aspx?class=ActivityRegarding&objecttypes=1,2,3,4&browse=0&ShowNewButton=0&ShowPropButton=1&DefaultType=0
但是如果我想在CRM里面隐藏 "新建" 按钮怎么办呢?你不能直接使用 crmForm.all.regardingobjectid.ShowNewButton = 0; 但是可以在onLoad()里使用下面的方法:
显示/隐藏 "新建" 按钮
bShow = 0 : 隐藏 "新建" 按钮
bShow = 1 : 显示 "新建" 按钮
*/
function NewButton(bShow)
{
return function()
{
crmForm.all.regardingobjectid.AddParam("ShowNewButton", bShow);
}
}
crmForm.all.regardingobjectid.attachEvent( " setadditionalparams " ,NewButton( 0 ));
4. 现在我们来看看Lookup的条件过滤,CRM3和CRM4有很大不同,我们通过例子来说明:
我们用的例子是:在公司(Account)记录里的主要联系人(primarycontactid) Lookup列表里只显示和这条公司记录(Account)有关联的联系人(Contact)。
a. CRM 3.0
if (crmForm.FormType == 2 && crmForm.ObjectId != null )
{
crmForm.all.primarycontactid.lookupbrowse = 1;
crmForm.all.primarycontactid.additionalparams = "fetchXml="
+ "<fetch mapping='logical'><entity name='contact'><all-attributes /><filter>"
+ "<condition attribute='accountid' operator='eq' value='" + crmForm.ObjectId + "' />"
+ "</filter></entity></fetch>";
}
b. CRM 4.0
据我目前了解,在CRM4.0里主要有两种unsupported customization可以进行这样的过滤。由于这个例子并不需要复杂的FetchXml查询,所以我们用第一种方法:
(1) 定制联系人(Contact) 实体, 打开 Contacts Lookup View, 点击'添加新的搜索栏'( Add Find Column), 选择parentcustomerid,保存并发布。
(2) 定制公司 (Account) 实体,在onLoad()里面添加如下代码:
if (crmForm.FormType == 2 && crmForm.ObjectId != null )
{
var name = crmForm.all.name.DataValue;
crmForm.all.primarycontactid.additionalparams = 'search=' + name;
}
这里使用到了‘search’参数,它的作用相当于用户在搜索栏填写了搜索内容。这里的搜索内容就是‘公司名’,由于我们在第一步已经将公司名添加到了搜索关键字栏,所以这个lookup将返回我们指定的公司(Account)记录。
现在如果我们把要求改变一下:只显示和这个公司(Account)的上级公司(parentaccountid)有关联的联系人(Contact)。
第一步是一样的,第二步改成如下代码:
FilterLookup = function (source, target)
{
if (IsNull(source) || IsNull(target)) { return; }
var name = IsNull(source.DataValue) ? '' : source.DataValue[0].name;
target.additionalparams = 'search=' + name;
}
上级公司(parentaccountid)也是一个Lookup,我们还要在它的 onChange()里面添加代码:
FilterLookup(crmForm.all.parentaccountid, crmForm.all.primarycontactid);
Ok, 现在问题解决了!
如果我们再改变一下要求:我们希望当用户选择了上级公司(parentaccountid)后,这个上级公司主要联系人(primarycontactid) 自动填充到当前公司(account)的主要联系人里。
虽然我们可以使用 AJAX 技术来达到这个目的,CRM4.0为我们提供了一个更酷的特性:Lookup自动填充(automatic resolutions)功能。
感谢 Adi Katz, 让我们从头开始来实现它:
(1) 关闭上级公司(parentaccountid) 的自动填充功能(automatic resolutions in field),只需要在Form上双击parentaccountid区域,然后在第一页就可以看到选项来取消这个功能。
(2) 在Form.onLoad()里面输入如下代码:
{
var contactLookup = crmForm.all.primarycontactid;
if( contactLookup.DataValue != null ) {return;}
contactLookup.AutoResolve = 1;
var accountLookup = crmForm.all.parentaccountid;
primaryContact = accountLookup.items[0].keyValues.primarycontactid;
contactLookup.SetFocus();
contactDiv = document.all.primarycontactid_d.getElementsByTagName("DIV")[0];
contactDiv.innerText = primaryContact.value;
contactLookup.Lookup( true , true , primaryContact.value , true );
}
function OnCrmPageLoad()
{
crmForm.all.parentaccountid.attachEvent( "onafterselect" , OnAfterAccountSelect );
}
OnCrmPageLoad();
Ok! 所有问题都解决了。一开始提到的CRM 4.0复杂的FetchXml过滤功能将在下一篇blog里详细解释。:)
5. 现在我们来看一个普遍应用的例子:我只想在regardingobjectid的Lookup里列出Open的Case。
a. CRM 3.0
crmForm.all.regardingobjectid.lookuptypes = " 112 " ;
crmForm.all.regardingobjectid.lookuptypeIcons = " /_imgs/ico_16_112.gif " ;
/* only show the active cases : Form.onLoad() */
if (crmForm.ObjectId != null )
{
crmForm.all.regardingobjectid.lookupbrowse = 1;
crmForm.all.regardingobjectid.additionalparams = "fetchXml="
+ "<fetch mapping='logical'><entity name='incident'><all-attributes /><filter>"
+ "<condition attribute='statecode' operator='eq' value='0' />"
+ "</filter></entity></fetch>";
}
b. CRM 4.0
在4.0里,上述功能(additionalparams)不再可以使用,但是我们仍然可以使用上一篇文章提到的技术:打开Case Lookup View, 点击'添加新的搜索栏'( Add Find Column), 选择statecode,保存并发布。然后在Form.onLoad()里面输入如下代码:
crmForm.all.regardingobjectid.lookuptypes = " 112 " ;
crmForm.all.regardingobjectid.lookuptypeIcons = " /_imgs/ico_16_112.gif " ;
crmForm.all.regardingobjectid.additionalparams = ' search=Active ' ;
大家已经看到了,像这种简单的Filter功能用这个技术就可以实现。但是如果想实现复杂一点的呢?
比如我们现在的要求变成了:只列出当前用户的Open Case!
像这样复杂一些的Filter功能我们一般先用高级查找(Advanced Find)来Build 出FetchXml,然后再进行下一步。具体做法在Ronald Lemmen 的Blog里有讲:就是在搜索结果的IE地址栏里输入如下代码:javascript:prompt("", resultRender.FetchXml.value);
这样会弹出一个JavaScript.pormpt窗口,具体到我们的这个例子弹出的FetchXml语句是:
<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false"><entity name="incident"><attribute name="title"/><attribute name="ticketnumber"/><attribute name="createdon"/><attribute name="incidentid"/><order attribute="title" descending="false"/><filter type="and"><condition attribute="statecode" operator="eq" value="0"/><condition attribute="ownerid" operator="eq-userid"/></filter></entity></fetch>
下面介绍的在CRM 4.0里面的FilteredView功能最先是Adi Katz开发的,然后网友George进行了一些改进。我拿改进好的版本给大家演示:(注:下面的定制方法属于非常Unsupported的那种Customization,idea是重载了code-behind 的程序,加一个Filter进去。CRM 4.0 目前只有一种基于Filtered Lookup的 Supported Customization,是Michael Höhne开发的收费插件)
你需要修改这个文件:CRMWeb//_controls//lookup//lookupsingle.aspx,加入下面的代码:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
crmGrid.PreRender += new EventHandler(crmGrid_PreRender);
}
void crmGrid_PreRender( object sender, EventArgs e)
{
if (crmGrid.Parameters["search"] != null && crmGrid.Parameters["search"].StartsWith("<fetch"))
{
crmGrid.Parameters.Add("fetchxml", crmGrid.Parameters["search"]);
crmGrid.Parameters.Remove("searchvalue");
this._showNewButton = false;
}
}
</ script >
然后在
crmForm.all.regardingobjectid.lookuptypes = " 112 " ;
crmForm.all.regardingobjectid.lookuptypeIcons = " /_imgs/ico_16_112.gif " ;
var fetchStr = " <fetch version= " 1.0 " output-format= " xml - platform " mapping= " logical " distinct= " false " ><entity name= " incident " ><attribute name= " title " /><attribute name= " ticketnumber " /><attribute name= " createdon " /><attribute name= " incidentid " /><order attribute= " title " descending= " false " /><filter type= " and " ><condition attribute= " statecode " operator= " eq " value= " 0 " /><condition attribute= " ownerid " operator= " eq - userid " /></filter></entity></fetch> " ;
crmForm.all.regardingobjectid.lookupbrowse = 1 ;
crmForm.all.regardingobjectid.additionalparams = " search= " + fetchStr;