We created a hierarchical representation of accounts in CRM that can be added as either a tool bar button or as an IFrame within account. The page can also display an icon next to the account name or it can just display the account name by iteself. The entries link to the actual account records.
Without Icon
With Icon
Query
When pulling hierarchy data, one concern is performance. Each level in the hierarchy would require a call to the CRM webservice. Instead we went with a RecursionCTE query that used the filtered views.
with RecursionCTE (parentaccountid, accountid, [name])
as
(
select R2.parentaccountid, R2.accountid, R2.name from filteredaccount as R2
where r2.accountid = @accountid
UNION ALL
select R1.parentaccountid, R1.accountid, R1.name from filteredaccount as R1
join RecursionCTE as R2 on R1.parentaccountid = R2.accountid
)
select R1.new_pictureurl, R1.accountid, R1.name, case when r1.accountid =@accountid then null else R1.parentaccountid end as parentaccountid
from filteredaccount as R1
JOIN RecursionCTE as R2
on R1.accountid = R2.accountid
as
(
select R2.parentaccountid, R2.accountid, R2.name from filteredaccount as R2
where r2.accountid = @accountid
UNION ALL
select R1.parentaccountid, R1.accountid, R1.name from filteredaccount as R1
join RecursionCTE as R2 on R1.parentaccountid = R2.accountid
)
select R1.new_pictureurl, R1.accountid, R1.name, case when r1.accountid =@accountid then null else R1.parentaccountid end as parentaccountid
from filteredaccount as R1
JOIN RecursionCTE as R2
on R1.accountid = R2.accountid
Adding Nodes to Treeview
I used an ASP.Net TreeView to display the hierarchy. I loaded the data into a dataset using a relationship and iterated through the dataset, adding nodes. I also checked to see that only four levels of nodes are shown. The reset are collapsed so the screen doesn’t become too cluttered.
… … dsResult.Tables[0].TableName = "Account"; DataRelation relation = new DataRelation("ParentChild", dsResult.Tables["Account"].Columns["accountid"], dsResult.Tables["Account"].Columns["parentaccountid"], true); relation.Nested = true; dsResult.Relations.Add(relation);
DataTable dt = dsResult.Tables[0]; DataRow[] rows = dt.Select(string.Format("accountid = '{0}'", acctId)); if (rows != null && rows.Length > 0) { DataRow row = rows[0]; TreeNode node = GetNode(row, true); TreeView1.Nodes.Add(node); DataRow[] childRows = row.GetChildRows(relation); if (childRows.Length > 0) AddChildNodesRecursive(node, childRows, relation, 1); } } }
private void AddChildNodesRecursive(TreeNode parentNode, DataRow[] dataRows, DataRelation relation, int depth) { foreach (DataRow row in dataRows) { TreeNode node = GetNode(row, false); parentNode.ChildNodes.Add(node);
DataRow[] childRows = row.GetChildRows(relation); if (childRows.Length > 0) AddChildNodesRecursive(node, childRows, relation, depth + 1);
if(depth >= _maxTreeDepth - 1) // set to 4 node.Collapse();
} }
Displaying Image and URL
I set the navigate URL to link to the account edit screen for the selected node. There is also an option to set an image URL to display an icon for the account. I added a custom attribute to hold the icon URL. If no URL exists then a default icon can be used.
private static TreeNode GetNode(DataRow row, bool isRoot) { TreeNode node = new TreeNode(); node.Text = " " + row["name"].ToString(); node.Target = "_new"; node.NavigateUrl = string.Format("/{0}/SFA/accts/edit.aspx?id=" + row["accountid"].ToString(), ORG); ;
if (row["inet_imageurl"] == DBNull.Value) // Set to some default Image node.ImageUrl = "/isv/photos/defaultimage.gif"; else node.ImageUrl = row["inet_imageurl"].ToString();
return node;
}
Opening Window without Toolbar and Sized Correctly
I ran into a problem with the way the account window opens. The target is set to _new so it will open in a new window, however you don’t have any control over how the window is displayed (width, height, show toolbar, ect). I had to intercept the button click and use JavaScript to open the new window. Looking back, it would have been better to render the HTML myself rather than relying on the tree view control. However, it does the job.
I had to set the onclick of the TreeView to <asp:TreeView onclick="BLOCKED SCRIPTpostBackByObject()" ID="TreeView1" runat="server" ImageSet="Arrows">… …
function postBackByObject(){if(window.event.srcElement.href != null && window.event.srcElement.href != "" && window.event.srcElement.href.substr(0,4).toLocaleLowerCase() == "http"&& window.event.srcElement.nameProp.substr(0,11).toLocaleLowerCase() != "webresource"){window.open(window.event.srcElement.href,'popup','toolbar=no,location=no,directories=no,status=yes,scrollbars=yes,menubar=no,resizable=yes,width=1000,height=560');}
// clicks that are expanding the treeview come in with webresource - let them occurif(window.event.srcElement.href != null && window.event.srcElement.href != "" && window.event.srcElement.href.substr(0,4).toLocaleLowerCase() == "http"&& window.event.srcElement.nameProp.substr(0,11).toLocaleLowerCase() != "webresource"){ event.returnValue = false; return false;} }
Adding to IFRAME
To add the page as an IFrame add a tab to the account entity and add a section. Next, add an IFRAME. Set "Pass record object type-code and unique identifier as paramters" as true. Also allow cross side scripting.
Set the necessary number of rows and allow scrolling.
The IFrame shows up in a new tab.
Adding to ToolbarYou can also opt to add the item as a toolbar item within account. Simply, add the following segment to the ISVConfig and re-import it.
<ImportExportXml version="4.0.0.0" languagecode="1033" generatedBy="OnPremise">
<IsvConfig>
<configuration version="3.0.0000.0">
<Entities>
<Entity name="account">
<!-- The Account Tool Bar -->
<ToolBar ValidForCreate="0" ValidForUpdate="1">
<Button Icon="/_imgs/treeOn.gif" Url="/ISV/AccountHierarchy/Default.aspx" PassParams="1" WinParams="1" WinMode="2">
<Titles>
<Title LCID="1033" Text="Account Hierarchy" />
</Titles>
<ToolTips>
<ToolTip LCID="1033" Text="View Account Hierarchy" />
</ToolTips>
</Button>
<ToolBarSpacer />
</ToolBar>
<Grid>
<MenuBar>
</MenuBar>
</Grid>
</Entity>
</Entities>
</configuration>
</IsvConfig>
</ImportExportXml>