Display CRM Business Unit Organization Hierarchy in a TreeView

Hi guys,

I've see several people and customer requested this feature in CRM.
The hiearchical view of BU in CRM is not something provided out of the box and I don't understand why because it could be very handy.

The CRM SDK offers the possibility to do this using the RetrieveBusinessHierarchyBusinessUnitRequest() and RetrieveBusinessHierarchyBusinessUnitResponse() Classes.

The Response contains the BusinessEntityCollection we are interested in. This is a collection of instances of the businessunit class.

Question is now: How will be do the sort and triage of all the BusinesUnits retrieved in order to find the correct hiearchy?, which BU is the child of which one?, etc........

1°) Each BusinessUnit object has several properties, we will use these properties to identify and test which BU is the child of which one.

We will only need 3 of these for the sorting and display.

businessunitid Specifies the ID of the business unit. (we will use it to identify the BU we are currently working on)
name Specifies the name of the business unit.(we will use it for display in the TreeView)
parentbusinessunitid

Specifies the ID for the parent business unit. (we will use it to retrieve Parent/Child links between BUs)

2°) We will perform the operation in 2 steps:

-> Retrieve the Root BusinessUnit

-> Retrieve all the Childs hiearchy from this point using a nested loop.
for each businnesunit retrieved, we will check the parentbusinessunit property, if this property matches the businessunitid of the current businees unit, we found its child. This is as simple as that.

3°) For each BU we will retrieve we will create a new node in our TreeView (we can add a pictures for all the BU for a cool display :-))

4°) Here is how the final app is supposed to look like:

App Screenshot

(Just create a Windows Form, add a TreeView and Button control to it)

Now the code sample:

using MSCRM30RetrieveBUHierarchy.crmsdk; // crmsdk is the name I choose for the CRM WebService Reference, don't forget that :-)

// Classic.... just creating a function to Init my WebService instance.
public CrmService InitSvc()
{
CrmService csvc = new CrmService();
csvc.Credentials = System.Net.CredentialCache.DefaultCredentials;
csvc.PreAuthenticate = true;
csvc.UnsafeAuthenticatedConnectionSharing = true;
return csvc;
}

// This function implements the test logic to find which are the childs of the current BU.
public void GetChildBu(Guid myBUid, TreeNode myBUNode,RetrieveBusinessHierarchyBusinessUnitResponse allBu)
{
foreach (businessunit bu in allBu.BusinessEntityCollection.BusinessEntities)
{
if(bu.parentbusinessunitid!=null) // we skeep the root bu.....
{
Guid currentBUid = bu.businessunitid.Value;
Guid currentBUparentBUid = bu.parentbusinessunitid.Value;

if(currentBUparentBUid == myBUid)
{
// Add each child to the treeview.
TreeNode myBUNode2 = myBUNode.Nodes.Add(bu.name.ToString());
// Loop again into the function........
GetChildBu(currentBUid,myBUNode2,allBu);
}
}
}
}

// This is the MAIN function needed to be triggered by the Button on your form.
public void RetrieveHierachy()
{
// Init the WebService......
CrmService mysvc = InitSvc();

// Define the columnset, we only care about 3 attributes: name, busineesunitid and parentbusinessunitid
ColumnSet cols = new ColumnSet();
cols.Attributes = new string[] {"name","businessunitid","parentbusinessunitid"};

// Prepare the query to retrieve the BusinessUnit collection...
RetrieveBusinessHierarchyBusinessUnitRequest retrieve = new RetrieveBusinessHierarchyBusinessUnitRequest();
retrieve.ColumnSet = cols;

// Here you need to pass the Root BusinessUnitId you can find within MSCRMDb from a simple SQL Query
// SELECT BusinessUnitId FROM BusinessUnitBase WHERE CREATEDBY IS NULL
retrieve.EntityId = new Guid("A18918BE-8E63-DA11-BC4B-0003FF0D54C9");

// The collection will be now retrieved.......
RetrieveBusinessHierarchyBusinessUnitResponse retrieved = (RetrieveBusinessHierarchyBusinessUnitResponse)mysvc.Execute(retrieve);

// Init the root BU node of our treeview.
TreeNode rootNode = null;

// Retrieve the rootBU
foreach (businessunit bu in retrieved.BusinessEntityCollection.BusinessEntities)
{
if(bu.parentbusinessunitid==null) // if no parent, we have the root!
{
// we get the RootBU then
string rootBUName = bu.name.ToString();

// we create the RootBU node on our treeview.
rootNode = treeView.Nodes.Add(rootBUName);
}
}

// Retrieve the other BU......
GetChildBu(retrieve.EntityId,rootNode,retrieved);
}

// And this of course :-)
private void button1_Click(object sender, System.EventArgs e)
{
RetrieveHierachy();
}

You can download the project file here.

Hope this helps

Benjamin

Comments


// Here you need to pass the Root BusinessUnitId you can find within MSCRMDb from a simple SQL Query // SELECT BusinessUnitId FROM BusinessUnitBase WHERE CREATEDBY IS NULL // retrieve.EntityId = new Guid("C1D98250-C0DE-DB11-BA56-0003FFA73EB9"); // retrieve.EntityId = new Guid("B1170AE7-0B9F-DA11-8444-0003FF166C81"); // cl: using fetchxml for that job... const string BUSINESSUNITID = "businessunitid"; string ls_fetchroot = string.Format(@"<fetch mapping=""logical"">                  <entity name=""businessunit"">                     <attribute name=""{0}""/>                    <filter type=""and"">                        <condition attribute=""createdby"" operator=""null""/>                    </filter>                  </entity>               </fetch>",BUSINESSUNITID); String ls_rootxml = mysvc.Fetch(ls_fetchroot); System.Xml.XmlDocument lo_mydoc = new System.Xml.XmlDocument(); lo_mydoc.LoadXml(ls_rootxml); // some tests should be performed, whether everything is ok here... retrieve.EntityId = new Guid(lo_mydoc.SelectSingleNode("//resultset/result/" + BUSINESSUNITID).InnerXml);

  • Anonymous
    June 09, 2007
    Christian,This is great, thank you so much to contribute!
  • Anonymous
    October 05, 2007
    How can I show all contacts in a Business Unit?Thanks,Rodney Viana
  • Anonymous
    October 08, 2007
    Hallo Rodney!In principle, showing contacts is possible with retrieving the contacts via fetchxml. I changed the code a litte bit. The codesnippet shown replaces the getchildbu method and the initalisation of the service in the 'MAIN' function retrivebeuhierarchy.I also added a new picture in the imagelist1 object, in order to distinguish between a businessunit and a contact.But: this can be quite slow, because all contacts are retrieved. This can be a great amount. The better approach would be to have an event on the node. EG when rightclicked, load all contacts and show them..But may be, this idea helps a little bit.---cut here---cut here---cut here---
        string is_contactfetchpattern = @&quot;&lt;fetch mapping=&quot;&quot;logical&quot;&quot;&gt;
                     <entity name=""contact"">                    <attribute name=""fullname""/>                   <filter type=""and"">                       <condition attribute=""owningbusinessunit"" operator=""eq"" value=""{0}""/>                   </filter>                 </entity>              </fetch>";
        CrmService mysvc;    // This function implements the test logic to find which are the childs of the current BU.    public void GetChildBu(Guid myBUid, TreeNode myBUNode,RetrieveBusinessHierarchyBusinessUnitResponse allBu)    {        System.Xml.XmlDocument lo_tmp = new System.Xml.XmlDocument();        TreeNode tn_tmp;        foreach (businessunit bu in allBu.BusinessEntityCollection.BusinessEntities)        {            if(bu.parentbusinessunitid!=null) // we skeep the root bu.....            {                Guid currentBUid = bu.businessunitid.Value;                Guid currentBUparentBUid = bu.parentbusinessunitid.Value;                if(currentBUparentBUid == myBUid)                {                    // Add each child to the treeview.                    TreeNode myBUNode2 = myBUNode.Nodes.Add(bu.name.ToString());                    // add the contacs for this bu...                    lo_tmp.LoadXml( mysvc.Fetch(string.Format(is_contactfetchpattern,currentBUid)));                    foreach (System.Xml.XmlNode anode in lo_tmp.SelectNodes(&quot;//resultset/result&quot;))                    {                        tn_tmp = myBUNode.Nodes.Add(anode.FirstChild.FirstChild.InnerText );                        tn_tmp.ImageIndex=1;                    }                    // Loop again into the function........                    GetChildBu(currentBUid,myBUNode2,allBu);                }            }        }    }    // This is the MAIN function needed to be triggered by the Button on your form.    public void RetrieveHierachy()    {        // Init the WebService......                        mysvc = InitSvc();
    ---cut here---cut here---cut here---
  • Anonymous
    October 09, 2007
    Hi Christian,Thanks you very much for your time. I have solved the problem already. I have used a query to fetch only the users where owningbusinessunit is the current business unit like you did. My code is for ASP.NET and slightly different, but I am sharing the code (it can be easily ported to Windows form) since your answer to this post explaining how to get the root business object helped me a lot to finish my project. Please let me know what you think. I am new to CRM and your feedback will be very important. I found out how to create a link to the account but not to the business unit. Do you know how to do this?   private void GetContacts(string BusinessID, TreeNode myBUNode, bool hasChildren)   {       string fetchXml = String.Format(@"
                &lt;fetch mapping=&quot;&quot;logical&quot;&quot;&gt;            &lt;entity name=&quot;&quot;contact&quot;&quot;&gt;            &lt;attribute name=&quot;&quot;contactid&quot;&quot; /&gt;            &lt;attribute name=&quot;&quot;fullname&quot;&quot; /&gt;            &lt;attribute name=&quot;&quot;owningbusinessunit&quot;&quot; /&gt;            &lt;filter&gt;            &lt;condition attribute=&quot;&quot;owningbusinessunit&quot;&quot; operator=&quot;&quot;eq&quot;&quot; value=&quot;&quot;{0}&quot;&quot; /&gt;            &lt;/filter&gt;            &lt;/entity&gt;            &lt;/fetch&gt;            &quot;, &quot;{&quot; + BusinessID + &quot;}&quot;);
           String xmlResult = mysvc.Fetch(fetchXml);       System.Xml.XmlDocument xmlContacts = new System.Xml.XmlDocument();       xmlContacts.LoadXml(xmlResult);       bool child = hasChildren;       foreach (System.Xml.XmlNode xn in xmlContacts.SelectNodes("//resultset/result"))       {           child = true;           TreeNode node = new TreeNode(xn.ChildNodes[1].InnerText, null, null, String.Format("http://crm/sfa/conts/edit.aspx?id={0}", xn.ChildNodes[0].InnerText), null);           myBUNode.ChildNodes.Add(node);       }       if (!child)       {           TreeNode node = new TreeNode("(no contact)");           myBUNode.ChildNodes.Add(node);       }   }   // This function implements the test logic to find which are the childs of the current BU.   public bool GetChildBu(Guid myBUid, TreeNode myBUNode, RetrieveBusinessHierarchyBusinessUnitResponse allBu)   {       bool child = false;       foreach (businessunit bu in allBu.BusinessEntityCollection.BusinessEntities)       {           if (bu.parentbusinessunitid != null) // we skip the root bu.....           {               Guid currentBUid = bu.businessunitid.Value;               Guid currentBUparentBUid = bu.parentbusinessunitid.Value;               if (currentBUparentBUid == myBUid)               {                   child = true;                   // Add each child to the treeview.                   TreeNode myBUNode2 = new TreeNode(bu.name.ToString());                   myBUNode.ChildNodes.Add(myBUNode2);                   // Loop again into the function........                   bool hasChildren = GetChildBu(currentBUid, myBUNode2, allBu);                   GetContacts(bu.businessunitid.Value.ToString(), myBUNode2, hasChildren);               }           }       }       return child;   }
  • Anonymous
    October 11, 2007
    Hallo Rodney!We obviously both found the same solution on the Problem. So i am not wrong with it ;-). In Fact, it should be ok to search the contacts while running recursively through the tree.Have you ever thougt about showing this type of information in an hyperbolic view like e.g. http://hypergraph.sourceforge.net/index.html ? One could get all connections from contacts to accounts to businessunits over this view. Away from the standardtreedisplay. Ok. This does not fit perfectly, because one contact may not be related to many BUs or accounts. But this could help for relationships...Concerning the businessunitdisplay: you are right. I did not find a standardway to display the information.  I think this is driven by the fact, that single users should not be able to change the BU of an object within crm (directly), because the BU could drive the distribution of the data. And there is no way to disallow editing a single attribute or relationship in the standardviews. So i think, one has to create a mask byself... (but i did not make any research on this topic)ciao, christianPS you can also contact me via email if you like: christian.leverenz (at) orbis.de (or private by christian (at) leverenz.de ;-) )
  • Anonymous
    October 11, 2007
    while washing hands, i had an idea for the businessunits. The crm system itself uses something like:http://localhost/biz/business/edit.aspx?id={A94DDCA3-B5A2-DA11-8FEA-0003FF166C81}But this is definitely undocumented and therefore unsupported ;-)))cheers, christian
  • Anonymous
    October 14, 2007
    Christian,Thanks for the support and new ideas. The business unit thing is really undocumented, I am saving this info. I finished the project and the client told me he wanted a hierarchy of relationships instead. I already delivered the new requirement. The strange thing is that I knew nothing about CRM before. Now I am feeling confortable. I am working in a new video for my blog that will join CRM and MOSS 2007. I will let you know when I post it. Thanks I will keep in touch.