• John Athitakis
  • NEWBIE
  • 135 Points
  • Member since 2014

  • Chatter
    Feed
  • 0
    Best Answers
  • 0
    Likes Received
  • 0
    Likes Given
  • 17
    Questions
  • 40
    Replies

I'm working on a quick bit of code to leverage a managed packed with Hip Chat and I'm getting an error in production that is not showing up in my sandbox.

The code is incredible basic, which is what is ultimately confusing me. The managed package is expecting a String Sobject name. The trigger also seems to work, but rather, it's the test class which is causing the issue.

Trigger

 

trigger ScopingRequestInsertedHipChatWebhook on Scoping_Requests__c (after insert) {
        HCFS.HipChat.notify();
}

Test Class (Which is causing the following error: 
System.NullPointerException: Attempt to de-reference a null object ) on the line following my static void
 
@isTest
public class ScopingRequestHipChatWebhookTriggerTest {
    
    @isTest
    static void testTriggerAfterInsert() {
        HCFS.HipChatTriggerTestHelper.testAfterInsert('Scoping_Requests__c');
    }
}


The API Name for the object is Scoping_Requests__c, and in Sandbox it's causing me no errors (tests execute correctly.If I change the value from 'Scoping_Requests__c' to something else for a test, I correctly receive a 'null object' error). In production however I'm failing deploys with the very same error even though everything matches. 

Thoughts? 

Earlier Question got burried! Reposting it here.

So I have a bit of code (fully below) that another community member helped me on and it works really well save for one thing I'm trying to suss out. The objects involved are Project (which has a lookup to Opportunity), Opportunity, Sales Invoice (a custom object) and Sales Invoice Line (a custom object that is Master-Detail to Sales Invoice as a child). These were installed as a managed package and the relationship of the child to the parent is the rather strange R00N40000001p914EAA. All this code compiles/no errors/no errors during run time.

What it should do: When a project is created with a link to an opportunity, the opportunty, any sales orders it has and any sales order line items THEY have have a lookup to project populated (with some added functionality if we null out the links). Everything is working until you get to the child in the master-detail, where it seems it is simply not doing anything.
Here is the full code followed by the portion where the problem is occuring. 
 
trigger PSA_Project_Automation on pse__Proj__c (before insert, before update) {
    
  list<id> opportunityIds = new list<id>();
  map<id,id> mOpptyProjIds = new map<id,id>();

  for(pse__Proj__c proj : trigger.new) {
    if(proj.pse__Opportunity__c != null) {
      opportunityIds.add(proj.pse__Opportunity__c);
      mOpptyProjIds.put(proj.pse__Opportunity__c,proj.id);
    }
    if(trigger.isUpdate) {
      if(trigger.oldmap.get(proj.id).pse__opportunity__c != null
          && proj.pse__Opportunity__c == null
         ) {
        opportunityIds.add(trigger.oldmap.get(proj.id).pse__opportunity__c);
        mOpptyProjIds.put(trigger.oldmap.get(proj.id).pse__opportunity__c,null);
      }
    }
  }

  // Get the Opportunities and update them
  list<opportunity> opportunitiesToUpdate = [SELECT id,pse__Primary_Project__c, Primary_Project_Test__c FROM Opportunity WHERE id IN :opportunityIds];

    
  for(opportunity o :opportunitiesToUpdate) {
    if(mOpptyProjIds.containsKey(o.id)) {
      o.pse__Primary_Project__c = mOpptyProjIds.get(o.id);
    }
  }

  if(opportunitiesToUpdate.size()>0) {
    update opportunitiesToUpdate;
  }
  
  list<Sales_Invoice__c> SalesInvoiceToUpdate = [SELECT id, Primary_Project__c, Opportunity__c FROM Sales_Invoice__c WHERE 	Opportunity__c IN :opportunityIds];
    for(Sales_Invoice__c s: SalesInvoiceToUpdate){
      if(mOpptyProjIds.containsKey(s.Opportunity__c)) {   
     s.Primary_Project__c=mOpptyProjIds.get(s.Opportunity__c);
      }
    }
   if(SalesInvoiceToUpdate.size()>0) {
    update SalesInvoiceToUpdate;
  }
  
 list<Sales_Invoice__c> SalesInvoiceItemToUpdate = [SELECT id, Primary_Project__c, Opportunity__c, (SELECT Opportunity__c, Primary_Project__c, Sales_Invoice__c FROM R00N40000001p914EAA__r) FROM Sales_Invoice__c  WHERE Opportunity__c IN :opportunityIds];
    for(Sales_Invoice__c si: SalesInvoiceItemToUpdate){
      List<Sales_Invoice_Item__c> itemList = si.R00N40000001p914EAA__r; 
        for(Sales_Invoice_Item__c i : itemList){  
      i.Primary_Project__c=mOpptyProjIds.get(si.Opportunity__c);
      
            }
    }
   if(SalesInvoiceItemToUpdate.size()>0) {
    update SalesInvoiceItemToUpdate;
  }  
    
}
And here is the exact portion that doesn't seem to do anything. 
 
list<Sales_Invoice__c> SalesInvoiceItemToUpdate = [SELECT id, Primary_Project__c, Opportunity__c, (SELECT Opportunity__c, Primary_Project__c, Sales_Invoice__c FROM R00N40000001p914EAA__r) FROM Sales_Invoice__c  WHERE Opportunity__c IN :opportunityIds];
    for(Sales_Invoice__c si: SalesInvoiceItemToUpdate){
      List<Sales_Invoice_Item__c> itemList = si.R00N40000001p914EAA__r; 
        for(Sales_Invoice_Item__c i : itemList){  
      i.Primary_Project__c=mOpptyProjIds.get(si.Opportunity__c);
      
            }
    }
   if(SalesInvoiceItemToUpdate.size()>0) {
    update SalesInvoiceItemToUpdate;
  }

 

Here is a simple trigger. There are two objects with look ups to one another, Project and Opportunity.
Goal here is that when a Project is created with a lookup populated to Opportunity, that the code goes out to that Opportunity and populates the lookup withe the id to the project. This should work on create/change and if the lookup on Project is nulled out, that the lookup that may have exited pointing back to it from Opportunity is also nulled.

The code is working in the first instance (when a project is created, it finds the opp and fills out the lookup) but it is not doing any actions on 'update' (nulling or updating) which is confusing me given the simplicty of the code.

Thoughts?

 

trigger PSA_Project_Automation on pse__Proj__c (before insert, before update) {
List<Id> OpportunityIds = new List<Id>();
List<Id> NullOpportunityIds = new List<Id>();   
    for (pse__Proj__c proj : Trigger.new){
       
            if(proj.pse__Opportunity__c != null){
            OpportunityIds.add(proj.pse__Opportunity__c);
            }  
           
        if(Trigger.IsUpdate){
            pse__Proj__c oldProj = Trigger.oldMap.get(proj.Id);
            
            if(proj.pse__Opportunity__c == null && oldProj.pse__Opportunity__c !=null)
             NullOpportunityIds.add(oldProj.pse__Opportunity__c); 
        }
        
    }

    List<Opportunity> oppList = [SELECT id, pse__Primary_Project__c FROM Opportunity Where id in :OpportunityIds];
    List<Opportunity> NullOppList = [SELECT id, pse__Primary_Project__c FROM Opportunity Where id in :NullOpportunityIds];
    
    for(pse__Proj__c p : Trigger.new){
    for(integer i=0; i<oppList.size(); i++){
      oppList[i].pse__Primary_Project__c = p.id;
        }
    for(integer i=0; i<NullOppList.size(); i++){
      NullOppList[i].pse__Primary_Project__c = null;      
        }
    }

Synopsis: I have a custom objet (Sales_Areas__c) that Opportunities have a lookup toward. Opportunities have lookups and other data that I want updated when the Sales Area is updated (with related info). I have this set as such.

Trigger on Sales Area fires on edit, calls a queueable class that collects all the related opportunities (since there can be very very many) and 'updates' them.
Trigger on Opportunity that fires on edit, calls a queuable class that gathers all the relevent data from its related Sales area and makes changes to the Opportunity.

In this sense, an edit to the Opportunity or Sales area results in an update (desired behavior). I moved from some less effective @future code to queueable because of issues I was facing with @future code and now, have encountered problems with queueable.

My old Opportunity code prevented recussions by using the isFuture(); syntax before calling the old @future code. Since what invoked my Opportunity code at the Sales area was a Queueable, this was not a problem. But now that they are both Queueable, I'm facing this issue:

Too many queueable jobs added to the queue: 2

It seems my Opportnity Code (which edits the Opportunity) is invoking itself. I attempted to prevent this via:

trigger OpportunityReferenceUpdate on Opportunity (before insert, before update) {
    if(Limits.getQueueableJobs()<2){ 
        List<Opportunity> opportunityRecords = new List<Opportunity>();      
        for (Opportunity op : Trigger.new){
        opportunityRecords.add(op);
        }
    OpportunityReferenceQueuable updateJob = new OpportunityReferenceQueuable(opportunityRecords);
    ID jobID=System.enqueueJob(updateJob);
    }

But that does not seem to work? Em I using Limits.getQueueableJobs() incorrectly? 

Is there a way to check the name of the class that invoked the current method via isQueueable() (something other than a boolean, but maybe the name of the job).
 

Thank you. 

 

 

Short story. I have a trigger on a custom object that invokes a queueable class. That class is gathering up related opportunities (via a lookup) and editing them. 

On the opportunity level, I have a bit of future code that fires. 

Now before my future code is invoked I have the following.

if(!System.isFuture()) {    
    OpportunityReferenceUpdateClass.processOpportunityReferences(Trigger.newMap.keySet());
}

To solve an issue where I was having a nightly batch executing that was causing an error (batch invoking future is a no no) I changed it to this.

if(!System.isFuture() && !System.isBatch()) {    
    OpportunityReferenceUpdateClass.processOpportunityReferences(Trigger.newMap.keySet());
}

Success---no more errors...however for some reason now that I added isBatch to my conditions to ensure its not firing due to my nightly Batch, it seems my Queueable code is now no longer able to update it? It doesn't make a whole lot of sense to me.

 

Here is a glimpse of my apex jobs (there are no batches running)

Without isBatch, the Queueable code is completing and kicking off my future code. 


4/11/2016 3:22 PMFutureCompleted 000Athitakis, John4/11/2016 3:22 PMOpportunityReferenceUpdateClassprocessOpportunityReferences7071100000s5byt000000000000000  
 4/11/2016 3:22 PMQueueableCompleted 000Athitakis, John SalesAreaRelatedOpportunityUpdateClass 7071100000s5bta000000000000000 

With isBatch, the Future code is never fired. 

4/11/2016 3:26 PM	Queueable	Completed	 	0	0	0	Athitakis, John	 	SalesAreaRelatedOpportunityUpdateClass	 	7071100000s5c5F	000000000000000


My confusion is, I would expect this behavior if I was using isQueueable, but I'm not? (One more, there are no batches going on from what I can see).

 

Any ideas? 

Situation: I have batch apex running every night at 1 am on Opportunity Lin Items.

I also have @ future code on my opportunity. It seems once I introduced the later, that my batches are failing because at my Opportunity level, I have roll ups which the batch is causing to save, leading to a the Batch failing due to the @future.

I believe an easy solution to this is convert my code to queueable--and while I recently learned about that, the code is pretty lengthy and I don't know a good way of doing that conversion (if thats the only answer, so be it).
 

However, is there a way I could tell my trigger instigating the @future method to not fire due to a save occuring from rollups without necessarily naming each rollup? Currently my call looks like this:

trigger OpportunityReferenceUpdate on Opportunity (after insert, after update) {

       
 if( !System.isFuture() ) {    
    OpportunityReferenceUpdateClass.processOpportunityReferences(Trigger.newMap.keySet());
}
   
}

Sadly, my 'isFuture' flag does not seem to be catching the edge case from the Batch update. 

I can imagine something like this as a temporary fix. 

trigger OpportunityReferenceUpdate on Opportunity (after insert, after update) {
//Used to update text fields within the Opportunity to reduce the amount of references due to SFDC Limits
//Author: John Athitakis
//Relates to BIZOPS-4428
//
 // create a set of all the unique ownerIds
 // 
 // 
 boolean Change=False;
 
   for (Opportunity sa : Trigger.new){
     if(Trigger.IsUpdate){
       Opportunity saOld = Trigger.oldMap.get(sa.Id);
        
      if( (sa.Rollup1 == saOld.rollup1) && (sa.Rollup2==sa.Rollup2) ){
    Change=True;
      }
     }
   }
    
    
    
 if( !System.isFuture() && nodechange== False) {    
    OpportunityReferenceUpdateClass.processOpportunityReferences(Trigger.newMap.keySet());
}
 

Is there a better way?

So here is my situation. I recently began to realize I need to async more of our code, and learned all about @future (WEEE!) but have quickly come to realize that because we have many moving pieces that @future has limitations (specifically when one @future might trigger another @future).

In queueable, which seems to be the silver knight I have wanted but, I'm having a misunderstanding as to how to go about this (most examples I find show the queuable apex class which I understand but, not understanding how to pass it the values of triggering ID's).

So here is my simple code:

Trigger

 

trigger SalesAreaRelatedOpportunityUpdate on Sales_Area__c (after insert,after update) {
    SalesAreaRelatedOpportunityUpdateClass.processOpportunities(Trigger.newMap.keySet());

}

 

Class

global class SalesAreaRelatedOpportunityUpdateClass {
@future
    public static void processOpportunities(Set<ID> salesAreaId){

    List <Opportunity> updateOps = [SELECT Id FROM Opportunity WHERE IsClosed = False AND (Sales_Area__c in :salesAreaId OR Secondary_Sales_Area__c in :salesAreaId)];
    if(!updateOps.isEmpty()) {
        update updateOps;   
        
    }
                                                                
}
}

What this code does is merely update the related opportunities from a custom object which in turn, triggers other code. Some of that other code due to load needs to be async (and currently is @future, although once I figure out this queueable business I may change it as well).

My question is this---how would I change this pretty basic code to something queable? I'm having some troubles understnading how to pass the keySet in particular. 

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. 

Have some very simple code. Basically when a custom object (sales area) is updated, I want all the related opportunities the not won/lost opps that are related to it to be updated as well (which for most sales areas, is just 5-6 opps atm since this is a new process). I've tried a few dozen ways to get around the error in the title, have moved my update out of a for look as well as the select statement, but cannot for the life of me understand what I'm doing wrong?
 
trigger SalesAreaRelatedOpportunityUpdate on Sales_Area__c (after update) {
      if(checkRecursive.runOnce())
    {
    List<ID> salesAreaId = new List<ID>(); 
        
        for (Sales_Area__c sa : Trigger.new){
        salesAreaId.add(sa.id);
        }
        
        List <Opportunity> updateOps=[SELECT Id FROM Opportunity WHERE IsClosed=False AND (Sales_Area__c in :salesAreaId OR Secondary_Sales_Area__c in  :salesAreaId)];
        update updateOps;
        
    }
}

Hello, I'm hoping someone can help me out! 

So this code is pretty basic in idea. A custom object is created or edited. It has numerous lookups to the user table (each representing a different type of relationship, for example: Field Rep).

The user table as number fields that this code is meant to decrement or increment.

The issue is this; when i made an adjustment to the user table, if i set a static value--its fine, example:

 

u.Sales_Area_Field_Rep__c=10;

But if I do something like the following, I get the de-reference error
u.Sales_Area_Field_Rep__c++;
u.Sales_Area_Field_Rep__c+=1;
u.Sales_Area_Field_Rep__c=u.Sales_Area_Field_Rep__c+1;

Here is a big snippet of the code. Any help would be REALLY appreciated. 

 

trigger UpdateSalesAreaToUserCount on Sales_Area__c (before insert, before update) {
    
String prefix = User.sObjectType.getDescribe().keyPrefix;
   
list <User> usersToUpdate= new List <User>{};
    if(Trigger.IsUpdate){
       for (Sales_Area__c co : Trigger.new) { 
  		 Sales_Area__c coOld = Trigger.oldMap.get(co.Id);
         
          //Begin Field Rep
 		   	if(coOld.Field_Rep__c != co.Field_Rep__c){
			if(!String.isBlank(coOld.Field_Rep__c))
            {
                User u = new User(Id = coOld.Field_Rep__c, Sales_Area_Field_Rep__c=coOld.Field_Rep__r.Sales_Area_Field_Rep__c);
                u.Sales_Area_Field_Rep__c++;
                usersToUpdate.add(u);           
            }
            if(!String.isBlank(co.Field_Rep__c))
            {
                User u = new User(Id = co.Field_Rep__c, Sales_Area_Field_Rep__c=co.Field_Rep__r.Sales_Area_Field_Rep__c);
                u.Sales_Area_Field_Rep__c=10;
                usersToUpdate.add(u);              
            }
Getting a strange error on some test cases when comparing two decimal values together. Error occurs on line 27 and would probably occur again on line 32. 

 
@isTest 
/*
Author: --
Test for Trigger: PR2PO_ExchangeRateTest
*/
public class PR2PO_ExchangeRateTest {
	static testMethod void validateExchangeTrigger() {
//Logic: 
        Vendor__c vendor=new Vendor__c();
        vendor.Name='Test';
        vendor.Billing_Contact_Email__c='test@puppetlabs.com';
        insert vendor; 
        Vendor__c vendorInserted = [SELECT Id FROM Vendor__c WHERE Billing_Contact_Email__c = 'test@puppetlabs.com' ];
        Purchase_Order__c order = new Purchase_Order__c();
        order.Vendor__c = vendorInserted.Id;
        order.Name = 'PO-99999-Test';
        order.Terms__c  = 'Net 30';
        order.Currency__c= 'GBP';
        order.Currency_Base__c='GBP';
        string newCurrencyCode=order.Currency_Base__c;
        order.Total__c=100.50;
        test.startTest();
        date startOfMonth = date.today().toStartofMonth().addMonths(-1);
        insert order;    
		system.assertEquals(startOfMonth , date.today().toStartofMonth().addMonths(-1));
        Exchange_Rate__c exchangeRate = [SELECT ConversionRate__c FROM Exchange_Rate__c WHERE StartDate__c <= today AND StartDate__c >= :startOfMonth AND IsoCode__c = :newCurrencyCode ORDER BY StartDate__c DESC LIMIT 1];
        system.assertEquals(order.ExchRate_to_USD__c = exchangeRate.ConversionRate__c);        
        order.Currency__c= 'EUR';
        order.Currency_Base__c='EUR';      
        update order;
        system.assertEquals(startOfMonth , date.today().toStartofMonth().addMonths(-1));
        Exchange_Rate__c exchangeRate2 = [SELECT ConversionRate__c FROM Exchange_Rate__c WHERE StartDate__c <= today AND StartDate__c >= :startOfMonth AND IsoCode__c = :newCurrencyCode ORDER BY StartDate__c DESC LIMIT 1];
        system.assertEquals(order.ExchRate_to_USD__c = exchangeRate2.ConversionRate__c);        
        order.Currency__c= 'USD';
        order.Currency_Base__c='USD'; 
        update order;
		system.assertEquals(order.ExchRate_to_USD__c = 1.0); 
        test.stopTest();  
    }
}

 

So I'm trying to make a very basic workflow that updates leaders that are being converted (basically just updating one custom checkbox to true), but for the life of me, I cannot seem to get it to actually seem to fire. Basic logic here 

 

ISCHANGED(Status) && OR(ISPICKVAL(Status, 'Qualified to Opportunity'), ISPICKVAL(Status, 'Attached to Account'))&& Accepted__c=False && SQL__c=True
Note that we have rules that required the above two status before conversion (so all converted ops go into one or the other). I have instead also checked it with Converted=True and Is changed on Convert and True but despite it all, the field update never seems to fire. Thoughts? 

Scratching my head--works fine in the UI, but not via data loader. Any suggestions?

GIST: Check to see if company name is in another table, OR in some other conditions (basic if then) flag a field as true on lead. Nothing complex. Why is Data Loader skipping it?

trigger LeadAssignmentRuleTest on Lead (before insert) {
  for (Lead a : Trigger.new) {
      List<Inside_View_Company__c> b= [select id, Account_Name__c from Inside_View_Company__c where Account_Name__c = :a.Company LIMIT 1];
     

    if(!b.isEmpty() || a.Event_Temperature__c=='Hot'|| a.Event_Temperature__c=='Warm') {
          a.Direct_to_Sales__c=True;
    }
    if(!String.isblank(a.Form_Name__c)){
          if(a.Form_Name__c.containsIgnoreCase('buy') || a.Form_Name__c.containsIgnoreCase('inquiry')){
		  a.Direct_to_Sales__c=True; 												
          }
	}
 }
}
So I worked out some code to invoke a rest service off of an event in SFDC. I wrote the code out in two parts, the trigger and a @Future Class. I'm scratching my had as to where to begin however with the test classes (What I've been trying out seems to not be working, much of the documentation suggests test methods which don't seem supported any longer).


The trigger
trigger ProductLicenseRestCall on ProductLicense__c (before update) {
    for (ProductLicense__c a : Trigger.new) {
        ProductLicense__c b=Trigger.oldMap.get(a.ID);
        // make the asynchronous web service callout
        if(a.License_Sent__c != b.License_Sent__c){
		if(a.License_Sent__c==TRUE){
        a.Requested_Date_Time__c=DateTime.now();
        WebServiceCallout.sendNotification(String.valueOf(a.Id));    
        }}
    }

}

And the class. Its very simple and minimal and in all my testing in our sandbox, is working just fine. 
public class WebServiceCallout {

    @future (callout=true)
    public static void sendNotification(String licenseId) {
        
     list<productkeylicense__c> Pass = [SELECT Key__c FROM ProductKeyLicense__c ORDER BY  CreatedDate LIMIT 1];
        HttpRequest req = new HttpRequest();
        HttpResponse res = new HttpResponse();
        Http http = new Http();
        req.setEndpoint('https://acmecorp.com/endpoint/'+ licenseId);
        req.setMethod('POST');
        
        req.setHeader('License-Client-Key', Pass[0].Key__c);     

        try {
            res = http.send(req);
        } catch(System.CalloutException e) {
            System.debug('Callout error: '+ e);
            System.debug(res.toString());
        }

    }



}

 

My first time using APEX for Rest (Or rest in general) and did a bit of reading and not sure what might be wrong. Error I'm getting from my logs is:

16:31:55.231 (231776791)|CALLOUT_RESPONSE|[17]|System.HttpResponse[Status=Not Found, StatusCode=404]
 

(nothing else of note)

Here is the @future class being involved by a simple trigger. We are using a bearer token for 'License-Client-Key', a test value in it currently that is setup to be accepted by the real endpoint (thus the 'boom'). Not sure if this is the issue? Thoughts or suggestions would be appreciated.
 

public class WebServiceCallout {

    @future (callout=true)
    public static void sendNotification(String name, Date expiration, string product, Decimal quantity, String type) {

        HttpRequest req = new HttpRequest();
        HttpResponse res = new HttpResponse();
        Http http = new Http();
        req.setEndpoint('https://endpointurl.com/destination);
        req.setMethod('POST');
        
        req.setHeader('License-Client-Key', 'boom');     
        req.setBody('Name='+EncodingUtil.urlEncode(name, 'UTF-8')+'&Expiration='+ expiration +'&Product='+EncodingUtil.urlEncode(product, 'UTF-8')+'&Quantity='+ quantity+'&Type='+EncodingUtil.urlEncode(type, 'UTF-8'));
       // req.setCompressed(true); // otherwise we hit a limit of 32000

        try {
            res = http.send(req);
        } catch(System.CalloutException e) {
            System.debug('Callout error: '+ e);
            System.debug(res.toString());
        }

    }

   //  run WebServiceCallout.testMe(); from Execute Anonymous to test
    public static void testMe() {
      WebServiceCallout.sendNotification('My Test Customer',date.newInstance(2015, 7, 15),'01tG0000003Hvt2',1,'Subscription');
    }

}
So I have code that creates an event when a field is populated (on create or update). This is a way for secondary systems to trigger event creations. The logic is on create if the field isnt null, make an event with some attributes. On update, only do so if the new value differs from the old AND is not null. For some reason in my testing, it appears fine, but Ringlead (a third party tool thats populating this field) is causing double activities to create and I'm confused as to how/why.

/* 
Author: John Athitakis
Synopsis: When a new or existing Lead has a special string field populated, an event is created with that string as its subject line
*/

trigger LeadCreateEvent on Lead (after insert, after update) {
   //  Logic: On insert if the Event Activity Field is Populated, Create an Event.
 if(Trigger.IsInsert) {
 for (Lead UpdatedLead: Trigger.new) {
     if(UpdatedLead.Event_Activity_Subject__c!=null){     
            Event event=new Event(
            OwnerId = UpdatedLead.CreatedById,
            WhoId = UpdatedLead.id,
            StartDateTime = UpdatedLead.CreatedDate,
            EndDateTime = UpdatedLead.CreatedDate,
            Subject = UpdatedLead.Event_Activity_Subject__c
        );
            insert event ;
     }
 }
 }
   // Logic: On update, if there is a change in the Event Activity Field & it is not Null, Create an Event 
 if(Trigger.IsUpdate){
     for (Lead UpdatedLead2: Trigger.new) {
      Lead PriorLead = Trigger.oldMap.get(UpdatedLead2.ID);
         if (UpdatedLead2.Event_Activity_Subject__c != PriorLead.Event_Activity_Subject__c && UpdatedLead2.Event_Activity_Subject__c!=null){
            Event event2 = new Event(
            OwnerId = UpdatedLead2.CreatedById,
            WhoId = UpdatedLead2.id,
            StartDateTime = UpdatedLead2.CreatedDate,
            EndDateTime = UpdatedLead2.CreatedDate,
            Subject = UpdatedLead2.Event_Activity_Subject__c
        );
             insert event2 ;    }}

So I have a strange error. I have a bit of code that is updating leads and adding them as members to campaigns when a field is updated from an external tool with a campign ID. It works fine in my testing, but in the last day have been getting the following error when the external tool is touching the record. From what I can see, the field in quesiton is not being accessed:

 

LeadCreateCampaignMember: execution of AfterUpdate
caused by: System.StringException: Invalid id: undefined
Trigger.LeadCreateCampaignMember: line 24, column 1

I will bold line 24 in this case:


/* 
Author: John Athitakis
Synopsis: When a new or existing Lead has a special string field populated, an event is created with that string as its subject line
*/

trigger LeadCreateCampaignMember on Lead (after insert, after update) {
   //  Logic: On insert if the Event Activity Field is Populated, Create an Event.
 if(Trigger.IsInsert) {
 for (Lead UpdatedLead: Trigger.new) {
     if(UpdatedLead.campaign_id__c!=null){ 
            CampaignMember cml = new CampaignMember();
            cml.campaignid = UpdatedLead.campaign_id__c;
            cml.leadid = UpdatedLead.id;
            Database.upsert(cml, false) ;
     }
 }
 }
   // Logic: On update, if there is a change in the Event Activity Field & it is not Null, Create an Event 
 if(Trigger.IsUpdate){
     for (Lead UpdatedLead2: Trigger.new) {
      Lead PriorLead = Trigger.oldMap.get(UpdatedLead2.ID);
         if (UpdatedLead2.campaign_id__c != PriorLead.campaign_id__c && UpdatedLead2.campaign_id__c!=null){
          CampaignMember cml2 = new CampaignMember();
          cml2.campaignid = UpdatedLead2.campaign_id__c;
          cml2.leadid = UpdatedLead2.id;
          Database.upsert(cml2, false);
}
     }}}
 Obviously

I'm working on a quick bit of code to leverage a managed packed with Hip Chat and I'm getting an error in production that is not showing up in my sandbox.

The code is incredible basic, which is what is ultimately confusing me. The managed package is expecting a String Sobject name. The trigger also seems to work, but rather, it's the test class which is causing the issue.

Trigger

 

trigger ScopingRequestInsertedHipChatWebhook on Scoping_Requests__c (after insert) {
        HCFS.HipChat.notify();
}

Test Class (Which is causing the following error: 
System.NullPointerException: Attempt to de-reference a null object ) on the line following my static void
 
@isTest
public class ScopingRequestHipChatWebhookTriggerTest {
    
    @isTest
    static void testTriggerAfterInsert() {
        HCFS.HipChatTriggerTestHelper.testAfterInsert('Scoping_Requests__c');
    }
}


The API Name for the object is Scoping_Requests__c, and in Sandbox it's causing me no errors (tests execute correctly.If I change the value from 'Scoping_Requests__c' to something else for a test, I correctly receive a 'null object' error). In production however I'm failing deploys with the very same error even though everything matches. 

Thoughts? 

Earlier Question got burried! Reposting it here.

So I have a bit of code (fully below) that another community member helped me on and it works really well save for one thing I'm trying to suss out. The objects involved are Project (which has a lookup to Opportunity), Opportunity, Sales Invoice (a custom object) and Sales Invoice Line (a custom object that is Master-Detail to Sales Invoice as a child). These were installed as a managed package and the relationship of the child to the parent is the rather strange R00N40000001p914EAA. All this code compiles/no errors/no errors during run time.

What it should do: When a project is created with a link to an opportunity, the opportunty, any sales orders it has and any sales order line items THEY have have a lookup to project populated (with some added functionality if we null out the links). Everything is working until you get to the child in the master-detail, where it seems it is simply not doing anything.
Here is the full code followed by the portion where the problem is occuring. 
 
trigger PSA_Project_Automation on pse__Proj__c (before insert, before update) {
    
  list<id> opportunityIds = new list<id>();
  map<id,id> mOpptyProjIds = new map<id,id>();

  for(pse__Proj__c proj : trigger.new) {
    if(proj.pse__Opportunity__c != null) {
      opportunityIds.add(proj.pse__Opportunity__c);
      mOpptyProjIds.put(proj.pse__Opportunity__c,proj.id);
    }
    if(trigger.isUpdate) {
      if(trigger.oldmap.get(proj.id).pse__opportunity__c != null
          && proj.pse__Opportunity__c == null
         ) {
        opportunityIds.add(trigger.oldmap.get(proj.id).pse__opportunity__c);
        mOpptyProjIds.put(trigger.oldmap.get(proj.id).pse__opportunity__c,null);
      }
    }
  }

  // Get the Opportunities and update them
  list<opportunity> opportunitiesToUpdate = [SELECT id,pse__Primary_Project__c, Primary_Project_Test__c FROM Opportunity WHERE id IN :opportunityIds];

    
  for(opportunity o :opportunitiesToUpdate) {
    if(mOpptyProjIds.containsKey(o.id)) {
      o.pse__Primary_Project__c = mOpptyProjIds.get(o.id);
    }
  }

  if(opportunitiesToUpdate.size()>0) {
    update opportunitiesToUpdate;
  }
  
  list<Sales_Invoice__c> SalesInvoiceToUpdate = [SELECT id, Primary_Project__c, Opportunity__c FROM Sales_Invoice__c WHERE 	Opportunity__c IN :opportunityIds];
    for(Sales_Invoice__c s: SalesInvoiceToUpdate){
      if(mOpptyProjIds.containsKey(s.Opportunity__c)) {   
     s.Primary_Project__c=mOpptyProjIds.get(s.Opportunity__c);
      }
    }
   if(SalesInvoiceToUpdate.size()>0) {
    update SalesInvoiceToUpdate;
  }
  
 list<Sales_Invoice__c> SalesInvoiceItemToUpdate = [SELECT id, Primary_Project__c, Opportunity__c, (SELECT Opportunity__c, Primary_Project__c, Sales_Invoice__c FROM R00N40000001p914EAA__r) FROM Sales_Invoice__c  WHERE Opportunity__c IN :opportunityIds];
    for(Sales_Invoice__c si: SalesInvoiceItemToUpdate){
      List<Sales_Invoice_Item__c> itemList = si.R00N40000001p914EAA__r; 
        for(Sales_Invoice_Item__c i : itemList){  
      i.Primary_Project__c=mOpptyProjIds.get(si.Opportunity__c);
      
            }
    }
   if(SalesInvoiceItemToUpdate.size()>0) {
    update SalesInvoiceItemToUpdate;
  }  
    
}
And here is the exact portion that doesn't seem to do anything. 
 
list<Sales_Invoice__c> SalesInvoiceItemToUpdate = [SELECT id, Primary_Project__c, Opportunity__c, (SELECT Opportunity__c, Primary_Project__c, Sales_Invoice__c FROM R00N40000001p914EAA__r) FROM Sales_Invoice__c  WHERE Opportunity__c IN :opportunityIds];
    for(Sales_Invoice__c si: SalesInvoiceItemToUpdate){
      List<Sales_Invoice_Item__c> itemList = si.R00N40000001p914EAA__r; 
        for(Sales_Invoice_Item__c i : itemList){  
      i.Primary_Project__c=mOpptyProjIds.get(si.Opportunity__c);
      
            }
    }
   if(SalesInvoiceItemToUpdate.size()>0) {
    update SalesInvoiceItemToUpdate;
  }

 

Here is a simple trigger. There are two objects with look ups to one another, Project and Opportunity.
Goal here is that when a Project is created with a lookup populated to Opportunity, that the code goes out to that Opportunity and populates the lookup withe the id to the project. This should work on create/change and if the lookup on Project is nulled out, that the lookup that may have exited pointing back to it from Opportunity is also nulled.

The code is working in the first instance (when a project is created, it finds the opp and fills out the lookup) but it is not doing any actions on 'update' (nulling or updating) which is confusing me given the simplicty of the code.

Thoughts?

 

trigger PSA_Project_Automation on pse__Proj__c (before insert, before update) {
List<Id> OpportunityIds = new List<Id>();
List<Id> NullOpportunityIds = new List<Id>();   
    for (pse__Proj__c proj : Trigger.new){
       
            if(proj.pse__Opportunity__c != null){
            OpportunityIds.add(proj.pse__Opportunity__c);
            }  
           
        if(Trigger.IsUpdate){
            pse__Proj__c oldProj = Trigger.oldMap.get(proj.Id);
            
            if(proj.pse__Opportunity__c == null && oldProj.pse__Opportunity__c !=null)
             NullOpportunityIds.add(oldProj.pse__Opportunity__c); 
        }
        
    }

    List<Opportunity> oppList = [SELECT id, pse__Primary_Project__c FROM Opportunity Where id in :OpportunityIds];
    List<Opportunity> NullOppList = [SELECT id, pse__Primary_Project__c FROM Opportunity Where id in :NullOpportunityIds];
    
    for(pse__Proj__c p : Trigger.new){
    for(integer i=0; i<oppList.size(); i++){
      oppList[i].pse__Primary_Project__c = p.id;
        }
    for(integer i=0; i<NullOppList.size(); i++){
      NullOppList[i].pse__Primary_Project__c = null;      
        }
    }

Short story. I have a trigger on a custom object that invokes a queueable class. That class is gathering up related opportunities (via a lookup) and editing them. 

On the opportunity level, I have a bit of future code that fires. 

Now before my future code is invoked I have the following.

if(!System.isFuture()) {    
    OpportunityReferenceUpdateClass.processOpportunityReferences(Trigger.newMap.keySet());
}

To solve an issue where I was having a nightly batch executing that was causing an error (batch invoking future is a no no) I changed it to this.

if(!System.isFuture() && !System.isBatch()) {    
    OpportunityReferenceUpdateClass.processOpportunityReferences(Trigger.newMap.keySet());
}

Success---no more errors...however for some reason now that I added isBatch to my conditions to ensure its not firing due to my nightly Batch, it seems my Queueable code is now no longer able to update it? It doesn't make a whole lot of sense to me.

 

Here is a glimpse of my apex jobs (there are no batches running)

Without isBatch, the Queueable code is completing and kicking off my future code. 


4/11/2016 3:22 PMFutureCompleted 000Athitakis, John4/11/2016 3:22 PMOpportunityReferenceUpdateClassprocessOpportunityReferences7071100000s5byt000000000000000  
 4/11/2016 3:22 PMQueueableCompleted 000Athitakis, John SalesAreaRelatedOpportunityUpdateClass 7071100000s5bta000000000000000 

With isBatch, the Future code is never fired. 

4/11/2016 3:26 PM	Queueable	Completed	 	0	0	0	Athitakis, John	 	SalesAreaRelatedOpportunityUpdateClass	 	7071100000s5c5F	000000000000000


My confusion is, I would expect this behavior if I was using isQueueable, but I'm not? (One more, there are no batches going on from what I can see).

 

Any ideas? 

Situation: I have batch apex running every night at 1 am on Opportunity Lin Items.

I also have @ future code on my opportunity. It seems once I introduced the later, that my batches are failing because at my Opportunity level, I have roll ups which the batch is causing to save, leading to a the Batch failing due to the @future.

I believe an easy solution to this is convert my code to queueable--and while I recently learned about that, the code is pretty lengthy and I don't know a good way of doing that conversion (if thats the only answer, so be it).
 

However, is there a way I could tell my trigger instigating the @future method to not fire due to a save occuring from rollups without necessarily naming each rollup? Currently my call looks like this:

trigger OpportunityReferenceUpdate on Opportunity (after insert, after update) {

       
 if( !System.isFuture() ) {    
    OpportunityReferenceUpdateClass.processOpportunityReferences(Trigger.newMap.keySet());
}
   
}

Sadly, my 'isFuture' flag does not seem to be catching the edge case from the Batch update. 

I can imagine something like this as a temporary fix. 

trigger OpportunityReferenceUpdate on Opportunity (after insert, after update) {
//Used to update text fields within the Opportunity to reduce the amount of references due to SFDC Limits
//Author: John Athitakis
//Relates to BIZOPS-4428
//
 // create a set of all the unique ownerIds
 // 
 // 
 boolean Change=False;
 
   for (Opportunity sa : Trigger.new){
     if(Trigger.IsUpdate){
       Opportunity saOld = Trigger.oldMap.get(sa.Id);
        
      if( (sa.Rollup1 == saOld.rollup1) && (sa.Rollup2==sa.Rollup2) ){
    Change=True;
      }
     }
   }
    
    
    
 if( !System.isFuture() && nodechange== False) {    
    OpportunityReferenceUpdateClass.processOpportunityReferences(Trigger.newMap.keySet());
}
 

Is there a better way?

So here is my situation. I recently began to realize I need to async more of our code, and learned all about @future (WEEE!) but have quickly come to realize that because we have many moving pieces that @future has limitations (specifically when one @future might trigger another @future).

In queueable, which seems to be the silver knight I have wanted but, I'm having a misunderstanding as to how to go about this (most examples I find show the queuable apex class which I understand but, not understanding how to pass it the values of triggering ID's).

So here is my simple code:

Trigger

 

trigger SalesAreaRelatedOpportunityUpdate on Sales_Area__c (after insert,after update) {
    SalesAreaRelatedOpportunityUpdateClass.processOpportunities(Trigger.newMap.keySet());

}

 

Class

global class SalesAreaRelatedOpportunityUpdateClass {
@future
    public static void processOpportunities(Set<ID> salesAreaId){

    List <Opportunity> updateOps = [SELECT Id FROM Opportunity WHERE IsClosed = False AND (Sales_Area__c in :salesAreaId OR Secondary_Sales_Area__c in :salesAreaId)];
    if(!updateOps.isEmpty()) {
        update updateOps;   
        
    }
                                                                
}
}

What this code does is merely update the related opportunities from a custom object which in turn, triggers other code. Some of that other code due to load needs to be async (and currently is @future, although once I figure out this queueable business I may change it as well).

My question is this---how would I change this pretty basic code to something queable? I'm having some troubles understnading how to pass the keySet in particular. 

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. 

Have some very simple code. Basically when a custom object (sales area) is updated, I want all the related opportunities the not won/lost opps that are related to it to be updated as well (which for most sales areas, is just 5-6 opps atm since this is a new process). I've tried a few dozen ways to get around the error in the title, have moved my update out of a for look as well as the select statement, but cannot for the life of me understand what I'm doing wrong?
 
trigger SalesAreaRelatedOpportunityUpdate on Sales_Area__c (after update) {
      if(checkRecursive.runOnce())
    {
    List<ID> salesAreaId = new List<ID>(); 
        
        for (Sales_Area__c sa : Trigger.new){
        salesAreaId.add(sa.id);
        }
        
        List <Opportunity> updateOps=[SELECT Id FROM Opportunity WHERE IsClosed=False AND (Sales_Area__c in :salesAreaId OR Secondary_Sales_Area__c in  :salesAreaId)];
        update updateOps;
        
    }
}