Looks like the thing you missed here is the DML update operation. You also have a query inside of a loop (performing queries and DML inside of a loop is bad practice on the Salesforce platform. We're pretty restricted on the number of queries/DML that can be performed in a single transaction.)
Right now, you're changing the value of the record in memory only. That change isn't being persisted by saving it to, for lack of a better word, the database (which is what DML does).
In-memory changes to records are only automatically saved to the database without DML when:
- You're working on records in
Trigger.new
or Trigger.newMap
- In a
before insert
or before update
trigger context
Your trigger is defined on after insert
and after update
contexts, and you're trying to update at least one record that isn't guaranteed to be in trigger.new
or trigger.newMap
, so you need to explicitly perform the DML (at least, with your code as-is).
The biggest concepts on customizing the Salesforce platform can be boiled down to:
- Try to use clicks instead of code, where practical (using the provided "declarative" features. i.e. rollup summary fields, validation, workflow, formula fields, process builder)
- Only have one trigger per SObject
- In code, generally try to use and operate on collections (lists, sets, and maps) instead of individual records
- Keep DML and Queries outside of loops
For one reason or another, we can't have a rollup summary field on Account
that summarizes the related Contact
records, so a purely declarative solution isn't possible1. I do, however, think you can save yourself a bit of trouble by making Primary__c
a formula field.
The general idea is that you use a trigger to find the maximum value of Sum_of_Commissions__c
for the contacts related to a given account, and then the formula field (on the Contact) looks to see whether or not its Sum_of_Commissions__c
matches the maximum value that's stored on the account.
A quick and dirty example of that to get you started:
trigger X on Contact(after insert, after update, before delete){
// To avoid performing a query in a loop, we typically gather things used in the WHERE
// clause in a collection (set, list, or map) by using a simple loop beforehand.
Set<Id> accountIdSet = new Set<Id>();
for(Contact c :trigger.new){
accountIdSet.add(c.AccountId);
}
// Now we can perform the query we want to
// By using the GROUP BY clause and Aggregate Functions, we can have SOQL do the work
// of figuring out what the max value should be
// Using GROUP BY means we'll get a List of AggregateResult back rather than a List of
// SObjects
List<AggregateResult> results = [SELECT AccountId, MAX(Sum_of_Commissions__c) maxCommission FROM Contact WHERE AccountId IN :accountIdSet GROUP BY AccountId];
// Now we can work on getting the accounts that we need to update
// Use another collection to hold the accounts so we can do 1 DML operation after we've
// processed everything
List<Account> accountsToUpdate = new List<Account>();
for(AggregateResult ar :results){
// Another helpful way to avoid queries is to use the SObject constructor to set
// the Id of an SObject.
// As long as the SObject instance has an Id, we can perform a DML update on it.
// A query would get you that Id as well, but queries are a precious resource.
accountsToUpdate.add(new Account(
// AggregateResult is a bit of a pain to work with. We need to use .get()
// to access each field, and it gives us a plain "Object" back that needs
// to be type-cast
Id = (Id)ar.get('AccountId'),
Max_Commission__c = (Decimal)ar.get('maxCommission')
);
}
// Done with all the account work, time to DML
update accountsToUpdate;
}
From there, the formula field you'd put on Contact
would be something simple like Sum_of_Commissions__c = Account.Max_Commission__c
.
This approach could end up marking more than one Contact as "primary" (if more than one contact has the max value). Maybe you're ok with that. If not, then you should still be able to apply the concepts demonstrated in my example (gather Ids first, then query outside of all loops; gather records to be updated in a list, then perform the update outside of all loops; etc...)
1: There is Andrew Fawcett's Declarative Lookup Rollup Summary tool which provides a declarative interface to define rollups for more things than Salesforce give us by default. It automatically generates a trigger and test code. So it's declarative, but backed by code.