The business requirement is to have a lookup relation ship between a Contract and Opportunity. A field on the opportunity must get the SUM of a custom field on the Contracts which belong to the opportunity. I use the @future annotation to increase the CPU time, however, I get an error "Attempt to de-reference a null object".

public class contractRollUpCLASS {
    public static void calculate() {

    //limit the size of list by using Sets which do not contain duplicate elements.
    Set<ID> oppIds = new Set<ID>();

    //when adding new or updating existing contracts.
    if(trigger.isInsert || trigger.isUpdate) {

        for(Contract con : (List<Contract>)trigger.new) {

            if(con.Is_Obsolete__c == false)

    //when deleting contracts.

        for(Contract con : (List<Contract>)trigger.old ) {
           if(con.Is_Obsolete__c == false)
    System.debug('***************NUMBER OF IDS ************** : '+ String.ValueOf(oppIds.size()));

    //Map will contain one Opportunity Id to one sum value.
    map<Id, Double> OpportunityMap = new Map<Id,Double>();

    List<Opportunity> oppsToUpdate = new List<Opportunity>();

    //aggregate result.
    for(AggregateResult q : [SELECT Renewal_Opportunity__c, SUM(Latest_PD_Renewal_Amount__c) sumLatest FROM Contract WHERE Renewal_Opportunity__c IN : oppIds AND Latest_PD_Renewal_Amount__c != null GROUP BY Renewal_Opportunity__c]) {

    for(Opportunity opp : [SELECT Id, Annual_Maintenance_Value__c FROM Opportunity WHERE Id IN : oppIds]) {

        Double sumLatestPD = OpportunityMap.get(opp.Id);
        opp.Annual_Maintenance_Value__c = sumLatestPD;
    update oppsToUpdate;   


Here's the trigger:

trigger invokeClasses on Contract (after insert, after update, after delete, after undelete) {


1 Answer 1


You can't use the Trigger class values inside a future method. If you need the values, you need to pass in the data from the trigger:

Set<Id> recordIds = new Set<Id>();
if(Trigger.old != null) {
if(Trigger.new != null) {
contractRollupClass.calculate(recordIds, Trigger.isInsert, Trigger.isUpdate, Trigger.isDelete);

From there, you need to query the records back from the database, and then do the logic you want.

Also, you may want to consider using Queueable, instead, as it has more capability than future methods do, but that's entirely up to you.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .