function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Laura Babb 4Laura Babb 4 

invocable method not updating records as expected

I need to nail down this invocable method (or even convert to a trigger?). Not sure. I am an Admin, not a Developer, so I need help! A kind developer wrote this method for me and I am trying to trigger it via PB, but the contact owner does not update.
The PB is:
Account object (created or edited) >>
Criteria node = Account.Demand_Gen_Owner__c IS CHANGED || Account.OwnerId IS CHANGED >>
Immediate action = Apex Class
global class UpdateContactsFromAccount {
    
    @InvocableMethod(label='save info' description='Update Contact Owners')
    global static void saveInformation (List<Id> accountIds) {
    	List<Account> listAccount = [Select Id, Demand_Gen_Owner__c, OwnerId FROM Account WHERE Id In :accountIds];
        List<Contact> listContact = [Select Id, AccountId, OwnerId FROM Contact WHERE AccountId In :accountIds];
        
        Map<Id, Account> mapIdToAccount = new Map<Id, Account>();
        mapIdToAccount.putAll(listAccount);
        System.debug('Accounts for which Contacts to be updated:: mapIdToAccount : ' + mapIdToAccount);
        for(Contact con: listContact){
            if(con.AccountId != null){
                Account acc = mapIdToAccount.get(con.AccountId);
                if(acc.Demand_Gen_Owner__c != 'Unassigned' || acc.Type != 'Current Customer'){
                	con.OwnerId = acc.Demand_Gen_Owner__c;
                }
                else {
                    con.OwnerId = acc.OwnerId;
                }
            }
        }
        update listContact;
    }
}
Currently this is just a PB in production and it is causing errors intermittently (usually accounts that have a ton of contacts) or also during bulk uploads. I think it's too many contact records that it's trying to update. Also have an issue with an unrelated managed package triggering the process and causing insert/update errors on the package fields (none of these fields are part of my process). I came here with this originally and it was recommended to use an invocable method/apex class to better control the updates. Please help so I can get this into prod! As always, thanks so much!
 
Best Answer chosen by Laura Babb 4
ApuroopApuroop
Got you.

Try this:
global class UpdateContactsFromAccount {
    @InvocableMethod(label='save info' description='Update Contact Owners')
    global static void saveInformation (List<Id> accountIds) {
        User Unassigned = [SELECT Id FROM User WHERE Name LIKE 'Unassigned']; //Change this if needed
    	List<Account> listAccount = [Select Id, Demand_Gen_Owner__c, OwnerId, Type FROM Account WHERE Id =:accountIds];
        List<Contact> listContact = [Select Id, AccountId, OwnerId FROM Contact WHERE AccountId =:accountIds];
        List<Contact> toUpdate = new List<Contact>();
        
        Map<Id, Account> mapIdToAccount = new Map<Id, Account>();
        mapIdToAccount.putAll(listAccount);
        System.debug('Accounts for which Contacts to be updated:: mapIdToAccount : ' + mapIdToAccount);
        for(Contact con: listContact){
            if(con.AccountId != null){
                Account acc = mapIdToAccount.get(con.AccountId);
                if(acc.Demand_Gen_Owner__c != Unassigned.Id || acc.Type != 'Current Customer'){
                	con.OwnerId = acc.Demand_Gen_Owner__c;
                }
                else {
                    con.OwnerId = acc.OwnerId;
                }
            }
        }
        update listContact;
    }
}

On line 15, we need the Type field. This is the error that am seeing as of now and checking against that user created on line 4. Copy that SOQL and execute in the query editor and make sure it is returning the correct ID of that Unassigned user, just in case.

Change this and let me know. Unfortunately I don't have those many contacts on each account in my dev org to do the testing. :(

All Answers

ApuroopApuroop
Hey Laura. I have a few questions before jumping to conclusions.

What is the data type of the field - Demand_Gen_Owner__c?
If it is a lookup to the User object, on line 14 - you can't do that check.
If it is a text field, on line 15 - you can't do that because con.OwnerId is a Lookup field to the user object.
Laura Babb 4Laura Babb 4
Hey Apuroop,

Demand_Gen_Owner__c is a lookup to the User object ("Unassigned" is a user in our instance). The reason I say that Demand Gen Owner does not equal 'Unassigned' is because if Demand Gen Owner DOES equal (or is changed to) Unassigned, then the related contacts should then be owned by the Account Owner. If Demand Gen Owner equals (or is changed to) any other user, then the related contacts should be owned by the Demand Gen Owner.
ApuroopApuroop
Got you.

Try this:
global class UpdateContactsFromAccount {
    @InvocableMethod(label='save info' description='Update Contact Owners')
    global static void saveInformation (List<Id> accountIds) {
        User Unassigned = [SELECT Id FROM User WHERE Name LIKE 'Unassigned']; //Change this if needed
    	List<Account> listAccount = [Select Id, Demand_Gen_Owner__c, OwnerId, Type FROM Account WHERE Id =:accountIds];
        List<Contact> listContact = [Select Id, AccountId, OwnerId FROM Contact WHERE AccountId =:accountIds];
        List<Contact> toUpdate = new List<Contact>();
        
        Map<Id, Account> mapIdToAccount = new Map<Id, Account>();
        mapIdToAccount.putAll(listAccount);
        System.debug('Accounts for which Contacts to be updated:: mapIdToAccount : ' + mapIdToAccount);
        for(Contact con: listContact){
            if(con.AccountId != null){
                Account acc = mapIdToAccount.get(con.AccountId);
                if(acc.Demand_Gen_Owner__c != Unassigned.Id || acc.Type != 'Current Customer'){
                	con.OwnerId = acc.Demand_Gen_Owner__c;
                }
                else {
                    con.OwnerId = acc.OwnerId;
                }
            }
        }
        update listContact;
    }
}

On line 15, we need the Type field. This is the error that am seeing as of now and checking against that user created on line 4. Copy that SOQL and execute in the query editor and make sure it is returning the correct ID of that Unassigned user, just in case.

Change this and let me know. Unfortunately I don't have those many contacts on each account in my dev org to do the testing. :(
This was selected as the best answer
Laura Babb 4Laura Babb 4
I added in [SELECT Id FROM User WHERE Name LIKE 'Unassigned' and IsActive = TRUE] because there is an inactive user as well. This produces the correct Id. This won't limit the update to only Demand Gen Owner = Unassigned, correct? Also, when I'm using this in PB, I'm not sure what (if any) apex variables to set. Is that necessary? Again, really appreciate your help!
Laura Babb 4Laura Babb 4
And, just updating the code did not update the contact owner as expected.
ApuroopApuroop
I didn't understand - This won't limit the update to only Demand Gen Owner = Unassigned, correct?

We are trying to make the Demand_Gen_Owner a contact owner if the Demand_Gen_Owner is NOT an Unassigned user OR Account type can be anything except Current Customer, correct me if am wrong.

Since it's an update on the related object - you wouldn't need to set any Invocable variables as far as I think. All you need is the UserId which we can get it in the code.

So, after updating, is it the same error that you were facing before or it's just not updating the contact owner?
ApuroopApuroop
I just tested at the UI level for 3 records, it was working according to your criteria and the if conditions in the class. Are you sure you aren't missing some small details?
Laura Babb 4Laura Babb 4
Correct, but on the other hand, if the Demand Gen Owner IS changed to Unassigned, then the contact owner needs to update with the Account Owner.

It's not an error, it's just not updating.
ApuroopApuroop
Is the Account Type = Current Customer?
Laura Babb 4Laura Babb 4
No... What is your criteria for the action group? Mine is: Account object (created or edited) >>
Criteria node = Account.Demand_Gen_Owner__c IS CHANGED || Account.OwnerId IS CHANGED >>
Immediate action = Apex Class (no apex variables set)
ApuroopApuroop
You need set the accountIds variable to the current record that is going through the process. It should look like below:

User-added image
Earlier I thought you were asking about additional apex variables, my bad.
Laura Babb 4Laura Babb 4
Awesome! That worked! However, when I change the Demand Gen Owner to 'Unassigned', it changes the contact owner to 'Unassigned' versus changing it to the Account Owner. How do I modify that?
Laura Babb 4Laura Babb 4
There is also one other criteria I need... There is a field on the contact record called 'Sales Requested Follow Up' (checkbox). If this is checked, it assigns the specific contact to Account Owner (not all related contacts). Where would I add that parameter?
ApuroopApuroop
When I have the Type = Current Customer and  change the Demand Gen Owner to 'Unassigned', the contact owner is changed to Account owner.

Did you mean assign the specific contact to an Account? (not Account Owner) If you think this requirement should be carried out along with this then obviously in the Invocable class. Otherwise, I'd go for a trigger.
Laura Babb 4Laura Babb 4
So 2 fold. If the Account Type equals (or changes to) Current Client then related contacts' owner changes to Account Owner || if Demand Gen Owner changes to Unassigned, then related contacts' owner changes to Account Owner. Both need not be true at the same time.

And with the contact checkbox, Sales Requested Follow Up, if that box is checked, specific contact (not all related contacts) should change to Account Owner. Assuming the account is not a current client and has a Demand Gen Owner populated (not Unassigned), there could be potentially a mix of contacts owned by the account owner (have the sales requested follow up box checked) and contact owned by the demand gen owner (checkbox is false).

Is this possible to incorporate in the invocable class?
ApuroopApuroop
This can be done with a new invocable class along with a new criteria in the process builder, because you said both the expressions (1st para) should be true, you would want to use AND operation. The earlier one that we dealt with so far is using an OR operator.

Create a new invocable method in the same class with @Invocablemethod and perform your logic. The only reason we aren't going for trigger is because you're looking for a change in only teo particular fields.

Also, mark a best answer if you think the question is solved. :)
Laura Babb 4Laura Babb 4
It would be OR - both do not need to be true. I will mark this as solved also, thank you!
ApuroopApuroop
I'd say create a separate @InvocableMethod in the same class and create a new Immediate Action (Apex) in the process builder. This would eliminate a bit of confusion I reckon. Good luck! :)