We have a existing workflow rule on Account
that fires whenever record is created or updated. It does have a condition but it executes every time.
Below is the Workflow criteria:
Current User: Bypass Workflow EQUALS False
and it updates below field on Account
object by calculating formula:
/* Global Parent */
IF(ISBLANK(Parent.Id), Name,
/* 1st Tier */
IF(ISBLANK(Parent.Parent.Id), Parent.Name,
/* 2nd Tier */
IF(ISBLANK(Parent.Parent.Parent.Id), Parent.Parent.Name,
/* 3rd Tier */
IF(ISBLANK(Parent.Parent.Parent.Parent.Id), Parent.Parent.Parent.Name,
/* 4th Tier */
IF(ISBLANK(Parent.Parent.Parent.Parent.Parent.Id), Parent.Parent.Parent.Parent.Name,
/* 5th Tier */
IF(ISBLANK(Parent.Parent.Parent.Parent.Parent.Parent.Id), Parent.Parent.Parent.Parent.Parent.Name,
"Account has too many tiers"))))))
the above workflow rule works as expected, but the reason that i want to convert this workflow to apex is because, when I have a large account hierarchy, multiple times update operation is performed. As I am updating value on workflow, it repeatedly calls before and afterUpdate operation that exceeds the CPU time limit. If I can convert this workflow to apex, it will follow unique order of execution will save CPU Time somehow.
I have written below apex code
public static void PopulateTopLevelAccountText(Account acc,Account oldAcc)
{
system.debug('Account acc:'+acc);
Id TopID;
String Name;
User u=[Select Id,BypassWorkflow__c from User where Id=:UserInfo.getUserId()];
if(u.BypassWorkflow__c==false)
{
system.debug('UltimateParentAccountId__c'+acc.UltimateParentAccountId__c);
TopID = acc.UltimateParentAccountId__c;
system.debug('Top Levele Acc Id:'+TopID);
}
Name = [Select Name from Account where Id=:TopId].Name;
system.debug('Acc Name:'+Name);
Account ac = [Select id,TopLevelAccountText__c,Name from Account where Id=:acc.Id];
system.debug('Ac Name'+ac);
ac.TopLevelAccountText__c = Name;
update ac;
}
Field UltimateParentAccountId__c
that I am using is another formula field present on Account
object. It contains below formula:
/* Global Parent */
IF(ISBLANK(Parent.Id), Id,
/* 1st Tier */
IF(ISBLANK(Parent.Parent.Id), Parent.Id,
/* 2nd Tier */
IF(ISBLANK(Parent.Parent.Parent.Id), Parent.Parent.Id,
/* 3rd Tier */
IF(ISBLANK(Parent.Parent.Parent.Parent.Id), Parent.Parent.Parent.Id,
/* 4th Tier */
IF(ISBLANK(Parent.Parent.Parent.Parent.Parent.Id), Parent.Parent.Parent.Parent.Id,
/* 5th Tier */
IF(ISBLANK(Parent.Parent.Parent.Parent.Parent.Parent.Id), Parent.Parent.Parent.Parent.Parent.Id,
NULL))))))
If you compare both formula fields, there is only difference is in workflow field update. I am calculating Name, and in this formula field I am calculating ID.
and I am calling this method on AfterInsert and AfterUpdate trigger.
Let's say If I have a Top Level Account 'ABC' contains 200 child accounts. If want to make ABC as child account of newly created parent account 'DEF' then on 'ABC' account it does not update any value on TopLevelAccountText__c
field that I am updating via apex code. With workflow rule, it updates perfectly fine for child and their grandchildren accounts. If 'ABC' is Top level Account, then 'DEF' text value should be updated on 'ABC' and all their 200 Child accounts.
Can someone help me what is wrong in the apex logic?
DEF(Top level Account)
---ABC(Child of DEF)
--ABC1
---ABC2 till 200 accounts (Child of ABC)
I also want to call this method on Delete operation. Let's say if I delete Top Level Account 'DEF' then TopLevelAccountText__c
field should get updated with name as'ABC' in 'ABC' account as well as all their 200 child accounts.
Updated Code:
Below is the query that I have designed:
public static List<Account> getAllChildrenIncludingParent(Account acc) {
List<Account> result = [
select Id,Name,Parent.Id,Inactive__c,UltimateParentAccountId__c from Account where Id=:acc.Id
or Parent.Id = :acc.Id
or Parent.Parent.Id = :acc.Id
or Parent.Parent.Parent.Id = :acc.Id
or Parent.Parent.Parent.Parent.Id = :acc.Id
or Parent.Parent.Parent.Parent.Parent.Id = :acc.Id
];
return result;
}
Below is the modified apex method that updates Top level Account Name on their children.
public static void PopulateTopLevelAccountText(Account acc,Account oldAcc)
{
List<Account> accs=new List<Account>();
List<Id> aId=new List<Id>();
List<Account> aId1=new List<Account>();
accs=AccountServices.getAllChildrenIncludingParent(acc);
String Name;
User u=[Select Id,BypassWorkflow__c from User where Id=:UserInfo.getUserId()];
if(u.BypassWorkflow__c==false)
{
if(accs.size()>0){
for(Account a:accs){
TopID=a.UltimateParentAccountId__c;
aId.add(a.Id);
}
}
}
//Get the Name of Top Level Account
Name=[Select Name from Account where Id=:TopId].Name;
for(Account ac:[Select id,TopLevelAccountText__c,Name,UltimateParentAccountId__c from Account where Id IN:aId]){
ac.TopLevelAccountText__c=Name;
aId1.add(ac);
}
update aId1;
}
--Comments from Derek:
As for further recommendations, your new query will only grab 5 levels of your hierarchy (which may not be your entire hierarchy). If you've studied the use cases of this code thoroughly, and have determined that you will only need to support up to 5 levels of hierarchy, then this will work ok. In most situations though, I'd say that the far better query would be one that doesn't rely on parentId = :value OR parent.parentId = :value ...