1. 大家都知道CRM 里面的Lookup 保存了相关实体的GUID,让我们深入的了解一下CRM Lookup。当我们在2个实体间建立关系的时候,CRM自动生成了一些attributes来保存相关实体的信息,虽然我们在CRM定制界面只能看到一个 attribute,也就是保存GUID的那个,其实CRM还创建了一些隐含的attributes来保存其他信息,来看一个例子:
crmForm.all.regardingobjectid.DataValue[
0
].id;
//
The GUID of the lookup.
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(
"
lookuptypes
"
,
"
1,2
"
);
//
only show account and contact
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.lookuptypes
=
"
1,2
"
;
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
/**/
/* CRM 3.0: only show account owned contacts in the primarycontactid lookup : Form.onLoad() */
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()里面添加如下代码:
/**/
/* CRM 4.0: only show account owned contacts in the primarycontactid lookup : Form.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)。
第一步是一样的,第二步改成如下代码:
/**/
/* CRM 4.0: only show parent account owned contacts in the primarycontactid lookup : Form.onLoad() */
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()里面添加代码:
/**/
/* CRM 4.0: only show parent account owned contacts in the primarycontactid lookup : parentaccountid.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()里面输入如下代码:
function
OnAfterAccountSelect()

{
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
/**/
/* set the regarding to open case : Form.onLoad() */
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()里面输入如下代码:
/**/
/* set the regarding to open case : 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,加入下面的代码:
<
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)

{
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
>
然后在
/**/
/* set the regarding to open case which owned by current user : Form.onLoad() */
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;