Account Hierarchy Component for Salesforce Lightning

Salesforce accounts can be grouped together in something called an account hierarchy. Each account can have a parent account, and then that parent can have a parent, and so on. In this way you can create a tree structure for a set of related accounts.

A simple hierarchy diagram

In Salesforce Lightning you can view the account hierarchy by clicking on a button next to the account name. Unfortunately, this button is displayed on all accounts, even ones that don’t have a parent or children. The only way to tell if the account is a part of a hierarchy is by clicking on the button and viewing the hierarchy page.

Salesforce's account hierarchy indicator shows up even for a single account


I wanted to replace the hierarchy button with a Lightning Component that would only display if the account was part of a hierarchy. And – since this would be a component and I’d have a bit more real estate to work with (compared to a button) – I wanted to indicate if the account had a parent or children (and how many).

With that in mind I created the Account Hierarchy Lightning Component. It looks like this:

A new, better way to show if an account is part of a hierarchy


With all of that out of the way, here’s all of the code you’ll need to create your own account hierarchy component for Salesforce Lightning:


AccountHierarchy.cmp (Component):

<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId" controller="AccountHierarchyController">

    <!-- Atributes-->
    <aura:attribute name="hasParent" type="boolean" default="false"/>
    <aura:attribute name="numChildren" type="integer" default="0" />
    <aura:attribute name="childOrChildren" type="string" default="children" />
    <aura:attribute name="headerTitle" type="Aura.Component[]">
        <h2>
            <b>This account is part of a hierarchy.</b>
        </h2>
    </aura:attribute>

    <!-- Handlers -->
    <aura:handler name="init" value="{!this}" action="{!c.init}" />


    <aura:if isTrue="{!or(v.hasParent, v.numChildren > 0)}">
    <lightning:card title="{!v.headerTitle}" iconName="standard:hierarchy" class="slds-card_boundary">
        <div class="slds-card__body_inner">
            This account has
            <aura:if isTrue="{!and(v.hasParent, v.numChildren > 0)}">
                a parent and {!v.numChildren}&nbsp;{!v.childOrChildren}.
                <aura:set attribute="else">
                    <aura:if isTrue="{!v.hasParent}">a parent. </aura:if>
                    <aura:if isTrue="{!v.numChildren > 0}">{!v.numChildren}&nbsp;{!v.childOrChildren}. </aura:if>
                </aura:set>
            </aura:if>
            <a onclick="{!c.navigateToAccountHierarchy}">View the Account Hierarchy</a>.
        </div>
    </lightning:card>
    </aura:if>
</aura:component>


AccountHierarchyController.js (Controller):

({
    init : function(component, event, helper) {
        helper.checkForParent(component, event, helper);
        helper.checkForChildren(component, event, helper);
    },

    navigateToAccountHierarchy: function(component, event, helper) {
        var acctId = component.get('v.recordId');
        var evt = $A.get("e.force:navigateToComponent");
        evt.setParams({
            componentDef: "sfa:hierarchyFullView",
            componentAttributes: {
                recordId: acctId,
                sObjectName: "Account"
            }
        });
        evt.fire();
    }
})


AccountHierarchyHelper.js (Helper):

({
    checkForParent : function(component, event, helper) {
        var action = component.get("c.hasParent");
        var acctId = component.get('v.recordId');
        action.setParams({"accountId": acctId});

        action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                var result = response.getReturnValue();
                component.set("v.hasParent", result);
            }
        });
        $A.enqueueAction(action);
    },

    checkForChildren : function(component, event, helper) {
        var action = component.get("c.numChildren");
        var acctId = component.get('v.recordId');
        action.setParams({"accountId": acctId});

        action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                var result = response.getReturnValue();
                component.set("v.numChildren", result);
                if(result == 1) {
                    component.set("v.childOrChildren", "child");
                } else {
                    component.set("v.childOrChildren", "children");
                }
            }
        });
        $A.enqueueAction(action);
    }
})


AccountHierarchyController.apxc:

public class AccountHierarchyController {

    @AuraEnabled
    public static Boolean hasParent(String accountId) {
        List<Account> listAccounts = [SELECT ParentId FROM Account WHERE Id = :accountId LIMIT 1];
        Account acct = listAccounts[0];
        return acct.ParentId != null;
    }

    @AuraEnabled
    public static Integer numChildren(String accountId) {
        List<Account> listAccounts = [SELECT Id FROM Account WHERE ParentId = :accountId];
        return listAccounts.size();
    }
}


AccountHierarchyControllerTest.apxc:

@isTest
public class AccountHierarchyControllerTest {

	static testMethod void testHasParent() {
        Account acct = new Account(Name='Test');
        insert acct;

        Account acct2 = new Account(Name='Test2');
        acct2.ParentId = acct.Id;
        insert acct2;

        System.assertEquals(AccountHierarchyController.hasParent(acct.Id), False);
        System.assertEquals(AccountHierarchyController.hasParent(acct2.Id), True);
    }

    static testMethod void testNumChildren() {
        Account acct = new Account(Name='Test');
        insert acct;

        Account acct2 = new Account(Name='Test2');
        acct2.ParentId = acct.Id;
        insert acct2;

        System.assertEquals(AccountHierarchyController.numChildren(acct.Id), 1);
        System.assertEquals(AccountHierarchyController.numChildren(acct2.Id), 0);
    }
}

You may also want to disable Salesforce’s default account hierarchy button. After all, if you have the component, you don’t need a second way to get to the hierarchy page.

Thankfully Salesforce provides a way to turn it off. Here’s what you need to do:

Success! No more account hierarchy button. And you now have a fancy new component to use.