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
John AthitakisJohn Athitakis 

@Future Class not Updating Records

Background: This code works as a trigger, but for performance improvements because of how much stuff is going on at our opps with other automation, I wanted to make this an @future since its not time sensitive. This works with our Opportunity and related 'Sales Area' (a standard and secondary) which in turn have lookups to the user table based on what role that user plays in the sales area. The idea here is primarily to propogate down the proper info from the sales area down to the opportunity. Just to underline that, as a trigger--this works just dandy! 

My trigger

trigger OpportunityReferenceUpdate on Opportunity (after insert, after update) {
    OpportunityReferenceUpdateClass.processOpportunityReferences(Trigger.newMap.keySet());

}

Apex Class
public class OpportunityReferenceUpdateClass {
@future
    public static void processOpportunityReferences(Set<ID> ops){

        
    List<Opportunity> opl = [select id, Sales_Area__c, Secondary_Sales_Area__c, Inside_Rep_Lookup__c, Field_Rep_Lookup__c, TSE_Lookup__c, Inside_Rep_Split_Lookup__c, Field_Rep_Split_Lookup__c, TSE_Split_Lookup__c from Opportunity where id IN :ops]; 
        
    Set<id> Field_Rep = new Set<id>();
    Set<id> Inside_Rep = new Set<id>();
    Set<id> Sales_Associate = new Set<id>();
    Set<id> SDR = new Set<id>();
    Set<id> TSE1 = new Set<id>();
    Set<id> TSE2 = new Set<id>();
    
    Set<Id> saIdSet = new Set<id>();
    Set<Id> sa2IdSet = new Set<id>();
    for (Opportunity sa : opl){
        saIdSet.add(sa.Sales_Area__c);
        
        if(sa.Secondary_Sales_Area__c!=null)
        sa2IdSet.add(sa.Secondary_Sales_Area__c);
    }
            
    Map<Id, Sales_Area__c> saMap = new Map<Id, Sales_Area__C>([select ID, Field_Rep_ID__c, Inside_Rep_ID__c, TSE_ID__c from Sales_Area__c where id IN: saIdSet]);
    Map<Id, Sales_Area__c> sa2Map = new Map<Id, Sales_Area__C>([select ID, Field_Rep_ID__c, Inside_Rep_ID__c, TSE_ID__c from Sales_Area__c where id IN: sa2IdSet]);
        
        
    for (Opportunity sa : opl){
      sa.Inside_Rep_Lookup__c='005G0000006uYgi';
        if(sa.Sales_Area__c!=null){
        Sales_Area__c salesarea = saMap.get(sa.Sales_Area__C);
            

            
            sa.Inside_Rep_Lookup__c=salesarea.Inside_Rep_ID__c;
            sa.Inside_Rep_ID__c=salesarea.Inside_Rep_ID__c;
            
            if(sa.Inside_Rep_At_Close_ID__c !=null || sa.IsClosed==True)
                sa.Inside_Rep_Lookup__c=sa.Inside_Rep_At_Close_ID__c;
                
            if(sa.Inside_Rep_Overwrite__c!=null)   
                sa.Inside_Rep_Lookup__c=sa.Inside_Rep_Overwrite__c;
        
            sa.Field_Rep_Lookup__c=salesarea.Field_Rep_ID__c;
            sa.Field_Rep_ID__c=salesarea.Field_Rep_ID__c;
            
            if(sa.Field_Rep_At_Close_ID__c !=null || sa.IsClosed==True)
                sa.Field_Rep_Lookup__c=sa.Field_Rep_At_Close_ID__c;
                
            if(sa.Field_Rep_Overwrite__c!=null)   
                sa.Field_Rep_Lookup__c=sa.Field_Rep_Overwrite__c;
        
            sa.TSE_Lookup__c=salesarea.TSE_ID__c;
            sa.TSE_ID__c=salesarea.TSE_ID__c;
            
            if(sa.TSE_At_Close_ID__c !=null || sa.IsClosed==True)
            sa.TSE_Lookup__c=sa.TSE_At_Close_ID__c;
                
            if(sa.TSE1_Overwrite__c!=null)   
            sa.TSE_Lookup__c=sa.TSE1_Overwrite__c;  

        }   
        
        if(sa.Secondary_Sales_Area__c != null){
            Sales_Area__c salesarea2 = sa2Map.get(sa.Secondary_Sales_Area__C);
                sa.Inside_Rep_Split_Lookup__c=salesarea2.Inside_Rep_ID__c;
                sa.Inside_Rep_Split_Id__c=salesarea2.Inside_Rep_ID__c;
                
                if(sa.Inside_Rep_Split_At_Close_ID__c !=null || sa.IsClosed==True)
                    sa.Inside_Rep_Split_Lookup__c=sa.Inside_Rep_Split_At_Close_ID__c;
            
                sa.Field_Rep_Split_Lookup__c=salesarea2.Field_Rep_ID__c;
                sa.Field_Rep_Split_ID__c=salesarea2.Field_Rep_ID__c;
                if(sa.Field_Rep_Split_At_Close_ID__c !=null ||sa.IsClosed==True)
                    sa.Field_Rep_Split_Lookup__c=sa.Field_Rep_Split_At_Close_ID__c;  
            
                sa.TSE_Split_Lookup__c=salesarea2.TSE_ID__c;
                sa.TSE_Split_ID__c=salesarea2.TSE_ID__c;
                if(sa.TSE_Split_At_Close_ID__c !=null || sa.IsClosed==True)
                    sa.TSE_Split_Lookup__c=sa.TSE_Split_At_Close_ID__c;                
            
        }
        
       
        if(sa.Field_Rep_Overwrite__c != null)
            Field_Rep.add(sa.Field_Rep_Overwrite__c);   
        if(sa.Inside_Rep_Overwrite__c != null)
            Inside_Rep.add(sa.Inside_Rep_Overwrite__c);
        if(sa.TSE1_Overwrite__c != null)
            TSE1.add(sa.TSE1_Overwrite__c);
        if(sa.TSE2_Overwrite__c != null)
            TSE2.add(sa.TSE2_Overwrite__c);
    }

    Map<id, User> Field_Rep_Names = new Map<id, User>([Select Name from User Where Id in :Field_Rep]);  
    Map<id, User> Inside_Rep_Names = new Map<id, User>([Select Name from User Where Id in :Inside_Rep]); 

    Map<id, User> TSE1_Names = new Map<id, User>([Select Name from User Where Id in :TSE1]); 
    Map<id, User> TSE2_Names = new Map<id, User>([Select Name from User Where Id in :TSE2]); 

    for (Opportunity sa : opl){
        if(sa.Field_Rep_Overwrite__c != null)
            sa.Field_Rep_Overwrite_text__c = Field_Rep_Names.get(sa.Field_Rep_Overwrite__c).Name;   
        else
            sa.Field_Rep_Overwrite_text__c ='';
        if(sa.Inside_Rep_Overwrite__c != null)
            sa.Inside_Rep_Overwrite_text__c = Inside_Rep_Names.get(sa.Inside_Rep_Overwrite__c).Name;   
        else
            sa.Inside_Rep_Overwrite_text__c ='';

        if(sa.TSE1_Overwrite__c != null)
            sa.TSE1_Overwrite_text__c = TSE1_Names.get(sa.TSE1_Overwrite__c).Name;   
        else
            sa.TSE1_Overwrite_text__c ='';
        if(sa.TSE2_Overwrite__c != null)
            sa.TSE2_Overwrite_text__c = TSE2_Names.get(sa.TSE2_Overwrite__c).Name;   
        else
            sa.TSE2_Overwrite_text__c ='';
    }
   
    }
    
}


You'll note that on one of the lines I included

        sa.Inside_Rep_Lookup__c='005G0000006uYgi';

This was to see if I could simply force a value (which it's not). My apex logs show that this future method is being fired and completing without error. If I intentionally induce errors (like removing fields from my select statement) those errors are reported as expected. 

Best Answer chosen by John Athitakis
Shashikant SharmaShashikant Sharma
Answers was in my first reply where I asked you to add a condition in trigger code.
 
trigger OpportunityReferenceUpdate on Opportunity (after insert, after update) {
 
// to avoid a future call from a future method
if( !System.isFuture() ) {    OpportunityReferenceUpdateClass.processOpportunityReferences(Trigger.newMap.keySet());
}

}
If this fix your issue please accept the solution so that others could be benifited from it.

Thanks
Shashikant

All Answers

Shashikant SharmaShashikant Sharma
Hi John,

I read your code and found

1. There is no Update opl; means noupdate statement to update the opportunity.
2. You should put a condition 
 
trigger OpportunityReferenceUpdate on Opportunity (after insert, after update) {
 
// to avoid a future call from a future method
if( !System.isFuture() ) {    OpportunityReferenceUpdateClass.processOpportunityReferences(Trigger.newMap.keySet());
}

}

if it still does not work then try to put a assert false statemnt to see the opl values.

System.assert( false,  opl );

Let me knwo how it goes.

Thanks
Shashikant
 
John AthitakisJohn Athitakis
I tried to put in an update OPL and got an error where a @future could not trigger an @future code. This code is triggered off an opporunity update and it updates the opportunity. How would one get around that to not refire the @future code? 
Shashikant SharmaShashikant Sharma
Answers was in my first reply where I asked you to add a condition in trigger code.
 
trigger OpportunityReferenceUpdate on Opportunity (after insert, after update) {
 
// to avoid a future call from a future method
if( !System.isFuture() ) {    OpportunityReferenceUpdateClass.processOpportunityReferences(Trigger.newMap.keySet());
}

}
If this fix your issue please accept the solution so that others could be benifited from it.

Thanks
Shashikant
This was selected as the best answer
Mahesh DMahesh D
Hi John,

+Shashikant,

Please find the modified code.
trigger OpportunityReferenceUpdate on Opportunity (after insert, after update) {
 
	// to avoid a future call from a future method
	if( !System.isFuture() ) {
		OpportunityReferenceUpdateClass.processOpportunityReferences(Trigger.newMap.keySet());
	} else {
		OpportunityReferenceUpdateClass.processOpportunityReferencesSync(Trigger.newMap.keySet());
	}
}
Here, what I am suggesting is, you can implement the same method "processOpportunityReferences" which should not have @future annotation. That way, if the trigger is calling normally then it will call @future method(processOpportunityReferences) and if the trigger is calling in the future context then Sync method(processOpportunityReferencesSync) will execute.

Please do let me know if it helps you.

Regards,
Mahesh
John AthitakisJohn Athitakis

Thank you Mahesh and Shashikant!

Mahesh, my only concern with your if else--is because the trigger is on the opportunity and updates the opportunity, wouldn't it cause an endless loop with your else condition?