I realized that I wanted to perform just one update operation, and that was preventing me from a solution that updates each opp only once, but might have three update operations. I use the static set processedOppIds to remember which opps have already been processed in the execution context.
The following code works (tested). It is called from the insert, update, and delete triggers. I use the field Opportunity_Team_Notify_Trigger__c to trigger the email in a process builder component.
It would be nice if apex had a way to perform an operation just before the execution context is destroyed. Then I could do just a single update, and I could also create some text that describes all of the changes.
public with sharing class OpportunityTeamNotifyHelper {
@testvisible static Set<Id> processedOppIds = new Set<Id>();
// after insert, after update, after delete
public static void splits(OpportunitySplit[] splits) {
Set<Id> oppIds = gatherIdsToProcess(splits, processedOppIds);
Opportunity[] opps = [select Id, Opportunity_Team_Notify_Trigger__c
from Opportunity
where Id in :oppIds];
for (Opportunity opp : opps) {
opp.Opportunity_Team_Notify_Trigger__c += 1;
}
update opps;
processedOppIds.addAll(oppIds);
}
@testvisible static Set<Id> gatherIdsToProcess(OpportunitySplit[] splits, Set<Id> exceptIds) {
Set<Id> oppIds = new Set<Id>();
for (OpportunitySplit split : splits) {
Id opportunityId = split.OpportunityId;
if (exceptIds.contains(opportunityId) == False) {
oppIds.add(opportunityId);
}
}
return oppIds;
}
}