Intermediate 30 min read

Bulkify the Logic

By the end of this lesson, you'll be able to:

  • Collect the Account Id and Amount delta for every affected Opportunity
  • Query and update the affected Accounts in a single bulk operation
  • Apply Module 25's "collect, then act once" pattern to this specific feature

Prerequisites: "Build the Trigger Handler"

Collecting the amount to adjust, per Account

public void afterUpdate(List<Opportunity> newOpportunities, Map<Id, Opportunity> oldMap) {
    Map<Id, Decimal> amountAdjustmentByAccountId = new Map<Id, Decimal>();

    for (Opportunity opp : newOpportunities) {
        Opportunity oldOpp = oldMap.get(opp.Id);
        Boolean justWon = opp.StageName == 'Closed Won' && oldOpp.StageName != 'Closed Won';
        Boolean justUnwon = opp.StageName != 'Closed Won' && oldOpp.StageName == 'Closed Won';
        Decimal amount = opp.Amount == null ? 0 : opp.Amount;

        if (justWon) {
            addAdjustment(amountAdjustmentByAccountId, opp.AccountId, amount);
        } else if (justUnwon) {
            addAdjustment(amountAdjustmentByAccountId, opp.AccountId, -amount);
        }
    }
    // ... apply the adjustments (next section)
}

private void addAdjustment(Map<Id, Decimal> adjustments, Id accountId, Decimal amount) {
    if (! adjustments.containsKey(accountId)) {
        adjustments.put(accountId, 0);
    }
    adjustments.put(accountId, adjustments.get(accountId) + amount);
}

This is Module 16's "Maps of sObjects" pattern combined with Module 6's ternary operator (defaulting a null Amount to 0) — one entry per affected Account, even if several Opportunities for the same Account changed in this one transaction.

Applying the adjustments in one bulk update

private void applyAdjustments(Map<Id, Decimal> amountAdjustmentByAccountId) {
    if (amountAdjustmentByAccountId.isEmpty()) {
        return;
    }

    List<Account> accounts = [
        SELECT Id, Total_Won_Revenue__c
        FROM Account
        WHERE Id IN :amountAdjustmentByAccountId.keySet()
    ];

    for (Account acc : accounts) {
        Decimal current = acc.Total_Won_Revenue__c == null ? 0 : acc.Total_Won_Revenue__c;
        acc.Total_Won_Revenue__c = current + amountAdjustmentByAccountId.get(acc.Id);
    }

    update accounts;
}

This is Module 25's core pattern applied directly: one query (WHERE Id IN :...keySet(), Module 16's keySet()) and one update, regardless of whether 1 or 200 Opportunities changed in this transaction — exactly what makes this feature bulk-safe.

Exercise

As a comment, explain why amountAdjustmentByAccountId.isEmpty() is checked before running the query and update.

Show hint

Think about a batch of Opportunity updates where none of them actually changed Closed Won status.

APEX

Bulkify the Logic Quiz

1. How many DML statements does applyAdjustments run, regardless of how many Opportunities changed?

Log in to submit the quiz and save your score.

My Notes

Log in to keep private notes on this lesson.

Questions about this lesson

No questions yet — be the first to ask.

Log in to ask a question about this lesson.

Summary

This lesson turns the previous lesson's per-record debug statements into a real, bulk-safe rollup — querying and updating Accounts once, regardless of how many Opportunities changed.